From 6d99eb2938e976229cb50d848a1bc491532b54f9 Mon Sep 17 00:00:00 2001 From: Brett Parker Date: Sat, 17 Mar 2018 14:58:46 +0000 Subject: [PATCH 1/1] Import Upstream version 1.2.2 --- .gitignore | 45 + AUTHORS | 6 + COPYING | 339 + COPYING.LIB | 482 + ChangeLog | 4 + HACKING.md | 741 + HACKING.pending | 12 + INSTALL.quagga.txt | 112 + Makefile.am | 26 + NEWS | 2594 +++ README | 11 + README.NetBSD | 48 + REPORTING-BUGS | 34 + SERVICES | 21 + TODO | 209 + bgpd/.gitignore | 18 + bgpd/BGP4-MIB.txt | 929 + bgpd/IMPLEMENTATION.txt | 169 + bgpd/Makefile.am | 41 + bgpd/bgp_advertise.c | 423 + bgpd/bgp_advertise.h | 173 + bgpd/bgp_aspath.c | 2076 ++ bgpd/bgp_aspath.h | 119 + bgpd/bgp_attr.c | 3260 +++ bgpd/bgp_attr.h | 238 + bgpd/bgp_btoa.c | 313 + bgpd/bgp_clist.c | 1235 ++ bgpd/bgp_clist.h | 172 + bgpd/bgp_community.c | 649 + bgpd/bgp_community.h | 75 + bgpd/bgp_damp.c | 675 + bgpd/bgp_damp.h | 149 + bgpd/bgp_debug.c | 1046 + bgpd/bgp_debug.h | 134 + bgpd/bgp_dump.c | 893 + bgpd/bgp_dump.h | 57 + bgpd/bgp_ecommunity.c | 776 + bgpd/bgp_ecommunity.h | 88 + bgpd/bgp_encap.c | 954 + bgpd/bgp_encap.h | 29 + bgpd/bgp_encap_tlv.c | 1012 + bgpd/bgp_encap_tlv.h | 177 + bgpd/bgp_encap_types.h | 212 + bgpd/bgp_filter.c | 719 + bgpd/bgp_filter.h | 39 + bgpd/bgp_fsm.c | 1205 ++ bgpd/bgp_fsm.h | 81 + bgpd/bgp_fsm_4271.dot | 34 + bgpd/bgp_fsm_quagga.dot | 59 + bgpd/bgp_lcommunity.c | 562 + bgpd/bgp_lcommunity.h | 75 + bgpd/bgp_main.c | 493 + bgpd/bgp_mpath.c | 799 + bgpd/bgp_mpath.h | 82 + bgpd/bgp_mplsvpn.c | 1063 + bgpd/bgp_mplsvpn.h | 51 + bgpd/bgp_network.c | 567 + bgpd/bgp_network.h | 34 + bgpd/bgp_nexthop.c | 567 + bgpd/bgp_nexthop.h | 84 + bgpd/bgp_nht.c | 612 + bgpd/bgp_nht.h | 66 + bgpd/bgp_open.c | 1144 + bgpd/bgp_open.h | 112 + bgpd/bgp_packet.c | 2717 +++ bgpd/bgp_packet.h | 59 + bgpd/bgp_regex.c | 94 + bgpd/bgp_regex.h | 40 + bgpd/bgp_route.c | 17989 ++++++++++++++++ bgpd/bgp_route.h | 273 + bgpd/bgp_routemap.c | 4690 ++++ bgpd/bgp_snmp.c | 887 + bgpd/bgp_snmp.h | 28 + bgpd/bgp_table.c | 126 + bgpd/bgp_table.h | 314 + bgpd/bgp_vty.c | 12619 +++++++++++ bgpd/bgp_vty.h | 35 + bgpd/bgp_zebra.c | 1222 ++ bgpd/bgp_zebra.h | 53 + bgpd/bgpd.c | 5614 +++++ bgpd/bgpd.conf.sample | 29 + bgpd/bgpd.conf.sample2 | 77 + bgpd/bgpd.h | 1002 + bootstrap.sh | 7 + buildtest.sh | 87 + common.am | 41 + configure.ac | 1713 ++ doc/.gitignore | 32 + doc/BGP-TypeCode | 24 + doc/Makefile.am | 125 + doc/appendix.texi | 257 + doc/basic.texi | 605 + doc/bgpd.8 | 125 + doc/bgpd.texi | 1976 ++ doc/defines.texi | 14 + doc/draft-zebra-00.ms | 209 + doc/fig-normal-processing.dia | 1738 ++ doc/fig-normal-processing.png | Bin 0 -> 29958 bytes doc/fig-normal-processing.txt | 11 + doc/fig-rs-processing.dia | 4426 ++++ doc/fig-rs-processing.png | Bin 0 -> 59640 bytes doc/fig-rs-processing.txt | 47 + doc/fig_topologies_full.txt | 6 + doc/fig_topologies_rs.txt | 5 + doc/filter.texi | 182 + doc/install.texi | 295 + doc/ipv6.texi | 183 + doc/isisd.8 | 111 + doc/isisd.texi | 432 + doc/kernel.texi | 47 + doc/main.texi | 498 + doc/mpls/.gitignore | 5 + doc/mpls/ChangeLog.opaque.txt | 192 + doc/mpls/cli_summary.txt | 90 + doc/mpls/opaque_lsa.txt | 365 + doc/mpls/ospfd.conf | 76 + doc/next-hop-tracking.txt | 326 + doc/nhrpd.8 | 105 + doc/nhrpd.texi | 144 + doc/ospf6d.8 | 111 + doc/ospf6d.texi | 168 + doc/ospf_fundamentals.texi | 582 + doc/ospfclient.8 | 42 + doc/ospfd.8 | 113 + doc/ospfd.texi | 922 + doc/overview.texi | 337 + doc/pimd.8 | 125 + doc/protocol.texi | 112 + doc/quagga.texi | 144 + doc/ripd.8 | 113 + doc/ripd.texi | 623 + doc/ripngd.8 | 114 + doc/ripngd.texi | 84 + doc/routemap.texi | 236 + doc/routeserver.texi | 550 + doc/snmp.texi | 185 + doc/snmptrap.texi | 211 + doc/texinfo.css | 227 + doc/texinfo.tex | 11198 ++++++++++ doc/vtysh.1 | 103 + doc/vtysh.texi | 61 + doc/watchquagga.8 | 231 + doc/zebra.8 | 134 + fpm/.gitignore | 15 + fpm/Makefile.am | 29 + fpm/fpm.h | 309 + fpm/fpm.proto | 88 + fpm/fpm_pb.c | 28 + fpm/fpm_pb.h | 63 + gdb/lib.txt | 295 + gdb/ospf.txt | 137 + infra/buildbot/master/master.cfg | 619 + infra/buildbot/master/pass.cfg | 21 + infra/buildbot/worker/buildbot-slave.service | 13 + infra/buildbot/worker/buildbot-slave.xml | 86 + infra/patchwork/pass.py | 4 + infra/patchwork/production.py | 89 + .../systemd/patchwork-delivery.socket | 15 + .../systemd/patchwork-delivery@.service | 15 + infra/patchwork/systemd/patchwork.service | 12 + init/.gitignore | 6 + isisd/.gitignore | 15 + isisd/AUTHORS | 5 + isisd/Makefile.am | 38 + isisd/README | 3 + isisd/dict.c | 1485 ++ isisd/dict.h | 123 + isisd/include-netbsd/.gitignore | 6 + isisd/include-netbsd/clnp.h | 547 + isisd/include-netbsd/esis.h | 146 + isisd/include-netbsd/iso.h | 214 + isisd/isis_adjacency.c | 534 + isisd/isis_adjacency.h | 118 + isisd/isis_bpf.c | 361 + isisd/isis_circuit.c | 1423 ++ isisd/isis_circuit.h | 187 + isisd/isis_common.h | 71 + isisd/isis_constants.h | 171 + isisd/isis_csm.c | 220 + isisd/isis_csm.h | 47 + isisd/isis_dlpi.c | 654 + isisd/isis_dr.c | 361 + isisd/isis_dr.h | 42 + isisd/isis_dynhn.c | 173 + isisd/isis_dynhn.h | 41 + isisd/isis_events.c | 278 + isisd/isis_events.h | 49 + isisd/isis_flags.c | 86 + isisd/isis_flags.h | 68 + isisd/isis_lsp.c | 3073 +++ isisd/isis_lsp.h | 126 + isisd/isis_main.c | 383 + isisd/isis_misc.c | 633 + isisd/isis_misc.h | 83 + isisd/isis_network.h | 37 + isisd/isis_pdu.c | 3257 +++ isisd/isis_pdu.h | 274 + isisd/isis_pfpacket.c | 417 + isisd/isis_redist.c | 825 + isisd/isis_redist.h | 59 + isisd/isis_route.c | 678 + isisd/isis_route.h | 70 + isisd/isis_routemap.c | 565 + isisd/isis_routemap.h | 25 + isisd/isis_spf.c | 1695 ++ isisd/isis_spf.h | 89 + isisd/isis_te.c | 1370 ++ isisd/isis_te.h | 331 + isisd/isis_tlv.c | 1196 + isisd/isis_tlv.h | 340 + isisd/isis_vty.c | 2428 +++ isisd/isis_zebra.c | 715 + isisd/isis_zebra.h | 34 + isisd/isisd.c | 2406 +++ isisd/isisd.conf.sample | 39 + isisd/isisd.h | 202 + isisd/iso_checksum.c | 77 + isisd/iso_checksum.h | 28 + isisd/topology/.gitignore | 12 + isisd/topology/Makefile.am | 21 + isisd/topology/random.c | 166 + isisd/topology/spacyc.c | 484 + isisd/topology/spgrid.c | 738 + isisd/topology/spgrid.h | 45 + isisd/topology/sprand.c | 501 + lib/.gitignore | 18 + lib/Makefile.am | 71 + lib/agentx.c | 315 + lib/buffer.c | 497 + lib/buffer.h | 102 + lib/checksum.c | 124 + lib/checksum.h | 3 + lib/command.c | 4334 ++++ lib/command.h | 583 + lib/daemon.c | 81 + lib/distribute.c | 1020 + lib/distribute.h | 63 + lib/event_counter.c | 73 + lib/event_counter.h | 56 + lib/fifo.h | 63 + lib/filter.c | 2036 ++ lib/filter.h | 72 + lib/getopt.c | 1056 + lib/getopt.h | 159 + lib/getopt1.c | 187 + lib/gitversion.pl | 47 + lib/hash.c | 259 + lib/hash.h | 79 + lib/if.c | 1171 + lib/if.h | 471 + lib/if_rmap.c | 330 + lib/if_rmap.h | 47 + lib/jhash.c | 170 + lib/jhash.h | 44 + lib/keychain.c | 988 + lib/keychain.h | 56 + lib/libospf.h | 92 + lib/linklist.c | 361 + lib/linklist.h | 151 + lib/log.c | 1057 + lib/log.h | 241 + lib/md5.c | 373 + lib/md5.h | 89 + lib/memory.c | 486 + lib/memory.h | 96 + lib/memtypes.awk | 87 + lib/memtypes.c | 319 + lib/network.c | 116 + lib/network.h | 43 + lib/nexthop.c | 168 + lib/nexthop.h | 91 + lib/pid_output.c | 108 + lib/plist.c | 2789 +++ lib/plist.h | 60 + lib/plist_int.h | 71 + lib/pqueue.c | 187 + lib/pqueue.h | 46 + lib/prefix.c | 1042 + lib/prefix.h | 295 + lib/privs.c | 852 + lib/privs.h | 90 + lib/queue.h | 635 + lib/regex-gnu.h | 542 + lib/regex.c | 5895 +++++ lib/route_types.pl | 199 + lib/route_types.txt | 80 + lib/routemap.c | 1363 ++ lib/routemap.h | 204 + lib/sigevent.c | 367 + lib/sigevent.h | 52 + lib/smux.c | 1502 ++ lib/smux.h | 116 + lib/snmp.c | 133 + lib/sockopt.c | 669 + lib/sockopt.h | 105 + lib/sockunion.c | 858 + lib/sockunion.h | 134 + lib/str.c | 127 + lib/str.h | 33 + lib/stream.c | 1074 + lib/stream.h | 237 + lib/table.c | 809 + lib/table.h | 254 + lib/thread.c | 1381 ++ lib/thread.h | 254 + lib/vector.c | 189 + lib/vector.h | 63 + lib/version.h.in | 56 + lib/vrf.c | 726 + lib/vrf.h | 140 + lib/vty.c | 3209 +++ lib/vty.h | 258 + lib/workqueue.c | 408 + lib/workqueue.h | 129 + lib/zassert.h | 44 + lib/zclient.c | 1311 ++ lib/zclient.h | 229 + lib/zebra.h | 541 + m4/.gitignore | 7 + m4/Makefile.am | 1 + m4/README.txt | 18 + m4/ax_sys_weak_alias.m4 | 333 + nhrpd/Makefile.am | 38 + nhrpd/README.kernel | 147 + nhrpd/README.nhrpd | 140 + nhrpd/debug.h | 42 + nhrpd/linux.c | 153 + nhrpd/list.h | 191 + nhrpd/netlink.h | 24 + nhrpd/netlink_arp.c | 275 + nhrpd/netlink_gre.c | 142 + nhrpd/nhrp-events.lua | 272 + nhrpd/nhrp_cache.c | 341 + nhrpd/nhrp_event.c | 283 + nhrpd/nhrp_interface.c | 406 + nhrpd/nhrp_main.c | 247 + nhrpd/nhrp_nhs.c | 371 + nhrpd/nhrp_packet.c | 312 + nhrpd/nhrp_peer.c | 877 + nhrpd/nhrp_protocol.h | 128 + nhrpd/nhrp_route.c | 383 + nhrpd/nhrp_shortcut.c | 402 + nhrpd/nhrp_vc.c | 217 + nhrpd/nhrp_vty.c | 983 + nhrpd/nhrpd.h | 413 + nhrpd/os.h | 5 + nhrpd/reqid.c | 49 + nhrpd/resolver.c | 190 + nhrpd/vici.c | 502 + nhrpd/vici.h | 24 + nhrpd/zbuf.c | 219 + nhrpd/zbuf.h | 189 + nhrpd/znl.c | 160 + nhrpd/znl.h | 29 + ospf6d/.gitignore | 18 + ospf6d/Makefile.am | 32 + ospf6d/OSPFv3-MIB.txt | 3951 ++++ ospf6d/README | 102 + ospf6d/ospf6_abr.c | 945 + ospf6d/ospf6_abr.h | 80 + ospf6d/ospf6_area.c | 816 + ospf6d/ospf6_area.h | 128 + ospf6d/ospf6_asbr.c | 1580 ++ ospf6d/ospf6_asbr.h | 100 + ospf6d/ospf6_flood.c | 1051 + ospf6d/ospf6_flood.h | 66 + ospf6d/ospf6_interface.c | 2000 ++ ospf6d/ospf6_interface.h | 177 + ospf6d/ospf6_intra.c | 1789 ++ ospf6d/ospf6_intra.h | 233 + ospf6d/ospf6_lsa.c | 967 + ospf6d/ospf6_lsa.h | 259 + ospf6d/ospf6_lsdb.c | 592 + ospf6d/ospf6_lsdb.h | 88 + ospf6d/ospf6_main.c | 365 + ospf6d/ospf6_message.c | 2550 +++ ospf6d/ospf6_message.h | 142 + ospf6d/ospf6_neighbor.c | 1036 + ospf6d/ospf6_neighbor.h | 154 + ospf6d/ospf6_network.c | 262 + ospf6d/ospf6_network.h | 40 + ospf6d/ospf6_proto.c | 84 + ospf6d/ospf6_proto.h | 101 + ospf6d/ospf6_route.c | 1413 ++ ospf6d/ospf6_route.h | 311 + ospf6d/ospf6_snmp.c | 1183 + ospf6d/ospf6_snmp.h | 31 + ospf6d/ospf6_spf.c | 918 + ospf6d/ospf6_spf.h | 164 + ospf6d/ospf6_top.c | 1463 ++ ospf6d/ospf6_top.h | 107 + ospf6d/ospf6_zebra.c | 864 + ospf6d/ospf6_zebra.h | 72 + ospf6d/ospf6d.c | 1863 ++ ospf6d/ospf6d.conf.sample | 52 + ospf6d/ospf6d.h | 133 + ospfclient/.gitignore | 16 + ospfclient/AUTHORS | 1 + ospfclient/COPYING | 339 + ospfclient/INSTALL | 182 + ospfclient/Makefile.am | 27 + ospfclient/NEWS | 1 + ospfclient/README | 4 + ospfclient/ospf_apiclient.c | 746 + ospfclient/ospf_apiclient.h | 135 + ospfclient/ospfclient.c | 352 + ospfd/.gitignore | 17 + ospfd/ChangeLog.opaque.txt | 221 + ospfd/Makefile.am | 40 + ospfd/OSPF-ALIGNMENT.txt | 119 + ospfd/OSPF-MIB.txt | 2723 +++ ospfd/OSPF-TRAP-MIB.txt | 443 + ospfd/ospf_abr.c | 1887 ++ ospfd/ospf_abr.h | 94 + ospfd/ospf_api.c | 649 + ospfd/ospf_api.h | 361 + ospfd/ospf_apiserver.c | 2641 +++ ospfd/ospf_apiserver.h | 201 + ospfd/ospf_asbr.c | 294 + ospfd/ospf_asbr.h | 81 + ospfd/ospf_ase.c | 867 + ospfd/ospf_ase.h | 47 + ospfd/ospf_dump.c | 1748 ++ ospfd/ospf_dump.h | 148 + ospfd/ospf_flood.c | 997 + ospfd/ospf_flood.h | 74 + ospfd/ospf_ia.c | 715 + ospfd/ospf_ia.h | 43 + ospfd/ospf_interface.c | 1278 ++ ospfd/ospf_interface.h | 314 + ospfd/ospf_ism.c | 633 + ospfd/ospf_ism.h | 114 + ospfd/ospf_lsa.c | 3780 ++++ ospfd/ospf_lsa.h | 340 + ospfd/ospf_lsdb.c | 330 + ospfd/ospf_lsdb.h | 87 + ospfd/ospf_main.c | 344 + ospfd/ospf_neighbor.c | 504 + ospfd/ospf_neighbor.h | 119 + ospfd/ospf_network.c | 260 + ospfd/ospf_network.h | 39 + ospfd/ospf_nsm.c | 867 + ospfd/ospf_nsm.h | 90 + ospfd/ospf_opaque.c | 2173 ++ ospfd/ospf_opaque.h | 146 + ospfd/ospf_packet.c | 3912 ++++ ospfd/ospf_packet.h | 177 + ospfd/ospf_ri.c | 1637 ++ ospfd/ospf_ri.h | 191 + ospfd/ospf_route.c | 1049 + ospfd/ospf_route.h | 166 + ospfd/ospf_routemap.c | 972 + ospfd/ospf_snmp.c | 2743 +++ ospfd/ospf_snmp.h | 38 + ospfd/ospf_spf.c | 1490 ++ ospfd/ospf_spf.h | 78 + ospfd/ospf_te.c | 2639 +++ ospfd/ospf_te.h | 466 + ospfd/ospf_vty.c | 7923 +++++++ ospfd/ospf_vty.h | 59 + ospfd/ospf_zebra.c | 1369 ++ ospfd/ospf_zebra.h | 78 + ospfd/ospfd.c | 1796 ++ ospfd/ospfd.conf.sample | 13 + ospfd/ospfd.h | 568 + pimd/.gitignore | 16 + pimd/AUTHORS | 9 + pimd/CAVEATS | 178 + pimd/COMMANDS | 81 + pimd/COPYING | 340 + pimd/DEBUG | 86 + pimd/LINUX_KERNEL_MROUTE_MFC | 26 + pimd/Makefile.am | 76 + pimd/README | 164 + pimd/TODO | 426 + pimd/TROUBLESHOOTING | 33 + pimd/WHY_SSM | 32 + pimd/pim_assert.c | 808 + pimd/pim_assert.h | 75 + pimd/pim_cmd.c | 4951 +++++ pimd/pim_cmd.h | 70 + pimd/pim_hello.c | 550 + pimd/pim_hello.h | 46 + pimd/pim_iface.c | 1194 + pimd/pim_iface.h | 159 + pimd/pim_ifchannel.c | 898 + pimd/pim_ifchannel.h | 145 + pimd/pim_igmp.c | 1435 ++ pimd/pim_igmp.h | 178 + pimd/pim_igmp_join.c | 72 + pimd/pim_igmp_join.h | 33 + pimd/pim_igmpv3.c | 1716 ++ pimd/pim_igmpv3.h | 100 + pimd/pim_int.c | 47 + pimd/pim_int.h | 31 + pimd/pim_join.c | 445 + pimd/pim_join.h | 43 + pimd/pim_macro.c | 437 + pimd/pim_macro.h | 44 + pimd/pim_main.c | 284 + pimd/pim_mroute.c | 451 + pimd/pim_mroute.h | 177 + pimd/pim_msg.c | 106 + pimd/pim_msg.h | 52 + pimd/pim_neighbor.c | 722 + pimd/pim_neighbor.h | 74 + pimd/pim_oil.c | 140 + pimd/pim_oil.h | 53 + pimd/pim_pim.c | 765 + pimd/pim_pim.h | 77 + pimd/pim_routemap.c | 32 + pimd/pim_rpf.c | 260 + pimd/pim_rpf.h | 36 + pimd/pim_signals.c | 87 + pimd/pim_signals.h | 28 + pimd/pim_sock.c | 389 + pimd/pim_sock.h | 57 + pimd/pim_ssmpingd.c | 449 + pimd/pim_ssmpingd.h | 45 + pimd/pim_static.c | 342 + pimd/pim_static.h | 48 + pimd/pim_str.c | 46 + pimd/pim_str.h | 32 + pimd/pim_time.c | 166 + pimd/pim_time.h | 40 + pimd/pim_tlv.c | 705 + pimd/pim_tlv.h | 133 + pimd/pim_upstream.c | 688 + pimd/pim_upstream.h | 122 + pimd/pim_util.c | 122 + pimd/pim_util.h | 37 + pimd/pim_version.c | 27 + pimd/pim_version.h | 30 + pimd/pim_vty.c | 203 + pimd/pim_vty.h | 32 + pimd/pim_zebra.c | 1300 ++ pimd/pim_zebra.h | 42 + pimd/pim_zlookup.c | 440 + pimd/pim_zlookup.h | 47 + pimd/pimd.c | 154 + pimd/pimd.conf.sample | 41 + pimd/pimd.h | 164 + pimd/test_igmpv3_join.c | 149 + pkgsrc/.gitignore | 8 + pkgsrc/Makefile.am | 3 + pkgsrc/README.txt | 7 + pkgsrc/bgpd.sh.in | 44 + pkgsrc/ospf6d.sh.in | 44 + pkgsrc/ospfd.sh.in | 44 + pkgsrc/ripd.sh.in | 44 + pkgsrc/ripngd.sh.in | 44 + pkgsrc/zebra.sh.in | 55 + ports/.gitignore | 6 + ports/Makefile | 59 + ports/README | 1 + ports/files/.gitignore | 6 + ports/files/md5 | 1 + ports/pkg/.gitignore | 6 + ports/pkg/COMMENT | 1 + ports/pkg/DESCR | 76 + ports/pkg/PLIST | 8 + qpb/.gitignore | 15 + qpb/Makefile.am | 30 + qpb/README.txt | 1 + qpb/linear_allocator.h | 207 + qpb/qpb.c | 29 + qpb/qpb.h | 372 + qpb/qpb.proto | 90 + qpb/qpb_allocator.c | 67 + qpb/qpb_allocator.h | 113 + redhat/.gitignore | 10 + redhat/Makefile.am | 9 + redhat/README.rpm_build.md | 128 + redhat/bgpd.init | 72 + redhat/bgpd.service | 16 + redhat/isisd.init | 72 + redhat/isisd.service | 16 + redhat/nhrpd.service | 16 + redhat/ospf6d.init | 72 + redhat/ospf6d.service | 16 + redhat/ospfd.init | 72 + redhat/ospfd.service | 16 + redhat/pimd.init | 72 + redhat/pimd.service | 14 + redhat/quagga-filter-perl-requires.sh | 3 + redhat/quagga-tmpfs.conf | 1 + redhat/quagga.logrotate | 55 + redhat/quagga.pam | 26 + redhat/quagga.spec.in | 799 + redhat/quagga.sysconfig | 25 + redhat/ripd.init | 72 + redhat/ripd.service | 16 + redhat/ripngd.init | 72 + redhat/ripngd.service | 16 + redhat/watchquagga.init | 66 + redhat/zebra.init | 73 + redhat/zebra.service | 16 + release.sh | 93 + ripd/.gitignore | 17 + ripd/Makefile.am | 28 + ripd/RIPv2-MIB.txt | 530 + ripd/rip_debug.c | 284 + ripd/rip_debug.h | 53 + ripd/rip_interface.c | 2111 ++ ripd/rip_interface.h | 37 + ripd/rip_main.c | 321 + ripd/rip_offset.c | 413 + ripd/rip_peer.c | 207 + ripd/rip_routemap.c | 1113 + ripd/rip_snmp.c | 593 + ripd/rip_zebra.c | 762 + ripd/ripd.c | 4143 ++++ ripd/ripd.conf.sample | 24 + ripd/ripd.h | 449 + ripngd/.gitignore | 17 + ripngd/Makefile.am | 26 + ripngd/ripng_debug.c | 288 + ripngd/ripng_debug.h | 51 + ripngd/ripng_interface.c | 1214 ++ ripngd/ripng_main.c | 316 + ripngd/ripng_nexthop.c | 214 + ripngd/ripng_nexthop.h | 64 + ripngd/ripng_offset.c | 421 + ripngd/ripng_peer.c | 216 + ripngd/ripng_route.c | 181 + ripngd/ripng_route.h | 57 + ripngd/ripng_routemap.c | 707 + ripngd/ripng_zebra.c | 593 + ripngd/ripngd.c | 3092 +++ ripngd/ripngd.conf.sample | 22 + ripngd/ripngd.h | 427 + solaris/.gitignore | 22 + solaris/Makefile.am | 124 + solaris/README.txt | 188 + solaris/depend.daemons.in | 8 + solaris/depend.dev.in | 2 + solaris/depend.doc.in | 1 + solaris/depend.libs.in | 5 + solaris/depend.smf.in | 8 + solaris/pkginfo.daemons.tmpl.in | 2 + solaris/pkginfo.dev.tmpl.in | 3 + solaris/pkginfo.doc.tmpl.in | 2 + solaris/pkginfo.libs.tmpl.in | 2 + solaris/pkginfo.smf.tmpl.in | 2 + solaris/pkginfo.tmpl.in | 10 + solaris/prototype.daemons.in | 20 + solaris/prototype.dev.in | 57 + solaris/prototype.doc.in | 17 + solaris/prototype.libs.in | 13 + solaris/prototype.smf.in | 8 + solaris/quagga.init.in | 280 + solaris/quagga.xml.in | 828 + stamp-h.in | 1 + tests/.gitignore | 41 + tests/Makefile.am | 92 + tests/aspath_test.c | 1387 ++ tests/bgp_capability_test.c | 697 + tests/bgp_mp_attr_test.c | 790 + tests/bgp_mpath_test.c | 488 + tests/bgpd.tests/Makefile.am | 7 + tests/bgpd.tests/aspathtest.exp | 76 + tests/bgpd.tests/ecommtest.exp | 13 + tests/bgpd.tests/testbgpcap.exp | 51 + tests/bgpd.tests/testbgpmpath.exp | 12 + tests/bgpd.tests/testbgpmpattr.exp | 38 + tests/common-cli.c | 90 + tests/common-cli.h | 49 + tests/config/unix.exp | 102 + tests/ecommunity_test.c | 162 + tests/global-conf.exp | 0 tests/heavy-thread.c | 147 + tests/heavy-wq.c | 180 + tests/heavy.c | 113 + tests/lib/bgpd.exp | 0 tests/lib/libzebra.exp | 0 tests/libzebra.tests/Makefile.am | 6 + tests/libzebra.tests/tabletest.exp | 9 + .../libzebra.tests/test-timer-correctness.exp | 7 + tests/libzebra.tests/testcli.exp | 23 + tests/libzebra.tests/testcommands.exp | 31 + tests/libzebra.tests/testnexthopiter.exp | 8 + tests/libzebra.tests/teststream.exp | 28 + tests/main.c | 200 + tests/prng.c | 132 + tests/prng.h | 38 + tests/table_test.c | 555 + tests/test-buffer.c | 59 + tests/test-checksum.c | 550 + tests/test-cli.c | 58 + tests/test-commands.c | 415 + tests/test-memory.c | 122 + tests/test-nexthop-iter.c | 291 + tests/test-privs.c | 152 + tests/test-segv.c | 61 + tests/test-sig.c | 78 + tests/test-stream.c | 76 + tests/test-timer-correctness.c | 197 + tests/test-timer-performance.c | 105 + tests/testcli.in | 93 + tests/testcli.refout | 290 + tests/testcommands.in | 216 + tests/testcommands.refout | 1007 + tests/tests.h | 31 + tools/.gitignore | 6 + tools/mrlg.txt | 5 + tools/multiple-bgpd.sh | 458 + tools/zebra.el | 108 + update-autotools | 28 + vtysh/.gitignore | 16 + vtysh/Makefile.am | 41 + vtysh/extract.pl.in | 243 + vtysh/vtysh.c | 2876 +++ vtysh/vtysh.conf.sample | 7 + vtysh/vtysh.h | 72 + vtysh/vtysh_config.c | 451 + vtysh/vtysh_main.c | 439 + vtysh/vtysh_user.c | 213 + vtysh/vtysh_user.h | 30 + watchquagga/.gitignore | 16 + watchquagga/Makefile.am | 11 + watchquagga/watchquagga.c | 1399 ++ zebra/.gitignore | 16 + zebra/GNOME-PRODUCT-ZEBRA-MIB | 78 + zebra/GNOME-SMI | 53 + zebra/Makefile.am | 76 + zebra/client_main.c | 230 + zebra/connected.c | 475 + zebra/connected.h | 55 + zebra/debug.c | 413 + zebra/debug.h | 67 + zebra/if_ioctl.c | 475 + zebra/if_ioctl_solaris.c | 385 + zebra/if_netlink.c | 33 + zebra/if_sysctl.c | 158 + zebra/interface.c | 2817 +++ zebra/interface.h | 264 + zebra/ioctl.c | 596 + zebra/ioctl.h | 58 + zebra/ioctl_null.c | 78 + zebra/ioctl_solaris.c | 435 + zebra/ioctl_solaris.h | 29 + zebra/ipforward.h | 35 + zebra/ipforward_proc.c | 200 + zebra/ipforward_solaris.c | 165 + zebra/ipforward_sysctl.c | 176 + zebra/irdp.h | 157 + zebra/irdp_interface.c | 783 + zebra/irdp_main.c | 334 + zebra/irdp_packet.c | 368 + zebra/kernel_netlink.c | 20 + zebra/kernel_null.c | 62 + zebra/kernel_socket.c | 1367 ++ zebra/kernel_socket.h | 33 + zebra/main.c | 498 + zebra/misc_null.c | 56 + zebra/redistribute.c | 427 + zebra/redistribute.h | 58 + zebra/redistribute_null.c | 84 + zebra/rib.h | 603 + zebra/router-id.c | 307 + zebra/router-id.h | 41 + zebra/rt.h | 35 + zebra/rt_netlink.c | 2069 ++ zebra/rt_netlink.h | 50 + zebra/rt_socket.c | 397 + zebra/rtadv.c | 1795 ++ zebra/rtadv.h | 107 + zebra/rtread_getmsg.c | 239 + zebra/rtread_netlink.c | 31 + zebra/rtread_sysctl.c | 84 + zebra/test_main.c | 396 + zebra/testrib.conf | 76 + zebra/zebra.conf.sample | 25 + zebra/zebra_fpm.c | 1756 ++ zebra/zebra_fpm.h | 35 + zebra/zebra_fpm_dt.c | 275 + zebra/zebra_fpm_netlink.c | 495 + zebra/zebra_fpm_private.h | 61 + zebra/zebra_fpm_protobuf.c | 311 + zebra/zebra_rib.c | 3347 +++ zebra/zebra_rnh.c | 629 + zebra/zebra_rnh.h | 49 + zebra/zebra_rnh_null.c | 10 + zebra/zebra_routemap.c | 715 + zebra/zebra_snmp.c | 582 + zebra/zebra_vty.c | 5528 +++++ zebra/zserv.c | 2283 ++ zebra/zserv.h | 149 + 788 files changed, 379183 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 ChangeLog create mode 100644 HACKING.md create mode 100644 HACKING.pending create mode 100644 INSTALL.quagga.txt create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 README.NetBSD create mode 100644 REPORTING-BUGS create mode 100644 SERVICES create mode 100644 TODO create mode 100644 bgpd/.gitignore create mode 100644 bgpd/BGP4-MIB.txt create mode 100644 bgpd/IMPLEMENTATION.txt create mode 100644 bgpd/Makefile.am create mode 100644 bgpd/bgp_advertise.c create mode 100644 bgpd/bgp_advertise.h create mode 100644 bgpd/bgp_aspath.c create mode 100644 bgpd/bgp_aspath.h create mode 100644 bgpd/bgp_attr.c create mode 100644 bgpd/bgp_attr.h create mode 100644 bgpd/bgp_btoa.c create mode 100644 bgpd/bgp_clist.c create mode 100644 bgpd/bgp_clist.h create mode 100644 bgpd/bgp_community.c create mode 100644 bgpd/bgp_community.h create mode 100644 bgpd/bgp_damp.c create mode 100644 bgpd/bgp_damp.h create mode 100644 bgpd/bgp_debug.c create mode 100644 bgpd/bgp_debug.h create mode 100644 bgpd/bgp_dump.c create mode 100644 bgpd/bgp_dump.h create mode 100644 bgpd/bgp_ecommunity.c create mode 100644 bgpd/bgp_ecommunity.h create mode 100644 bgpd/bgp_encap.c create mode 100644 bgpd/bgp_encap.h create mode 100644 bgpd/bgp_encap_tlv.c create mode 100644 bgpd/bgp_encap_tlv.h create mode 100644 bgpd/bgp_encap_types.h create mode 100644 bgpd/bgp_filter.c create mode 100644 bgpd/bgp_filter.h create mode 100644 bgpd/bgp_fsm.c create mode 100644 bgpd/bgp_fsm.h create mode 100644 bgpd/bgp_fsm_4271.dot create mode 100644 bgpd/bgp_fsm_quagga.dot create mode 100644 bgpd/bgp_lcommunity.c create mode 100644 bgpd/bgp_lcommunity.h create mode 100644 bgpd/bgp_main.c create mode 100644 bgpd/bgp_mpath.c create mode 100644 bgpd/bgp_mpath.h create mode 100644 bgpd/bgp_mplsvpn.c create mode 100644 bgpd/bgp_mplsvpn.h create mode 100644 bgpd/bgp_network.c create mode 100644 bgpd/bgp_network.h create mode 100644 bgpd/bgp_nexthop.c create mode 100644 bgpd/bgp_nexthop.h create mode 100644 bgpd/bgp_nht.c create mode 100644 bgpd/bgp_nht.h create mode 100644 bgpd/bgp_open.c create mode 100644 bgpd/bgp_open.h create mode 100644 bgpd/bgp_packet.c create mode 100644 bgpd/bgp_packet.h create mode 100644 bgpd/bgp_regex.c create mode 100644 bgpd/bgp_regex.h create mode 100644 bgpd/bgp_route.c create mode 100644 bgpd/bgp_route.h create mode 100644 bgpd/bgp_routemap.c create mode 100644 bgpd/bgp_snmp.c create mode 100644 bgpd/bgp_snmp.h create mode 100644 bgpd/bgp_table.c create mode 100644 bgpd/bgp_table.h create mode 100644 bgpd/bgp_vty.c create mode 100644 bgpd/bgp_vty.h create mode 100644 bgpd/bgp_zebra.c create mode 100644 bgpd/bgp_zebra.h create mode 100644 bgpd/bgpd.c create mode 100644 bgpd/bgpd.conf.sample create mode 100644 bgpd/bgpd.conf.sample2 create mode 100644 bgpd/bgpd.h create mode 100755 bootstrap.sh create mode 100755 buildtest.sh create mode 100644 common.am create mode 100755 configure.ac create mode 100644 doc/.gitignore create mode 100644 doc/BGP-TypeCode create mode 100644 doc/Makefile.am create mode 100644 doc/appendix.texi create mode 100644 doc/basic.texi create mode 100644 doc/bgpd.8 create mode 100644 doc/bgpd.texi create mode 100644 doc/defines.texi create mode 100644 doc/draft-zebra-00.ms create mode 100644 doc/fig-normal-processing.dia create mode 100644 doc/fig-normal-processing.png create mode 100644 doc/fig-normal-processing.txt create mode 100644 doc/fig-rs-processing.dia create mode 100644 doc/fig-rs-processing.png create mode 100644 doc/fig-rs-processing.txt create mode 100644 doc/fig_topologies_full.txt create mode 100644 doc/fig_topologies_rs.txt create mode 100644 doc/filter.texi create mode 100644 doc/install.texi create mode 100644 doc/ipv6.texi create mode 100644 doc/isisd.8 create mode 100644 doc/isisd.texi create mode 100644 doc/kernel.texi create mode 100644 doc/main.texi create mode 100644 doc/mpls/.gitignore create mode 100644 doc/mpls/ChangeLog.opaque.txt create mode 100644 doc/mpls/cli_summary.txt create mode 100644 doc/mpls/opaque_lsa.txt create mode 100644 doc/mpls/ospfd.conf create mode 100644 doc/next-hop-tracking.txt create mode 100644 doc/nhrpd.8 create mode 100644 doc/nhrpd.texi create mode 100644 doc/ospf6d.8 create mode 100644 doc/ospf6d.texi create mode 100644 doc/ospf_fundamentals.texi create mode 100644 doc/ospfclient.8 create mode 100644 doc/ospfd.8 create mode 100644 doc/ospfd.texi create mode 100644 doc/overview.texi create mode 100644 doc/pimd.8 create mode 100644 doc/protocol.texi create mode 100644 doc/quagga.texi create mode 100644 doc/ripd.8 create mode 100644 doc/ripd.texi create mode 100644 doc/ripngd.8 create mode 100644 doc/ripngd.texi create mode 100644 doc/routemap.texi create mode 100644 doc/routeserver.texi create mode 100644 doc/snmp.texi create mode 100644 doc/snmptrap.texi create mode 100644 doc/texinfo.css create mode 100644 doc/texinfo.tex create mode 100644 doc/vtysh.1 create mode 100644 doc/vtysh.texi create mode 100644 doc/watchquagga.8 create mode 100644 doc/zebra.8 create mode 100644 fpm/.gitignore create mode 100644 fpm/Makefile.am create mode 100644 fpm/fpm.h create mode 100644 fpm/fpm.proto create mode 100644 fpm/fpm_pb.c create mode 100644 fpm/fpm_pb.h create mode 100644 gdb/lib.txt create mode 100644 gdb/ospf.txt create mode 100644 infra/buildbot/master/master.cfg create mode 100644 infra/buildbot/master/pass.cfg create mode 100644 infra/buildbot/worker/buildbot-slave.service create mode 100644 infra/buildbot/worker/buildbot-slave.xml create mode 100644 infra/patchwork/pass.py create mode 100644 infra/patchwork/production.py create mode 100644 infra/patchwork/systemd/patchwork-delivery.socket create mode 100644 infra/patchwork/systemd/patchwork-delivery@.service create mode 100644 infra/patchwork/systemd/patchwork.service create mode 100644 init/.gitignore create mode 100644 isisd/.gitignore create mode 100644 isisd/AUTHORS create mode 100644 isisd/Makefile.am create mode 100644 isisd/README create mode 100644 isisd/dict.c create mode 100644 isisd/dict.h create mode 100644 isisd/include-netbsd/.gitignore create mode 100644 isisd/include-netbsd/clnp.h create mode 100644 isisd/include-netbsd/esis.h create mode 100644 isisd/include-netbsd/iso.h create mode 100644 isisd/isis_adjacency.c create mode 100644 isisd/isis_adjacency.h create mode 100644 isisd/isis_bpf.c create mode 100644 isisd/isis_circuit.c create mode 100644 isisd/isis_circuit.h create mode 100644 isisd/isis_common.h create mode 100644 isisd/isis_constants.h create mode 100644 isisd/isis_csm.c create mode 100644 isisd/isis_csm.h create mode 100644 isisd/isis_dlpi.c create mode 100644 isisd/isis_dr.c create mode 100644 isisd/isis_dr.h create mode 100644 isisd/isis_dynhn.c create mode 100644 isisd/isis_dynhn.h create mode 100644 isisd/isis_events.c create mode 100644 isisd/isis_events.h create mode 100644 isisd/isis_flags.c create mode 100644 isisd/isis_flags.h create mode 100644 isisd/isis_lsp.c create mode 100644 isisd/isis_lsp.h create mode 100644 isisd/isis_main.c create mode 100644 isisd/isis_misc.c create mode 100644 isisd/isis_misc.h create mode 100644 isisd/isis_network.h create mode 100644 isisd/isis_pdu.c create mode 100644 isisd/isis_pdu.h create mode 100644 isisd/isis_pfpacket.c create mode 100644 isisd/isis_redist.c create mode 100644 isisd/isis_redist.h create mode 100644 isisd/isis_route.c create mode 100644 isisd/isis_route.h create mode 100644 isisd/isis_routemap.c create mode 100644 isisd/isis_routemap.h create mode 100644 isisd/isis_spf.c create mode 100644 isisd/isis_spf.h create mode 100644 isisd/isis_te.c create mode 100644 isisd/isis_te.h create mode 100644 isisd/isis_tlv.c create mode 100644 isisd/isis_tlv.h create mode 100644 isisd/isis_vty.c create mode 100644 isisd/isis_zebra.c create mode 100644 isisd/isis_zebra.h create mode 100644 isisd/isisd.c create mode 100644 isisd/isisd.conf.sample create mode 100644 isisd/isisd.h create mode 100644 isisd/iso_checksum.c create mode 100644 isisd/iso_checksum.h create mode 100644 isisd/topology/.gitignore create mode 100644 isisd/topology/Makefile.am create mode 100644 isisd/topology/random.c create mode 100644 isisd/topology/spacyc.c create mode 100644 isisd/topology/spgrid.c create mode 100644 isisd/topology/spgrid.h create mode 100644 isisd/topology/sprand.c create mode 100644 lib/.gitignore create mode 100644 lib/Makefile.am create mode 100644 lib/agentx.c create mode 100644 lib/buffer.c create mode 100644 lib/buffer.h create mode 100644 lib/checksum.c create mode 100644 lib/checksum.h create mode 100644 lib/command.c create mode 100644 lib/command.h create mode 100644 lib/daemon.c create mode 100644 lib/distribute.c create mode 100644 lib/distribute.h create mode 100644 lib/event_counter.c create mode 100644 lib/event_counter.h create mode 100644 lib/fifo.h create mode 100644 lib/filter.c create mode 100644 lib/filter.h create mode 100644 lib/getopt.c create mode 100644 lib/getopt.h create mode 100644 lib/getopt1.c create mode 100644 lib/gitversion.pl create mode 100644 lib/hash.c create mode 100644 lib/hash.h create mode 100644 lib/if.c create mode 100644 lib/if.h create mode 100644 lib/if_rmap.c create mode 100644 lib/if_rmap.h create mode 100644 lib/jhash.c create mode 100644 lib/jhash.h create mode 100644 lib/keychain.c create mode 100644 lib/keychain.h create mode 100644 lib/libospf.h create mode 100644 lib/linklist.c create mode 100644 lib/linklist.h create mode 100644 lib/log.c create mode 100644 lib/log.h create mode 100644 lib/md5.c create mode 100644 lib/md5.h create mode 100644 lib/memory.c create mode 100644 lib/memory.h create mode 100644 lib/memtypes.awk create mode 100644 lib/memtypes.c create mode 100644 lib/network.c create mode 100644 lib/network.h create mode 100644 lib/nexthop.c create mode 100644 lib/nexthop.h create mode 100644 lib/pid_output.c create mode 100644 lib/plist.c create mode 100644 lib/plist.h create mode 100644 lib/plist_int.h create mode 100644 lib/pqueue.c create mode 100644 lib/pqueue.h create mode 100644 lib/prefix.c create mode 100644 lib/prefix.h create mode 100644 lib/privs.c create mode 100644 lib/privs.h create mode 100644 lib/queue.h create mode 100644 lib/regex-gnu.h create mode 100644 lib/regex.c create mode 100755 lib/route_types.pl create mode 100644 lib/route_types.txt create mode 100644 lib/routemap.c create mode 100644 lib/routemap.h create mode 100644 lib/sigevent.c create mode 100644 lib/sigevent.h create mode 100644 lib/smux.c create mode 100644 lib/smux.h create mode 100644 lib/snmp.c create mode 100644 lib/sockopt.c create mode 100644 lib/sockopt.h create mode 100644 lib/sockunion.c create mode 100644 lib/sockunion.h create mode 100644 lib/str.c create mode 100644 lib/str.h create mode 100644 lib/stream.c create mode 100644 lib/stream.h create mode 100644 lib/table.c create mode 100644 lib/table.h create mode 100644 lib/thread.c create mode 100644 lib/thread.h create mode 100644 lib/vector.c create mode 100644 lib/vector.h create mode 100644 lib/version.h.in create mode 100644 lib/vrf.c create mode 100644 lib/vrf.h create mode 100644 lib/vty.c create mode 100644 lib/vty.h create mode 100644 lib/workqueue.c create mode 100644 lib/workqueue.h create mode 100644 lib/zassert.h create mode 100644 lib/zclient.c create mode 100644 lib/zclient.h create mode 100644 lib/zebra.h create mode 100644 m4/.gitignore create mode 100644 m4/Makefile.am create mode 100644 m4/README.txt create mode 100644 m4/ax_sys_weak_alias.m4 create mode 100644 nhrpd/Makefile.am create mode 100644 nhrpd/README.kernel create mode 100644 nhrpd/README.nhrpd create mode 100644 nhrpd/debug.h create mode 100644 nhrpd/linux.c create mode 100644 nhrpd/list.h create mode 100644 nhrpd/netlink.h create mode 100644 nhrpd/netlink_arp.c create mode 100644 nhrpd/netlink_gre.c create mode 100755 nhrpd/nhrp-events.lua create mode 100644 nhrpd/nhrp_cache.c create mode 100644 nhrpd/nhrp_event.c create mode 100644 nhrpd/nhrp_interface.c create mode 100644 nhrpd/nhrp_main.c create mode 100644 nhrpd/nhrp_nhs.c create mode 100644 nhrpd/nhrp_packet.c create mode 100644 nhrpd/nhrp_peer.c create mode 100644 nhrpd/nhrp_protocol.h create mode 100644 nhrpd/nhrp_route.c create mode 100644 nhrpd/nhrp_shortcut.c create mode 100644 nhrpd/nhrp_vc.c create mode 100644 nhrpd/nhrp_vty.c create mode 100644 nhrpd/nhrpd.h create mode 100644 nhrpd/os.h create mode 100644 nhrpd/reqid.c create mode 100644 nhrpd/resolver.c create mode 100644 nhrpd/vici.c create mode 100644 nhrpd/vici.h create mode 100644 nhrpd/zbuf.c create mode 100644 nhrpd/zbuf.h create mode 100644 nhrpd/znl.c create mode 100644 nhrpd/znl.h create mode 100644 ospf6d/.gitignore create mode 100644 ospf6d/Makefile.am create mode 100644 ospf6d/OSPFv3-MIB.txt create mode 100644 ospf6d/README create mode 100644 ospf6d/ospf6_abr.c create mode 100644 ospf6d/ospf6_abr.h create mode 100644 ospf6d/ospf6_area.c create mode 100644 ospf6d/ospf6_area.h create mode 100644 ospf6d/ospf6_asbr.c create mode 100644 ospf6d/ospf6_asbr.h create mode 100644 ospf6d/ospf6_flood.c create mode 100644 ospf6d/ospf6_flood.h create mode 100644 ospf6d/ospf6_interface.c create mode 100644 ospf6d/ospf6_interface.h create mode 100644 ospf6d/ospf6_intra.c create mode 100644 ospf6d/ospf6_intra.h create mode 100644 ospf6d/ospf6_lsa.c create mode 100644 ospf6d/ospf6_lsa.h create mode 100644 ospf6d/ospf6_lsdb.c create mode 100644 ospf6d/ospf6_lsdb.h create mode 100644 ospf6d/ospf6_main.c create mode 100644 ospf6d/ospf6_message.c create mode 100644 ospf6d/ospf6_message.h create mode 100644 ospf6d/ospf6_neighbor.c create mode 100644 ospf6d/ospf6_neighbor.h create mode 100644 ospf6d/ospf6_network.c create mode 100644 ospf6d/ospf6_network.h create mode 100644 ospf6d/ospf6_proto.c create mode 100644 ospf6d/ospf6_proto.h create mode 100644 ospf6d/ospf6_route.c create mode 100644 ospf6d/ospf6_route.h create mode 100644 ospf6d/ospf6_snmp.c create mode 100644 ospf6d/ospf6_snmp.h create mode 100644 ospf6d/ospf6_spf.c create mode 100644 ospf6d/ospf6_spf.h create mode 100644 ospf6d/ospf6_top.c create mode 100644 ospf6d/ospf6_top.h create mode 100644 ospf6d/ospf6_zebra.c create mode 100644 ospf6d/ospf6_zebra.h create mode 100644 ospf6d/ospf6d.c create mode 100644 ospf6d/ospf6d.conf.sample create mode 100644 ospf6d/ospf6d.h create mode 100644 ospfclient/.gitignore create mode 100644 ospfclient/AUTHORS create mode 100644 ospfclient/COPYING create mode 100644 ospfclient/INSTALL create mode 100644 ospfclient/Makefile.am create mode 100644 ospfclient/NEWS create mode 100644 ospfclient/README create mode 100644 ospfclient/ospf_apiclient.c create mode 100644 ospfclient/ospf_apiclient.h create mode 100644 ospfclient/ospfclient.c create mode 100644 ospfd/.gitignore create mode 100644 ospfd/ChangeLog.opaque.txt create mode 100644 ospfd/Makefile.am create mode 100644 ospfd/OSPF-ALIGNMENT.txt create mode 100644 ospfd/OSPF-MIB.txt create mode 100644 ospfd/OSPF-TRAP-MIB.txt create mode 100644 ospfd/ospf_abr.c create mode 100644 ospfd/ospf_abr.h create mode 100644 ospfd/ospf_api.c create mode 100644 ospfd/ospf_api.h create mode 100644 ospfd/ospf_apiserver.c create mode 100644 ospfd/ospf_apiserver.h create mode 100644 ospfd/ospf_asbr.c create mode 100644 ospfd/ospf_asbr.h create mode 100644 ospfd/ospf_ase.c create mode 100644 ospfd/ospf_ase.h create mode 100644 ospfd/ospf_dump.c create mode 100644 ospfd/ospf_dump.h create mode 100644 ospfd/ospf_flood.c create mode 100644 ospfd/ospf_flood.h create mode 100644 ospfd/ospf_ia.c create mode 100644 ospfd/ospf_ia.h create mode 100644 ospfd/ospf_interface.c create mode 100644 ospfd/ospf_interface.h create mode 100644 ospfd/ospf_ism.c create mode 100644 ospfd/ospf_ism.h create mode 100644 ospfd/ospf_lsa.c create mode 100644 ospfd/ospf_lsa.h create mode 100644 ospfd/ospf_lsdb.c create mode 100644 ospfd/ospf_lsdb.h create mode 100644 ospfd/ospf_main.c create mode 100644 ospfd/ospf_neighbor.c create mode 100644 ospfd/ospf_neighbor.h create mode 100644 ospfd/ospf_network.c create mode 100644 ospfd/ospf_network.h create mode 100644 ospfd/ospf_nsm.c create mode 100644 ospfd/ospf_nsm.h create mode 100644 ospfd/ospf_opaque.c create mode 100644 ospfd/ospf_opaque.h create mode 100644 ospfd/ospf_packet.c create mode 100644 ospfd/ospf_packet.h create mode 100644 ospfd/ospf_ri.c create mode 100644 ospfd/ospf_ri.h create mode 100644 ospfd/ospf_route.c create mode 100644 ospfd/ospf_route.h create mode 100644 ospfd/ospf_routemap.c create mode 100644 ospfd/ospf_snmp.c create mode 100644 ospfd/ospf_snmp.h create mode 100644 ospfd/ospf_spf.c create mode 100644 ospfd/ospf_spf.h create mode 100644 ospfd/ospf_te.c create mode 100644 ospfd/ospf_te.h create mode 100644 ospfd/ospf_vty.c create mode 100644 ospfd/ospf_vty.h create mode 100644 ospfd/ospf_zebra.c create mode 100644 ospfd/ospf_zebra.h create mode 100644 ospfd/ospfd.c create mode 100644 ospfd/ospfd.conf.sample create mode 100644 ospfd/ospfd.h create mode 100644 pimd/.gitignore create mode 100644 pimd/AUTHORS create mode 100644 pimd/CAVEATS create mode 100644 pimd/COMMANDS create mode 100644 pimd/COPYING create mode 100644 pimd/DEBUG create mode 100644 pimd/LINUX_KERNEL_MROUTE_MFC create mode 100644 pimd/Makefile.am create mode 100644 pimd/README create mode 100644 pimd/TODO create mode 100644 pimd/TROUBLESHOOTING create mode 100644 pimd/WHY_SSM create mode 100644 pimd/pim_assert.c create mode 100644 pimd/pim_assert.h create mode 100644 pimd/pim_cmd.c create mode 100644 pimd/pim_cmd.h create mode 100644 pimd/pim_hello.c create mode 100644 pimd/pim_hello.h create mode 100644 pimd/pim_iface.c create mode 100644 pimd/pim_iface.h create mode 100644 pimd/pim_ifchannel.c create mode 100644 pimd/pim_ifchannel.h create mode 100644 pimd/pim_igmp.c create mode 100644 pimd/pim_igmp.h create mode 100644 pimd/pim_igmp_join.c create mode 100644 pimd/pim_igmp_join.h create mode 100644 pimd/pim_igmpv3.c create mode 100644 pimd/pim_igmpv3.h create mode 100644 pimd/pim_int.c create mode 100644 pimd/pim_int.h create mode 100644 pimd/pim_join.c create mode 100644 pimd/pim_join.h create mode 100644 pimd/pim_macro.c create mode 100644 pimd/pim_macro.h create mode 100644 pimd/pim_main.c create mode 100644 pimd/pim_mroute.c create mode 100644 pimd/pim_mroute.h create mode 100644 pimd/pim_msg.c create mode 100644 pimd/pim_msg.h create mode 100644 pimd/pim_neighbor.c create mode 100644 pimd/pim_neighbor.h create mode 100644 pimd/pim_oil.c create mode 100644 pimd/pim_oil.h create mode 100644 pimd/pim_pim.c create mode 100644 pimd/pim_pim.h create mode 100644 pimd/pim_routemap.c create mode 100644 pimd/pim_rpf.c create mode 100644 pimd/pim_rpf.h create mode 100644 pimd/pim_signals.c create mode 100644 pimd/pim_signals.h create mode 100644 pimd/pim_sock.c create mode 100644 pimd/pim_sock.h create mode 100644 pimd/pim_ssmpingd.c create mode 100644 pimd/pim_ssmpingd.h create mode 100644 pimd/pim_static.c create mode 100644 pimd/pim_static.h create mode 100644 pimd/pim_str.c create mode 100644 pimd/pim_str.h create mode 100644 pimd/pim_time.c create mode 100644 pimd/pim_time.h create mode 100644 pimd/pim_tlv.c create mode 100644 pimd/pim_tlv.h create mode 100644 pimd/pim_upstream.c create mode 100644 pimd/pim_upstream.h create mode 100644 pimd/pim_util.c create mode 100644 pimd/pim_util.h create mode 100644 pimd/pim_version.c create mode 100644 pimd/pim_version.h create mode 100644 pimd/pim_vty.c create mode 100644 pimd/pim_vty.h create mode 100644 pimd/pim_zebra.c create mode 100644 pimd/pim_zebra.h create mode 100644 pimd/pim_zlookup.c create mode 100644 pimd/pim_zlookup.h create mode 100644 pimd/pimd.c create mode 100644 pimd/pimd.conf.sample create mode 100644 pimd/pimd.h create mode 100644 pimd/test_igmpv3_join.c create mode 100644 pkgsrc/.gitignore create mode 100644 pkgsrc/Makefile.am create mode 100644 pkgsrc/README.txt create mode 100644 pkgsrc/bgpd.sh.in create mode 100644 pkgsrc/ospf6d.sh.in create mode 100644 pkgsrc/ospfd.sh.in create mode 100644 pkgsrc/ripd.sh.in create mode 100644 pkgsrc/ripngd.sh.in create mode 100644 pkgsrc/zebra.sh.in create mode 100644 ports/.gitignore create mode 100644 ports/Makefile create mode 100644 ports/README create mode 100644 ports/files/.gitignore create mode 100644 ports/files/md5 create mode 100644 ports/pkg/.gitignore create mode 100644 ports/pkg/COMMENT create mode 100644 ports/pkg/DESCR create mode 100644 ports/pkg/PLIST create mode 100644 qpb/.gitignore create mode 100644 qpb/Makefile.am create mode 100644 qpb/README.txt create mode 100644 qpb/linear_allocator.h create mode 100644 qpb/qpb.c create mode 100644 qpb/qpb.h create mode 100644 qpb/qpb.proto create mode 100644 qpb/qpb_allocator.c create mode 100644 qpb/qpb_allocator.h create mode 100644 redhat/.gitignore create mode 100644 redhat/Makefile.am create mode 100644 redhat/README.rpm_build.md create mode 100644 redhat/bgpd.init create mode 100644 redhat/bgpd.service create mode 100644 redhat/isisd.init create mode 100644 redhat/isisd.service create mode 100644 redhat/nhrpd.service create mode 100644 redhat/ospf6d.init create mode 100644 redhat/ospf6d.service create mode 100644 redhat/ospfd.init create mode 100644 redhat/ospfd.service create mode 100644 redhat/pimd.init create mode 100644 redhat/pimd.service create mode 100755 redhat/quagga-filter-perl-requires.sh create mode 100644 redhat/quagga-tmpfs.conf create mode 100644 redhat/quagga.logrotate create mode 100644 redhat/quagga.pam create mode 100644 redhat/quagga.spec.in create mode 100644 redhat/quagga.sysconfig create mode 100644 redhat/ripd.init create mode 100644 redhat/ripd.service create mode 100644 redhat/ripngd.init create mode 100644 redhat/ripngd.service create mode 100644 redhat/watchquagga.init create mode 100644 redhat/zebra.init create mode 100644 redhat/zebra.service create mode 100755 release.sh create mode 100644 ripd/.gitignore create mode 100644 ripd/Makefile.am create mode 100644 ripd/RIPv2-MIB.txt create mode 100644 ripd/rip_debug.c create mode 100644 ripd/rip_debug.h create mode 100644 ripd/rip_interface.c create mode 100644 ripd/rip_interface.h create mode 100644 ripd/rip_main.c create mode 100644 ripd/rip_offset.c create mode 100644 ripd/rip_peer.c create mode 100644 ripd/rip_routemap.c create mode 100644 ripd/rip_snmp.c create mode 100644 ripd/rip_zebra.c create mode 100644 ripd/ripd.c create mode 100644 ripd/ripd.conf.sample create mode 100644 ripd/ripd.h create mode 100644 ripngd/.gitignore create mode 100644 ripngd/Makefile.am create mode 100644 ripngd/ripng_debug.c create mode 100644 ripngd/ripng_debug.h create mode 100644 ripngd/ripng_interface.c create mode 100644 ripngd/ripng_main.c create mode 100644 ripngd/ripng_nexthop.c create mode 100644 ripngd/ripng_nexthop.h create mode 100644 ripngd/ripng_offset.c create mode 100644 ripngd/ripng_peer.c create mode 100644 ripngd/ripng_route.c create mode 100644 ripngd/ripng_route.h create mode 100644 ripngd/ripng_routemap.c create mode 100644 ripngd/ripng_zebra.c create mode 100644 ripngd/ripngd.c create mode 100644 ripngd/ripngd.conf.sample create mode 100644 ripngd/ripngd.h create mode 100644 solaris/.gitignore create mode 100644 solaris/Makefile.am create mode 100644 solaris/README.txt create mode 100644 solaris/depend.daemons.in create mode 100644 solaris/depend.dev.in create mode 100644 solaris/depend.doc.in create mode 100644 solaris/depend.libs.in create mode 100644 solaris/depend.smf.in create mode 100644 solaris/pkginfo.daemons.tmpl.in create mode 100644 solaris/pkginfo.dev.tmpl.in create mode 100644 solaris/pkginfo.doc.tmpl.in create mode 100644 solaris/pkginfo.libs.tmpl.in create mode 100644 solaris/pkginfo.smf.tmpl.in create mode 100644 solaris/pkginfo.tmpl.in create mode 100644 solaris/prototype.daemons.in create mode 100644 solaris/prototype.dev.in create mode 100644 solaris/prototype.doc.in create mode 100644 solaris/prototype.libs.in create mode 100644 solaris/prototype.smf.in create mode 100755 solaris/quagga.init.in create mode 100644 solaris/quagga.xml.in create mode 100644 stamp-h.in create mode 100644 tests/.gitignore create mode 100644 tests/Makefile.am create mode 100644 tests/aspath_test.c create mode 100644 tests/bgp_capability_test.c create mode 100644 tests/bgp_mp_attr_test.c create mode 100644 tests/bgp_mpath_test.c create mode 100644 tests/bgpd.tests/Makefile.am create mode 100644 tests/bgpd.tests/aspathtest.exp create mode 100644 tests/bgpd.tests/ecommtest.exp create mode 100644 tests/bgpd.tests/testbgpcap.exp create mode 100644 tests/bgpd.tests/testbgpmpath.exp create mode 100644 tests/bgpd.tests/testbgpmpattr.exp create mode 100644 tests/common-cli.c create mode 100644 tests/common-cli.h create mode 100644 tests/config/unix.exp create mode 100644 tests/ecommunity_test.c create mode 100644 tests/global-conf.exp create mode 100644 tests/heavy-thread.c create mode 100644 tests/heavy-wq.c create mode 100644 tests/heavy.c create mode 100644 tests/lib/bgpd.exp create mode 100644 tests/lib/libzebra.exp create mode 100644 tests/libzebra.tests/Makefile.am create mode 100644 tests/libzebra.tests/tabletest.exp create mode 100644 tests/libzebra.tests/test-timer-correctness.exp create mode 100644 tests/libzebra.tests/testcli.exp create mode 100644 tests/libzebra.tests/testcommands.exp create mode 100644 tests/libzebra.tests/testnexthopiter.exp create mode 100644 tests/libzebra.tests/teststream.exp create mode 100644 tests/main.c create mode 100644 tests/prng.c create mode 100644 tests/prng.h create mode 100644 tests/table_test.c create mode 100644 tests/test-buffer.c create mode 100644 tests/test-checksum.c create mode 100644 tests/test-cli.c create mode 100644 tests/test-commands.c create mode 100644 tests/test-memory.c create mode 100644 tests/test-nexthop-iter.c create mode 100644 tests/test-privs.c create mode 100644 tests/test-segv.c create mode 100644 tests/test-sig.c create mode 100644 tests/test-stream.c create mode 100644 tests/test-timer-correctness.c create mode 100644 tests/test-timer-performance.c create mode 100644 tests/testcli.in create mode 100644 tests/testcli.refout create mode 100644 tests/testcommands.in create mode 100644 tests/testcommands.refout create mode 100644 tests/tests.h create mode 100644 tools/.gitignore create mode 100644 tools/mrlg.txt create mode 100644 tools/multiple-bgpd.sh create mode 100644 tools/zebra.el create mode 100755 update-autotools create mode 100644 vtysh/.gitignore create mode 100644 vtysh/Makefile.am create mode 100755 vtysh/extract.pl.in create mode 100644 vtysh/vtysh.c create mode 100644 vtysh/vtysh.conf.sample create mode 100644 vtysh/vtysh.h create mode 100644 vtysh/vtysh_config.c create mode 100644 vtysh/vtysh_main.c create mode 100644 vtysh/vtysh_user.c create mode 100644 vtysh/vtysh_user.h create mode 100644 watchquagga/.gitignore create mode 100644 watchquagga/Makefile.am create mode 100644 watchquagga/watchquagga.c create mode 100644 zebra/.gitignore create mode 100644 zebra/GNOME-PRODUCT-ZEBRA-MIB create mode 100644 zebra/GNOME-SMI create mode 100644 zebra/Makefile.am create mode 100644 zebra/client_main.c create mode 100644 zebra/connected.c create mode 100644 zebra/connected.h create mode 100644 zebra/debug.c create mode 100644 zebra/debug.h create mode 100644 zebra/if_ioctl.c create mode 100644 zebra/if_ioctl_solaris.c create mode 100644 zebra/if_netlink.c create mode 100644 zebra/if_sysctl.c create mode 100644 zebra/interface.c create mode 100644 zebra/interface.h create mode 100644 zebra/ioctl.c create mode 100644 zebra/ioctl.h create mode 100644 zebra/ioctl_null.c create mode 100644 zebra/ioctl_solaris.c create mode 100644 zebra/ioctl_solaris.h create mode 100644 zebra/ipforward.h create mode 100644 zebra/ipforward_proc.c create mode 100644 zebra/ipforward_solaris.c create mode 100644 zebra/ipforward_sysctl.c create mode 100644 zebra/irdp.h create mode 100644 zebra/irdp_interface.c create mode 100644 zebra/irdp_main.c create mode 100644 zebra/irdp_packet.c create mode 100644 zebra/kernel_netlink.c create mode 100644 zebra/kernel_null.c create mode 100644 zebra/kernel_socket.c create mode 100644 zebra/kernel_socket.h create mode 100644 zebra/main.c create mode 100644 zebra/misc_null.c create mode 100644 zebra/redistribute.c create mode 100644 zebra/redistribute.h create mode 100644 zebra/redistribute_null.c create mode 100644 zebra/rib.h create mode 100644 zebra/router-id.c create mode 100644 zebra/router-id.h create mode 100644 zebra/rt.h create mode 100644 zebra/rt_netlink.c create mode 100644 zebra/rt_netlink.h create mode 100644 zebra/rt_socket.c create mode 100644 zebra/rtadv.c create mode 100644 zebra/rtadv.h create mode 100644 zebra/rtread_getmsg.c create mode 100644 zebra/rtread_netlink.c create mode 100644 zebra/rtread_sysctl.c create mode 100644 zebra/test_main.c create mode 100644 zebra/testrib.conf create mode 100644 zebra/zebra.conf.sample create mode 100644 zebra/zebra_fpm.c create mode 100644 zebra/zebra_fpm.h create mode 100644 zebra/zebra_fpm_dt.c create mode 100644 zebra/zebra_fpm_netlink.c create mode 100644 zebra/zebra_fpm_private.h create mode 100644 zebra/zebra_fpm_protobuf.c create mode 100644 zebra/zebra_rib.c create mode 100644 zebra/zebra_rnh.c create mode 100644 zebra/zebra_rnh.h create mode 100644 zebra/zebra_rnh_null.c create mode 100644 zebra/zebra_routemap.c create mode 100644 zebra/zebra_snmp.c create mode 100644 zebra/zebra_vty.c create mode 100644 zebra/zserv.c create mode 100644 zebra/zserv.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8da62f --- /dev/null +++ b/.gitignore @@ -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 index 0000000..61867a8 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Kunihiro Ishiguro +Toshiaki Takada +Yasuhiro Ohara +Alex D. Zinin +Gleb Natapov +Akihiro Mizutani diff --git a/COPYING b/COPYING new file mode 100644 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. + + + Copyright (C) 19yy + + 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. + + , 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 index 0000000..2b4628e --- /dev/null +++ b/COPYING.LIB @@ -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. + + + Copyright (C) + + 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. + + , 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 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 index 0000000..078de60 --- /dev/null +++ b/HACKING.md @@ -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) [, 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) .... + Copyright (C) [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 \ +with \$) is: + +`$QuaggaId: Format:%an, %ai, %h $` + +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: + +lib/log.h logging levels and usage guidance + +[more to be added] + +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 (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 + +- 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 + + + $ 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 " commands + Applying: zebra: route_unlock_node is missing in "show ip[v6] route " 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: + +- 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 ... | 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: + + + + +* Bugs can be reported via Bugzilla at: + + + +* Buildbot runs CI tests, and is at: + + + + 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: + + + + +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 .. | 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 index 0000000..73d3194 --- /dev/null +++ b/HACKING.pending @@ -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 index 0000000..11c85b1 --- /dev/null +++ b/INSTALL.quagga.txt @@ -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 index 0000000..3c3b65e --- /dev/null +++ b/Makefile.am @@ -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 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 ' 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 + +- 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 " + "show ip bgp community 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 + . 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 +. + +** MBGP support is added by Robert Olsson . +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 ' 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 ' 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 + +* 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 + +* 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 +. + +* Changes in zebra + +** Basic Linux policy based routing table support is added by Stephen +R. van den Berg . + +* 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 + + +** Check of network 127 is added. Reported by Carlos Alberto +Barcenilla + +* 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 . + +* Changes in zebra + +** Fix bug of display BGP routes as "O" instead of "B". Reported by +"William F. Maton" and Dave Hartzell +. + +* Changes in bgpd + +** `no network IPV6_NETWORK' statement and `no neighbor IP_ADDR timers +holdtime [TIMER]' statement doesn't work. Reported by Georg Hitsch +. Now both statement work. + +* Changes in ospfd + +** Last interface is not updated by ospf_if_update(). Reported by +Dave Hartzell . + +* 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 +. + +* 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 . + +! +line vty + access-class ACCESS-LIST-NAME +! + +** `show version' command added. Implemented by Carlos Alberto +Barcenilla + +* 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 + + +** 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 . + +* 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 . + +* 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 . + +** syslog logging feature is added by Peter Galbavy + + +** Inclusion of standard header files is reworked by Peter Galbavy + + +** 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 . Now +several files are included in ospfd directory. + +** ospf6d codes are merged from Yasuhiro Ohara '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 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 index 0000000..6bbc680 --- /dev/null +++ b/README.NetBSD @@ -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 index 0000000..83f4eb9 --- /dev/null +++ b/REPORTING-BUGS @@ -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 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 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 index 0000000..105be22 --- /dev/null +++ b/bgpd/.gitignore @@ -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 index 0000000..c911316 --- /dev/null +++ b/bgpd/BGP4-MIB.txt @@ -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 + . + + 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 index 0000000..fff360a --- /dev/null +++ b/bgpd/IMPLEMENTATION.txt @@ -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 index 0000000..753b679 --- /dev/null +++ b/bgpd/Makefile.am @@ -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 index 0000000..1b17b66 --- /dev/null +++ b/bgpd/bgp_advertise.c @@ -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 + +#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 index 0000000..e2857a3 --- /dev/null +++ b/bgpd/bgp_advertise.h @@ -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 + +/* 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 index 0000000..d813bfb --- /dev/null +++ b/bgpd/bgp_aspath.c @@ -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 + +#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 index 0000000..c8f929f --- /dev/null +++ b/bgpd/bgp_aspath.h @@ -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 index 0000000..a79a03c --- /dev/null +++ b/bgpd/bgp_attr.c @@ -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 + +#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 index 0000000..9ff074b --- /dev/null +++ b/bgpd/bgp_attr.h @@ -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 index 0000000..b408efd --- /dev/null +++ b/bgpd/bgp_btoa.c @@ -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 + +#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 index 0000000..13bdf8e --- /dev/null +++ b/bgpd/bgp_clist.c @@ -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 + +#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 index 0000000..d9db418 --- /dev/null +++ b/bgpd/bgp_clist.h @@ -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 index 0000000..f1997bd --- /dev/null +++ b/bgpd/bgp_community.c @@ -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 + +#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 index 0000000..c73dab3 --- /dev/null +++ b/bgpd/bgp_community.h @@ -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 index 0000000..ac64723 --- /dev/null +++ b/bgpd/bgp_damp.c @@ -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 +#include + +#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 index 0000000..16fd367 --- /dev/null +++ b/bgpd/bgp_damp.h @@ -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 index 0000000..ba79722 --- /dev/null +++ b/bgpd/bgp_debug.c @@ -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 + +#include +#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 index 0000000..253bd7f --- /dev/null +++ b/bgpd/bgp_debug.h @@ -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 index 0000000..01f9b41 --- /dev/null +++ b/bgpd/bgp_dump.c @@ -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 + +#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 index 0000000..d1e66d9 --- /dev/null +++ b/bgpd/bgp_dump.h @@ -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 index 0000000..5d69b42 --- /dev/null +++ b/bgpd/bgp_ecommunity.c @@ -0,0 +1,776 @@ +/* BGP Extended Communities Attribute + 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 + +#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 index 0000000..993fd5a --- /dev/null +++ b/bgpd/bgp_ecommunity.h @@ -0,0 +1,88 @@ +/* BGP Extended Communities Attribute. + 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_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 index 0000000..cd58ac2 --- /dev/null +++ b/bgpd/bgp_encap.c @@ -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 + * + */ + +/* + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU 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 + +#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 /, 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 /, 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 index 0000000..7442c73 --- /dev/null +++ b/bgpd/bgp_encap.h @@ -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 index 0000000..347b4b3 --- /dev/null +++ b/bgpd/bgp_encap_tlv.c @@ -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 + +#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 index 0000000..d94d544 --- /dev/null +++ b/bgpd/bgp_encap_tlv.h @@ -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 index 0000000..603ff9d --- /dev/null +++ b/bgpd/bgp_encap_types.h @@ -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 index 0000000..ab1e504 --- /dev/null +++ b/bgpd/bgp_filter.c @@ -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 + +#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 index 0000000..c1da904 --- /dev/null +++ b/bgpd/bgp_filter.h @@ -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 index 0000000..4198a8e --- /dev/null +++ b/bgpd/bgp_fsm.c @@ -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 + +#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 index 0000000..752d6e2 --- /dev/null +++ b/bgpd/bgp_fsm.h @@ -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 index 0000000..c03939f --- /dev/null +++ b/bgpd/bgp_fsm_4271.dot @@ -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 index 0000000..2b9bee8 --- /dev/null +++ b/bgpd/bgp_fsm_quagga.dot @@ -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 index 0000000..cc67e12 --- /dev/null +++ b/bgpd/bgp_lcommunity.c @@ -0,0 +1,562 @@ +/* BGP Large Communities Attribute + +Copyright (C) 2016 Keyur Patel + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU 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 + +#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 index 0000000..7841b4b --- /dev/null +++ b/bgpd/bgp_lcommunity.h @@ -0,0 +1,75 @@ +/* BGP Large Communities Attribute. + +Copyright (C) 2016 Keyur Patel + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU 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 index 0000000..af9c030 --- /dev/null +++ b/bgpd/bgp_main.c @@ -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 + +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "getopt.h" +#include "thread.h" +#include +#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 : ""), + 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 index 0000000..8195e47 --- /dev/null +++ b/bgpd/bgp_mpath.c @@ -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 + +#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 index 0000000..2a84d5e --- /dev/null +++ b/bgpd/bgp_mpath.h @@ -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 index 0000000..ac3ee08 --- /dev/null +++ b/bgpd/bgp_mplsvpn.c @@ -0,0 +1,1063 @@ +/* MPLS-VPN + 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 + +#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 /, 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 /, 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 /, 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 index 0000000..3299b9c --- /dev/null +++ b/bgpd/bgp_mplsvpn.h @@ -0,0 +1,51 @@ +/* MPLS-VPN + 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_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 index 0000000..1604239 --- /dev/null +++ b/bgpd/bgp_network.c @@ -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 + +#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 index 0000000..31995ca --- /dev/null +++ b/bgpd/bgp_network.h @@ -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 index 0000000..479ef94 --- /dev/null +++ b/bgpd/bgp_nexthop.c @@ -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 + +#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 index 0000000..fe4f5ad --- /dev/null +++ b/bgpd/bgp_nexthop.h @@ -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 index 0000000..1158ab1 --- /dev/null +++ b/bgpd/bgp_nht.c @@ -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 + +#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 index 0000000..dd6300e --- /dev/null +++ b/bgpd/bgp_nht.h @@ -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 index 0000000..2800423 --- /dev/null +++ b/bgpd/bgp_open.c @@ -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 + +#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 index 0000000..6233375 --- /dev/null +++ b/bgpd/bgp_open.h @@ -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 index 0000000..b497e45 --- /dev/null +++ b/bgpd/bgp_packet.c @@ -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 + +#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 index 0000000..6b0b7f4 --- /dev/null +++ b/bgpd/bgp_packet.h @@ -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 index 0000000..13fa829 --- /dev/null +++ b/bgpd/bgp_regex.c @@ -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 + +#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 index 0000000..9fc8322 --- /dev/null +++ b/bgpd/bgp_regex.h @@ -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 + +#ifdef HAVE_LIBPCREPOSIX +# include +#else +# ifdef HAVE_GNU_REGEX +# include +# 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 index 0000000..13596fb --- /dev/null +++ b/bgpd/bgp_route.c @@ -0,0 +1,17989 @@ +/* BGP routing information + Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro + Copyright (C) 2016 Job Snijders + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU 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 + +#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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /\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 /\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 /\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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /\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 + + */ +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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /\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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /, 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 /, 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 /, 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 /, 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 /\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 /\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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /\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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 index 0000000..332714c --- /dev/null +++ b/bgpd/bgp_route.h @@ -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 index 0000000..ccd73b6 --- /dev/null +++ b/bgpd/bgp_routemap.c @@ -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 + +#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 +#else +# ifdef HAVE_GNU_REGEX +# include +# 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 index 0000000..c4490bf --- /dev/null +++ b/bgpd/bgp_snmp.c @@ -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 + +#ifdef HAVE_SNMP +#include +#include + +#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 index 0000000..7a0d9dd --- /dev/null +++ b/bgpd/bgp_snmp.h @@ -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 index 0000000..92bb957 --- /dev/null +++ b/bgpd/bgp_table.c @@ -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 + +#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 index 0000000..8e963ae --- /dev/null +++ b/bgpd/bgp_table.h @@ -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 index 0000000..0040d62 --- /dev/null +++ b/bgpd/bgp_vty.c @@ -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 + +#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 /, 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 /, 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 /, 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 = ""; + 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 = ""; + 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 index 0000000..7329c5f --- /dev/null +++ b/bgpd/bgp_vty.h @@ -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 index 0000000..40ecbce --- /dev/null +++ b/bgpd/bgp_zebra.c @@ -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 + +#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 index 0000000..5d4ed62 --- /dev/null +++ b/bgpd/bgp_zebra.h @@ -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 index 0000000..6aeecb1 --- /dev/null +++ b/bgpd/bgpd.c @@ -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 + +#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, " "); + 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 index 0000000..b6a8b6f --- /dev/null +++ b/bgpd/bgpd.conf.sample @@ -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 index 0000000..d376ad2 --- /dev/null +++ b/bgpd/bgpd.conf.sample2 @@ -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 index 0000000..03df2f6 --- /dev/null +++ b/bgpd/bgpd.h @@ -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 index 0000000..c0b95d3 --- /dev/null +++ b/bootstrap.sh @@ -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 index 0000000..04fc2cc --- /dev/null +++ b/buildtest.sh @@ -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 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 index 0000000..ae292f1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,1713 @@ +## +## Configure template file for Quagga. +## autoconf will generate configure script. +## +## Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro +## Portions Copyright (c) 2003 Paul Jakma +## +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 +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +/* sys/conf.h depends on param.h on FBSD at least */ +#if HAVE_SYS_PARAM_H +# include +#endif +/* Required for MAXSIG */ +#if HAVE_SIGNAL_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#ifdef __APPLE__ +# define __APPLE_USE_RFC_3542 +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# 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 +#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 +#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 ])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 +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_VAR_H +# include +#endif +#if HAVE_NET_IF_DL_H +# include +#endif +#if HAVE_NET_NETOPT_H +# include +#endif +#if HAVE_NET_ROUTE_H +# include +#endif +#if HAVE_INET_ND_H +# include +#endif +#if HAVE_ARPA_INET_H +# include +#endif +/* Required for IDRP */ +#if HAVE_NETINET_IP_ICMP_H +# include +#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 ]) + 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 + ]) + 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 +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#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 +#endif]) +AC_MSG_CHECKING([for BSD struct ip_mreq hack]) +AC_TRY_COMPILE([#ifdef HAVE_SYS_PARAM_H +#include +#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 +#include +], [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 + ]) + 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 + ]) + 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 + ]) + 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 +#endif +#if HAVE_NETINET_IN6_VAR_H +#include +#endif +#if HAVE_NETINET_ICMP6_H +# include +#endif +#if HAVE_NETINET6_IN6_VAR_H +# include +#endif +#if HAVE_NETINET6_ND6_H +# include +#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 ], [ + 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 ], [ + 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 ], [ + 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 ],[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 ],[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 ],[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 ]], + [[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 index 0000000..459531b --- /dev/null +++ b/doc/.gitignore @@ -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 index 0000000..b321807 --- /dev/null +++ b/doc/BGP-TypeCode @@ -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 index 0000000..38920c8 --- /dev/null +++ b/doc/Makefile.am @@ -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 index 0000000..3904c5f --- /dev/null +++ b/doc/appendix.texi @@ -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 index 0000000..92e8d9e --- /dev/null +++ b/doc/basic.texi @@ -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..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 index 0000000..e680ddb --- /dev/null +++ b/doc/bgpd.8 @@ -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 index 0000000..d5aa30c --- /dev/null +++ b/doc/bgpd.texi @@ -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{}} {} +@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 :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 index 0000000..b7b529e --- /dev/null +++ b/doc/defines.texi @@ -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 index 0000000..2599472 --- /dev/null +++ b/doc/draft-zebra-00.ms @@ -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 +.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 index 0000000..c9e8e68 --- /dev/null +++ b/doc/fig-normal-processing.dia @@ -0,0 +1,1738 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Local +RIB# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“Out” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“In” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-normal-processing.png b/doc/fig-normal-processing.png new file mode 100644 index 0000000000000000000000000000000000000000..e4b8fdcc1a2c202af6219fa1a38b12408eed7ac7 GIT binary patch literal 29958 zcma&O1yCJP(=B>%cXvo|2u^T!C%8*+cMp=_PH=a3ch}&--QC^oO}=~oYwy0QHwDEx zr_SuzGrPNY_gbq7Rg{-Ng2#skfj~%7lA=l=5O^>M1f~WH4tx^-nw<;$fp8L*Qh|kq zUD;CD1m42gOKLiSKrny*{ercGA7p_*q#!9#Ar<%Z(+m$CmARLpEH5)7ydZBFC>%iw z9S|1#luA9D^tktU_3Cs)EX$V}`YS8V<;n7~S(YYgit%1uCooJbEEQv?MLgVO?~aFu zp#g^gFc?#l{q~Pbm&ulc%mcjk1D|uQhx7vjaw%}|DHg{TUUv4o>%Gyyz`&=cC-JH- zuZMHpR%bjmb46)s`0xQ1&o|hBJ4JQ%CX1Ou1RRFyrBmOJw}lY)VBn0Ppj=mD2m{Fk zw2GL_%*^=s{yoQW13FGl_n{~vI5;@d>OjF{ykDe^wrj19d(xqz;2>EEfx!A{%%ETX z{=TnQLsNMYX}OL8c!H%7=D(GTW5}06`PGx;5NCG)g}Na;t9=gI_LPFX>hD86fRb|K+zh*7MO8ePIlWW^Ko9WNC+I$_J- zf}u-TggL-}lCrSGj_yr3I4m6=g{CbMf>q}>x;81|5)%G6m>@z9Hu~-l8}QWZ^Y+~R z6GD~NWiiHX4MLA8ha#4GL{D4So|KdG@&0V3va*u0K9C8i&}Z=zH*Fe+-M(YXn?||H zj0_8C6I7C!`KCe7kksaJV>&}1N;)hz_LDME{~5=HHkY@|L?G>O1^feemZWnEoe=<8oWO>!wEdhCrZU*EM>7X)m2 zNf{Z|z={s92PJF*eEd^;u&nm$PoozeGG0FgNl_=owVtc|eR;*5%ufOdIBazqEU`eC z*w|4-JYN|ZqemXDucy1vtxyZaRnMmjWJE;+Xb_tM8V8b-aRmgr`ujzdaX(2pUaWU~ zmWbkYIiB6>3qF|0u!5(XkEq?sIb2s@=)h*}F#2+a0RB_39(rO^g@|AR*=w!kMKrYk54W~*)XGYBj*pK4r*hyVq@pSc#h|)& zZ=b(O<6H_jS*Q|lzuZi->pYq*nX&77D~x9S8sxN4A}s`VvAVkK&xrNk~XgK*PhsVq#*z)TtCQUvDM_nyi;{X%b^&YaRDSCi!1Z{ZaUndECz1E;`dv zQ#}v==CGPg9Nz3GWb)!+VL^e&r6?&Wub*C5S6AEHc>$}p-|AJnPUBe$(D!|7aJyLN z^SV#xb~!$pDN1|g_5$Vc)b<^K`L$DyS}(WjlY@gOq=?bNtkMiOjz91xA$W%7HsN*74W%Vz?YkHBLs-bv(7SeTt{ZfQY-?Af`BBIauayxboK z1zb@4KZlx{nxY~qh+e1pvZ85Si5e4lBHTAAvW1Ota*8D?FAo<*>HqEnxC9C@Z?p5! zwA#mKYt(~EWw8f0^lNTV%!2Xo=+PLODcvOot$VSN=5^PbCgPUuiU@vDU+bH$$#A%OJP-2RlT~pN=QiX@$t#vaa(V) z%OWaenbXDIwVvKhR!Kt%5d~FGkDsyff28W@WvjTFFhy8_|E#vL3`2ncNfleR_`bMi zm|D1vA?{D_RJ4wcvRO3b=e|Ev<-YJN^5Jl~;KOLF%-=RL=q+pbGc{~=Kn!I)i^PzJ z8YaIdapV*{fR3?@RvK;i`T0|2?d&cRueFQ_G%4eHYI9wvOLQjYqe< zPS--|{))+0#rOw*nJS7XA-HF&y7z4pq~2L*Rb+U0M@Pqz@$s{jCPvWi?yh1U*dWE% zuh<|o_c;fl<&?d=f5nWa-@r6aQ6)C1#4P~}3Tw*l(Tkf2{#+V6+=>bc9>EQU4yGLk&U zCH&0LO!x+z25g8-$GBEwJq=M78b%X4;C_4)`Fsqa9Br=-VCHx9_bRHmyf%jx9H2h7 zA^8jpC@%f4AKz#V(LO9||FDl0ET9K>^X{(K~Wp-XF)aC8LR7`W$}zi6KJ|C1+)I>J5PVve#5w&;8|#T#1Th)f6%u zoX7jqf$z&%Qv#iOV_u%VPq8|Wuc}11#RLOX=xfpH^n-JW9eM192__UHg<61yl3~i45T4O=u)`;Xyf0(Kp1_LwM&=o z(v9ybZ+y}e)Ye%jumP$$<&!qb9zt`ff%PMGGaBjpn?&4TAol@@pQ3b#F4?;q6&=lf zZmxyx+R1S zbc-k;Vn#MyD?%}RGmkX5upQca5&Z;ujGz37pIn&0!RX&WAMS4ybyWSzi@GZ9a`xcB zvnohR4^LCpOgi=^uj$rSp`!5Dc2h65unLhKk6oy^e}^bzcDD$vhF#4je40fUw%S@{FbLi zf*QJM^}0$I)srfCw%Dtl}2fYz|)+zIy_m9w{CNE@+<*z|6go>a9FM>0Xh*|%sy zc`((xL!TbnZ|?m3CKH*!Q9TD&P0vS#yJjD(s9f`gCnv z?&{e{5RBY}fpg>g+$9hIyYsZuh}S%ip|mPuFGuA)Tv^U+edhhOlxS#p^LHSFc%Zuu zr+4JIi_(@dSTH0L&Y&E6-YLTqdfYt`L6F@-ze9c+?xeYdcrmB^mfa@`+(!9rUgP8C zYRDNERwF?j_!{xR7kp-+FG~X1yBiY_?^jueZ#v}fYD9lZ5bO%4Ck@L*2>y9 zCBdU#fk|!Bzz&2ziG)Cpl{C?%e*0FVqNF9h{h6hVFMA0df(qPi*&Y&0hu=CFhK&no z=QydT+;oQe-%G6TOIuqZzF&P(SNB%ScgT$;u=NolqO-JvyZF%;D*ZLF3^u@lbS^g5 zDA!A%p7SqI;5?VXRz1RkASEbXT)k+mL-OC(&u*`fRFkl#E%#@B*~AqO6ijdTrIH7+ zW(W_}^=(wgF|+26xc+pNSjzwQm8#AKWnRwNRj=>hfCkeIGwdLF%xr#t)jO#DxwQP1 zJ^_<&ao`&7&ozOyXfRrcs|Y%+#qc-7breg7BUXf&#f;nX`oc%ppcy`%j@jLaIPC!7 zC;wgVEYIkD>t=b&en>dEEs|UX{VinFQ{H=U{SF|A?9pp#;m_p)C^(y7$E>6t@*_$5|Agtu9;cOSImFu;U zvHw^1w)F;zJq2DWP~tCjhSxXu;0VT+C#b5&V&BJ;XVJRj=Hh^X*w`+n);gyJn$zl* zX8)oBJ{AsY#N4yb42zSS&Ug5YqfUzq7*e$~-6^)`5brP=JEu&-dg@tMU||J*)#T)4 z?R8>WKLTP@PduQ+jiw0S?r1cXe?WrTxw~?2%FPaHaK+=;cu{X$?8*#SLv9|LZ{26y zthuRZmyjv&6fx1f1S#AXqn^T4M5i0Y(U=Ko!$S5OG55V0+i#4iROEMAT%$S0Ptn1r zFk={CXeMpTvKLeVQgCxgejK|B_>k*!F(q zDjcjQyot`7wZ|$Lhq?Nu)-&3eEdUD%0tL-*=@Rio?um9Ba5)FdOp1NMsvm}GC>-dd zz>A^%Tuy}OMi3KL$BQ4ApPF^WpJQHKtN`1O5%1y#+4Qy18M}lfw*n}JG>l3r z%}tw&DH859%Rt|)v-v|c%W~69vA>~81G%^NJb^Buf%UI?!sGwJ35qXO*=LhZ2@jh_vs$Nc z#G9#WqNMrrq@)?g&eGgc~pD23O#CcshZ*Hr3OUqh6)= zZ-;y}As#9=C!d;{`X9BKAxjF#UM9!gRO+*E%FV+6sh81keBl_Fp+29YT<`g}GwdXfD2ijEMiBvy=J6&q`)1cBzEoe15Ntlyni& zdQ#(t*Rl(+rA1Er#I~32Dz=QefhWvbRIAzj~* z^{&Sk0kjE}5Wwc#MpI5Ii=HgkjXU}BT62RXEbmAQ*5wcGJa2nov4R^c`O6px?`x$az3I|ap{N0uvh;(RroUqWbpC+ zwgEsRPd5h{e4gQ!cKB0tjR+F7XwmrWmO$QBoV1!F1fU#|x3{-A4i@Uj!G{g0!sKhG5KjB>{ zrxkog9TZdZRN_fb7wKkl?dL^!_OCVsVh1-9P$;DrQ_%?mmkV%fs#hZZX%0jCP3o#_1b{kw0pZptEF(=?X6*0|gn~$%RF1`Z^sh@o(%~7{i-? zPr}{4Fzv-J(r>DmoUClm%*P-%I|#N-Sm}p~(rqy15$uy2K3cH>Z)$@~2v%&~pjudyGygj_fz^`}GQ+NRG zN&B_O5kSPR4us7NY=PH`f?nDl5vRw1Lq+P7b68uOS#mtvtx9k824D^T11&WM$?WpT zMpMfv*;~cc@>h8<3#+ZzJNe_i2|I&e1GCSM+sJdWV{!e@h@0?iB!ZZz%-!GvkaDq; z=F-z8sRLDF&(XK}u2IF^z(M0`Zdns|X)I$ELnjZ42!gymh_Nn7iGM~W`6dQ=x4-6n zQ9w0?zDq#+3JauYs#a*>(i%Ed4gFA{SKh-q@s>8ut#@L?I|aF23@RKuHdUb3nc7tBRkAi9xM|J>iT`&NW{55Rr>tTCf|RtuFxtfsOW8n~w5 zfJ+ZX;MKXFX|?FsIv*URGgNx3S;me$zocP9$`bDv4m*gIT0gY&3~TFotHv2ikk`Cs za67jf_lau3inEDLDALZ?EE0jnrXYEzR znOEs}?H!)ab^Ta`^L_o8yvEz)a)vPwKd)RA=MheZ`Tq*7ty2}iZkc3KJNx`BL5me+ zcyx3GfNyM-a7r|#T{r&wfR$KbEa9hiGOA^Zh#7cvH6p-rA#Z>Jcl$fw{%ji`SXwo9F|0T@r`)Sl~ zU)9B%k~%reR`r-6dx5*7HC6vPt5R9`#oPK;&s|wEm)gt5r3cKY;d8(fv(EyIA%XRJ zd&=nEy-|&l9OE#^4%kN`IL2rsOu(y);?YQ>ehrk~-E$_x6f^dna?HU5IhW7Sxr=w7@A;<59|8My4 z8H8C&+pSiwefZxGbhfQgWH?89WTTCFa-!FrB{aL_UQ`#>7YTC1%VTyz#HU-6KO z)y{LCS+*Q&R^(t^`8bOB*f^w1}DQBKIIDK)={Xx0qlNjr7 zYHB{632%f_-5>N>Wt8Jzse!zfO_s0srI;WuQJPkDw~Pd5<4;oZAHumPXxaH1fiS6I z&P9lbg@5)5xOXFBUq$fH=En_jP4pf|a{&u3D#NX(lV@Nxskbhp6*(S=e@)rtCTA9i zTBvgW{L34cc+!La!++sYUy~og8Cx(b@ho6M{z*3-h~YXGNm(C4A9t`P;4psshTj1k z&rGRoxzV1`&CN|gR+gTTkt2hQG7bPWU3UMV<)sNaVn(Oc#Vx0+=ut z=VAcCkvV-VISqFB#YR^bz=Tv+SJ&6GDdWP!!Ie~1Z2<5_r}qm0Jf)|nM_3Jvv}ie- z7?|s5SJg&djEv{_EOgoo|4u(4j>Z{O`SXi$K8pFbSOB}=P<-BxD06<*VyWknRCWA# z;#9soSz{b;bz8bKFFi0p504f>Qji}IDr$^|_V?J|$2;2p9k(St(g7t79gay1x}QER zS10$|*MAx8?|*!HqM)QqomI@@_x5={DyprmJzr}RxS`d~EpEJQvg&FlPMYN?&MNS( z!KU34N?;cPLyq=zsBB|(uSE1wRhFpoV`W&@B;Gf}i2rqiYkAFqp!2LPcKa68wj z(P($S+7<~$uF)uOTKBYid%ojb=Y$5xrp_Nv*TBsIFwsOZv(aj^0~Z(9&QSEnS<}X$ zXMn}+<)va7LDN-9Wu;!D_0rYOkSgt5IAiDI+rx&k9ru?#4u@I_3|``B`J$Dr&lW!} z;9e@)cp~n+;V0xGBR)+IT6S3A>@)qxf4dYp}4X2KNxLx)@P#@ zYyOfDM-S~>0bn9J21YOvfvJ+V+FjVr-X7L^%8VH_v=BXQktDhvJzQP6dPpME)J-YH zl_gO)ta6^-{69dNZ%J}!a#F#m-E=(l?qp$TXb3_O80_1(Z?3M5lH{oXl3i7G!E@0r zf+Zs*rP<K}AJHL2-mZ^#)K5zCbJNnaZEY*H zDl*wDrh5YsP>8r^m3*A2((=^GZfz!V<%S8`sZUN>VX*G#8Sm_2Z}~du z;6Mh3N&X~RFcLFtOM{7Jd51$hzrx7ntKDE@$Gllz?P2G2do+1{T~jYXDKt@5*_M*h zdw0kAl?gA`o5Q&LZCnY?HCyWh$(QtKm?r5yLoN+z)w6p+GhN5 z!?UinTzsCl*~ri+#O(l*pZNaVh;;WnoOB7+J-ggKd?RK5!K!o=Us}2$_Q^_ZZ>C5g zjvSB0A@M_UM&7ZWo}Q893oLw|2a)EBjH+lz_#dx; z)cRw|J|ICuVuN1!xwiD#J;HZU2eHX=GBR)~OT8jI>FYeHY~;s~hlj_P$X*ou*+;$M zJ;wqi3u9xWm{i~64As&e4cEe-Ti&=?%5LM z|2M`@#mLA=PhVbHc@8W-VD#HB7hM8IgArcnjJR45~RG1{MDXEx7@a30S5-%>sZ)Dq%96W>X2K>53ASv_!VKN4c>)L_WHfamh zgH967Wkx51+XK~;?Se>lSY?9s_SA@8<+!H1uJ#rM?hS}y^$44l#-~SKkJ0=4ds`ZP z5^c^XFAop7WF|6ThQ1%~Rh`eJb#+c{d>7f~T|Smf4POORXu(K(+YNbEM*4zLz`F50 zT}=I+QwlXsg$x_2IA>2Jb^XVtqYh=vZ}G<+y!QrPaMpJd@|Qws{cEz+pTm>>O+m~lR9sSGan0UZ@cI=EAD=csx~#OcKI!KY+_=5~ zd$MHO4Q5!A2QQ&P!F)VafQ=Dw#X=tP(+D*0qDRp+AN738YS0ta&YQ4llI24SC_vyDJ3o9#?Z1P{FcUG?s zfIbJZOw2Ig0B8m-CZ=kD{yw`od#dW#Kw$naEdX@mMKAKtntHCv@(55dRS0)OD@wS_ z;-p=Szlb*7(^l8TI_@ncl0;1j8f*+aJXrTs!?+B$&ExO6!vC7$%IZ(Kw69-unyY~s z%2GP@{DPuP*H0$ZqvkKjZp~%W!w_rd_dqms#6-Z zomxFPtNe-IP*|*9W=D%TMUPz;Ny6hZyz2eF)zY-HVWH0p2H9&G^K}=;v;q1%j@R9J$5MC*r)3gTS$<-K?m0 zqTE&8JTBQ2D-z2>GFaQi#0yj&(Svzeye_$CKTl)b%M>uu8Zws7TRB2P-< zE%)9}x(W{7ZuVmW8SnAKjm+~{12plbUCbFj4&L1?`v!s!?z-S1PW=N^B{tn(B9N2Q zVaRjTLMf0?tis;iovl7sJxcahXCNjX)G_+V2KyLm?s}LYWy7R*kSa?*=)R~PlmHuD zqwIR4$@Ev{)onsT7?37`0L5u?OPiY_$yqwL)LPK(-OWR1rn2j3@;e!-=jQqDyT|wA zk*Tsfl4_5Fhet57Ai2g~d;GqEfikXw?GY(d%}Y|ZA51}*9w#NY|E-R&HA}3@MQR^%E16ITIdExVPQ--pEHHdNK?eG zoIYv#E6;l+vt8%)xF-Zdm#U0Dr``5!xw`oNNT7h$K2afNNGJZPa@2od`F(g%$;HK* zaL#^m+53l`q8g%?Df0l&PqEP|DuDC|~!Ksg~FCp~!LMolD8%9uidD{^Jw2ub`Xr$VN+o z?+gBfL92wATSh~*!NXTjbLaN>VWfaYznQ2EZ*gbWS?~NwbY+8*srYHR-uoop0>eKN zfV1TeK&}Y7()bs^Oo}|$i9v< zB6-7`HCwveJzsA%q7T8$m2jbese4d}%(VUVptbcHk2d?$%vq<(Z2{!BDa62G<9n(u zFhfXq@wO3E+x2lt!raLFcDR`p^>>LuFc$RJW#W_GJO%mir07(4*N1P(WCh#M!aS|v zT=a*HfX9XYIIn1i^8=h0AH%S{li0!n2w<;grE}`Ha#S>6L11V%NL5QC%quq|mO`c0 zFaK;c#O$zWXWs4hg@?BKA)3{P-{IjA<-=Q@s$^z&gSI2(_9Wfmcj!$py}gq){+cp5 z4p?`T%m&KQ^N$$qYE3@q5CQvCMcV%8F!+1x(GL%+#n-(%H(OA*KM4nfUw*ylmjouA zHLf+U^+)3r{a5o39KGB+-LcbrF-`%KfGv^R0AW2hx>4%)`d=Ch&lFA%IRr46w1KYt zL56bBHF#`;rOZ~iY(ZP%WeCXTQ5Mc-X3+<)efyq}ku2?|bVH_@>SxfvQm`_y*=j{Z zTE_(%Jud!93U(psJUN~auq9S1CD;i;BmeFC@+SqRNI%-kNH}ZZP>WzabXOP@Q??7Z zkk84k4fbV9kMj}!`q^ERUAZTZ^}Az)yxZj!N(x&rw~P^&0q6R`Ox204iE8G1+xBeD z;C!ywipbO>6!z&Tydiq_;QQK{cgHky6F%t)1hDEpg{l zun8}v)VC`U=pif+Z{Cjd`>QGic8fC-45&d?`p=$a;8@*;pnOxv=nYkA|EKf`5J;G8 zD8&_%U0CJyvgbh`O&Y2%5TlcNpl3y0>)ZWiFrx{30&WH|H~X;H=HKhv`VprOz*%fN zo<-^3yPYRu1T!ZE+TOMsYoDhni&{NhwApUY-pL>saP+1pE{JB$9BoBq8Dq(+VZg2@XNTnuyAgU_d>pm7L zpkUch-~)g47~t00lR@07qXjxQ5qz}lG&Bjyr4$a33_~jJg@yQD)GQD9yer8=i z2f8lQ;E%-yP+rQH5OJYFxRJ;pHkhGO1ge5LERbMve8s{Nvh+#I3ZrdXjITWFN>*>m z5ig(X{O(~1@-3BgdBuoK@RtWSB&JpYSRTlZB{FVa=XCy-jaly-WD5R=Qdcvv5;fwk z7xlQ_$~8_Ohzi>WeXAhxyuH0WoAvgV($bOF`K;x$Aktd(FJ$pye%}}g))f8g-%slF zCzjdIe!OeiNEL3?Uv*HOr#-x!f;lx%;DOkNdvt>$BH21+U|#^rNtgpjS7H2=5=PR+ zX`Fy!?5ufyeCq7&8wnfA+*0~qhm3qui)de0xTqbFqOx+G$IbpqQyTIyJ_7X698Iy) z+PEyJc)as?z7EBIXAt>J&+`;DhFl7j_P63vcT#+!I}$En;vm5{D^5;`jkE@{k%@)( zW(P?zUY9E0Jn?i*{v9Z!Mutq`DQuGR`kjU}Z$6&@u?+KBDaOvU|W0Aj(0 zi8T1^+-J-JwwWl5W;}RSY)1pg04^>z&hfS2=Sq;qf4XLj;r|+FS_#t?Dq#Wr*}DdE zRFB)kDL~XI{xp_evpWoboy_QV@9wqtc9s@~a$d+&_c`$g2*j6E(b{BVPACQe>O5bv^@Kb}RB-05`;qrym%dVg={j)q(JsX5wx&GhNO9_k{(T3jZ` z{M-iyS|@q>85}1eV^Zu&UfQ;^HT&MyS`l)5?q_%gYbXRz0?rsXI1d+HAGQ|exI4SE zjgh}s!vAqv5GKnpo=n}((02$ok>*kfVA)~i)IW14l1+N;UFq3M5dm!X9iXXzLYmcd z%%BD_m1O-jkB28;`!?uonJemD$a}$NYU1c=;@E6Hk7~|%&gR;5vA$C78bSwR1HabZ z{V_xheDGT}F0zqlCujXjwc3c{S@HHd@pIHHMrgi>5+J0cKQ}nTb$6Gu?=XZg6Dxh|PcG;0IK2}5%CTVGzqgvd ztAraZV5;z-&+TEFRr#y=|H6ZbrJm5EDjj8RCnBSVI;H~_e#_qVd^IH|rdpetmoA~O zxdqA5o@v0`^?wuEHaZ>1BL~lPmRb(9%N2cnSGO0+>V;P{j$42>oNSs@laQ!TE1R?Y zMOy4jlS%@-3KX7ty)G>+1+dS_RQ587=qSvTlwm_XDpr|4xs=M~z}D5@8E5v8b+Y|8 zsN0eg03OVot=Z0sTQ>zo_6oA~WH|ZXQYdl9)g);l=TZvXx)Q==kgO$ON2l4o6N2Z+ z{of$#`ipA}w`e=Qj|N4`AFkiNJR3Racf7FUGND?E#tREgy=V3Ar!|J>!Hri?Awr9k z%yvM-4P60<7%~aJ&P__nah7HuNgWqTqb!QtX~0a@{5Nz<+xg6wE(9M!44=R=L0}mj z0MLI5AFzBtAPq<;lNj|G=;_BMCplU}0ffx$Xxij&wh%zSp1r6@fw!FXHZ72tv{ZBA z8VILUODPm%*~Ts*;shy~za~_H?0rUH6N7O!IZIT)1ux5He(9wywkMl%m^8>LIUFaK zsJ2v~iiYo0LJGYC0^x#!0-z`~X>>rax!Kt~+WcfP@Ot3oVcZfc1oFdqOn=^w2%)=w z8K}p|;}+!+)_?=x-ATWJ@7{p2I`KZ2ovFLtEt%?PJZ=7`7DaNL@Lk~=Ve;6}0LL5+ zGVBq^w5~N-xBQ3W8D%Njglt7WmfJmf6dyXK-*(>fK0BLdFd!rkuBLg)7Zl&$`-{%X zGISeqEm*3B%?>-_nwm?&BtGWEzRQu1)^C4e$*j?K0_){9caA?3=DybWeO@i3Tb2vB zYx*3J@Q<#tGVtSK&PY#|=WpdlV~Em+2ie`}LgE^@uq^jIr?cXrbgPt>|0G(@AcqAu zawlg?^fAr4G{+=O1W@+5scBE<#jZM0^j&1#PPCaXvFD?9p|SA1i$-2eJ&YT?1K6$VnSq>C)?0*-mEiXg|l`>==zbrMyPH$^l#Z?FaF$7CBDk37C-74`8MW0R+ zu~T|m$Sxv0N?it50wF$BE=i0G2&yG4y?UDmNm#ELcMg|j@3I_!exUPSw-zrti?b$< zfk3JLc~+vvRSyf_$TGB1_6ck3p~Q<=*LXv*_cidK@FT_ZrtAra9NM3LQcCUFx9B8P zQriJ4O+f0ljF7rA^gqg=T}SS;HJAHOKsr$J3bt_${h*lIsMI>qSzmXT%bqRwff0e zVq`2R%2PU&M_0x>-KS8oUw`x($PWkL!1DAMp1c)qWSA_UQbL=|m+MQAzSXEzTDQ{T z3|#@xDheJ;zs5V_<2?rm6kE>ewcUfFKVTzJ42YxG#>d*y_-)H*A^g%6Z7VoiJpO>U z%Zlz9@E%?JG3zINSjC*c27CijjB2^m5C@s!wie;UrF3x6Uq~l4nSPMqpFL-@uOL+T zld~H3BcwWw@})Ynhts8*$3&)xnT}xTd@m0IDO7kLo3#r1==HZ1^^RYPGdX01){w(9rlBh*Bf&ssk`?9z#d4-zqgt&+v#APS)7ZnuLe2c7#)g!tKv7 z4CT{Wn6HwsqVyz_(Gvezf_U>&--eqH-)GvYxexT|zMr@D?yfEOxn4CGLF_2| zbME=RrBaeLbZOOUoni3$PU%sivAF*m;av+ zu&2sgBj|VHb&!Aax=vaOs=`8gUCHt_Al8Nz>9%+b8AoW1B*-2{OIRqB+xV-fWFu6C z>Iw_U2jp^;W7{GLK)CZRV{w@=TYU?2NfX8tlWnc;%L!0%tQ#r1E?fB%-~UyYG8>zQ z`C4Bps3Aa_as{Su487KyY(M&w>|A~Kh9#W8iRmuiA|Z*js#}tw7J>j{b~XPN3kJ#_ z-MJcu7wZ7Vet5SN>CcV-`O(B^`_jXo<+C&WrMDY?>q>kS)O~#$`tlyLh4pC}Uc66# zF&UHJu$odrXFwtCx0b$!;lCL}rTRx!qiH1>gDy{2=9VUPb%rOgaiT#)%tR~jCAO>L zp-0CP&NhU^j4ub0drKI=px2=qud|CTamvnAW)@;g{v@68b1L7RJr8H|k3Zylb|Mp< z_`R}H-RWt49yedAU<-b2^}^}LgA1C{r(CTD>ubyrk2OMy_XXRyL-bvFb?N6j_Rrl} zuqx@lvk!EXb(wgJECP5{ggdlj%a60=1^_LGLqO=071YpJ1PG@2N?p37CSR{0GoX#f z9J(g5*2M^T0z3rjYdUr9w~H6o%Y$4(zhGKc>)*Wj-?+K&yjtU&mNtCNM^1PXUhcG_ z`QKoy*H(OQY^xT9bPwpk{Q!K0^BIawPdwH`TL8t!#d55Wh}av3ceN#Or10J#N~uu)tO~UKS$@)0h>Sp=4^Nv$-QP>%Ca+F-jR8Pw2*&(eSh5r z2y}VMQT_O^rqbzgZ_sGQL>R6wfRne?@-p#_ODU7oM*MiRtNaocW!fWDn)X1i9U~QW z?Nue%TmqI33=r#x$!Bo0n~veelJiU*(b(Gh6B7P99PBcbRxKT6(X{k_`^?}rmdyO$ z9E3e#`Oh1_{l*#P>V@G0RshY)p%>93$6tsdyT&ShubJjj# z+~zcYq)w8}P)xmG`hqGmBDWsbI?Rd@Ei#yFO#d^#2v}Ra_FbI`e*s2zb(8;9cHqqN zF8M!n2Va_WfqalJ8_hbv781Ot-Qz|wHhpJ177LRtW&CR(IfNi;27k)<1U!7|&bDyu zF7O3N@Sm9rjZ%R7Fr9{xZrEpmW$%x0$(5Um5k()J!+|oV9CEjVX>0yWG5(!mjM+cT zr`dSL{6ES+)FRf+qsT-&LiM8T2^S7luN6r6&kMP>rn;iG%D>+PCZGRySCAkg2;%o2uD#;gtiA1n(m1bj=+ z=bF*dNxZ0A9!-Y7`D1Oce9x(>%02F7K}gOcqsWs%d!Q@B+v7c8tf21Lv66X>w>UCU zs-g9Y1@YwSek4}lLC=e^V|-_64@1r<$y+NMhWZg_OPw=Z)RUXguTlnG$Wv z_SBMgfRgHu;~-c5xu`|rZ-Dq4!t^;YU<-Ir$CrC+Cjn~(@vpLV+8eAQd?}r;a?b$HQz@V4n`h7$uS?BntF+*PR)orO#(&G$$#(#fI$HnZrJ8e$+I2G7}p#vYyl(!hgQsRsb zF%xvl+{-MhRF>Is>;E>0RR zLoQ_rs#J8>``(#u(Jpu><;~U=B18r0DIRbpvVSP~l4YA)VK=qzpc*docgP?^3g6d% z)l9;x)L#2>nDP+qJO8wR!&A4WHXPIKi|y(474xXx9C^Zb&B3HDE1cp#o}?ufOn>2f zZFBr9E6jlAQLocQ<{8bo#t_5gva%DPUhndLaaHD}fBf+dbmjOwUT$q~f9K9v2gDP} z$;m(qQ>_Ml-imNC^2}W<`&!!sh8ezIT7rlR9v5L$=<&sTh4%OF-?g;xu^0!W52xM6 ztZ0H$q8P{pk>8m=>^|(gKCkWolg@_2$-_gi7D}mzEftFyKJaj{p%2jDzn~DNr>7&V zfNo9(28IcMVPj`+0JtwCBqV^T7I4@OkS?etQT1_tcP)B6pg#)~N_O7#><>W$&S&In z%{n^XpDGd<9UY>c(;{%xa{&_RSOK3NChLA!4EVun!#>a3Lx=7DP~alV%UUyd(-@45 zRhR4j$x#d{wLysYe$vpm_=&X}xem3Sh7FMb`tA9u84fqXoXlVfNL<7*ic1*?XV*{*vMshMWcDQy+DN*~|{fefh=D}oE6A6;- z#5jopFLx3~-ED#&n9U<&p&o&iu(0f&8iG3@2AV;uil7^wy_nV;DS=c&o}@c_F$S9q z07C=#!M^{lmavc*g9oekI@Z8qW1zMlD*bi!GJ6FyBj-e-&EtpBBD_y{y7T6p3xylj zFcYSEY{{4jxE=ctFmYAAtv7{7`156V{NACLdC62?UTt%$0(kMW$1|Ygbn>w>!f+8# z)qq1lxScISfxg$cpRcw|OiU0H6O;Hnk_n-veg|3}BO)T49UYf}_9LJR(%G5KzbBd3 zqgJ|LdPoKZ1?6t%58T|`+)Sa|jy)$JrN*F=2ioW=4mcc?Af#en?$2Hh^P*2RfmGUY zAK^8456C5EO!E`XR#{6bwBZN58X81KMz|B=fu|A7>xrqZ_O+??`6LKhtc@78Fmru? z%A2G(5AR$Tr=6<`u&>O_pb6+&EkL5<`EZ^Q8~fc32-Td$Z4A>UQTi z%{rjv@$u0eBq=Y?KubF^F@YT_3TPW1?(Vz~GTe&40{{k{dS!WexlYaZf#Eo+4ey6F z0L=k<$pE#o_jZ03uLlb)ZP+y@GxOB=_!kZi^$InGR5pvP-aw!;(~_pEB@UmKme$hB zYG-@fd9jY_FsgiA%BZrCn0~T3>FbVOw;!y?x`?*0SS-oh+!-c3ST;-?*Zgp3s3E)M zoo>N(v#6uaCy%v(z$I&Fk&|SV*o1ES&%KcOFbTJJ_dpkFp zBf|{3HQLX5vavv!<-~rMEnWC`W4q5N<=c&T)0{%U|DiF-S#DYO{s1&4#})mcKN*Z_``sa0!^5}K}SbN zO$`geX3%+@*9K73u+vlPj3-%2)hZfk40mT1EGWY%eKH6$>t1qK2X2I~KL z06@mG($Qg*=`oox0@ob5-v}Xg3#XIK=4l zI)Ex3wjn~??K|f)w_|H9U*-*pFHs-fS#h0_;s2Cx>?oHcT#WcbBjqAD>r|@HMn+g8 zDq-N^d0ec=Z3P}4n!EF6sQ&+!qSWoM$y@fB)`9s=9yA?Ub;;#=yv^?7s;Ak}VG6(n zS`!LvAKesr*AWX=vsbrk|iRh(!c3;|D3-n%#l#U>SEL)PjVPh_F)N~$i@q_H0&(6`<;QLaf z@f4JNv-DlV^j$}qR@hRBifjL;0LLa|HDE`|sUrFE;QE;+);nU<17t%s?m^H{ zP(U`Yx4#ekPXZheAQ~&40_~s^5Fmiq+1cef#hcNqYwmmKOi# ziH+Tl;eCsDVE*lk_E2;z`CiDtDtL5(~t$96({#%{;5#h@e4w)lbXz zT}RH@(cU#F)CIPb-++xb+t*$26XX!Wv&qW`tMG`I`s z1LX@k1LV73?^d}Mn^q$3pO(;=&u>NmK}Zo(gHQq<;$S+LQ*2xuAkSeyg$1_OCikoF zZ;T6ZmzS3%%0&#?jfg{6WFkS5DRAcf!k+cQKF0k zpnRZSI|25M<+_zyW3Q$Kq(_taD6(apTqQ6;j-cQAyO9*b3pXdeTz8VB7BwOf;?-v` z`t1Q#_c?`_#Xr~GZnf3lR}Bm@aHF2?PWBh7RYGWZF8;AtTP=5!vZFEetr&g`vBTRJBlu> z_Kpr#7M6c^>+|NxkThz*^y%~G4fh=+OJkBL!tsvv!m@mFIM9SRkq=WCd3b(lhgmb; zL*^7-E}3d0fqC}r07(Kyq)=QhwEUm8{5+9)d8RQc+F2@E=glywhKJ@rs>+_Ww%c|U z#W*Ms@lWb|NrJ!nX-NN1b#EP2Rn&e96H13jD6NNXB$bv91<6DA0R#c*JRl$lQUcP_ z-Q5jJcXvoPNXLP@c;EZ|zVGkvk2}U?Fa~jUt-beJ>v`sU=3LJ>DliEr31Z`Qu*kiR zKZ&<~350sxAf1Lmp6aSVXnNWelXP)N%$UD-Ul^U>#IBJ)#`RDXv3$akqBTmq>eT6g zTjswd_T$R}w2QGdLOy1(;n&^jQ^J{1(eJX8)NsryRx?kMP@}IRsKioh4aE+)CB4_L zMl6*Sj3(jNPbP<=ZfKhaj88)Xzw#-3C|=`dGYKIeMDd1jU*hT zznMM{VCgh5!G(-Q%Tv8Jn5?M4wrNd&+R^qA@2hsUhFr)fG<$0}oOKLTj8J6up8Lu( zw^XfCC*|O@wB%IGv|#vqd~IhM9E7t{F8!42T6#WK=jOEmzdJXP65x0XhY9|sX6a_Q zQSO2XZTrtvb)>VBPJdaaU?e7zC8Mf!J*!A1*YK4~fog=oi}1?L4{qJFd;W?dVLX2l zQOi$fXZzi!#uX=+H+n$h$C{P8dQ0#h=As@fTCr-~=8&!ebsd$f@kefJ_Wtxi!jwJ1 z0UFXX)M0Vmlm(--;kwMR)dH!;mW;28SCdOq@hANr&V#qZGWU4EKq&OC`6?@i0r~R> zT5V0$6N1pm#2&{syO(e0l^HF^EpZI1q$E<3>$Ft07&5}-d@(YKe?~0un%nXCTE||l zNb#!RC2eCMp#<$rPxQ)3gjO@>6}f&>EMyl_Q~%sraKbW36%k|KLB`o}(hJnTpPpH? zxF&4fnHf*tySWXRR(KNzajpGR;EMO9oq4?&+7{PorUa{Kvfx?O9;YsQ7ww#-66N{B z&2;K;7hOr4m@m~1xue*}Ln_>)&r) z#}(h5+nw6`Yfes+YjjJ_+)0R)wVbdE8#%%8al7sxUJ--NMZYY@{aHq5TwontJBRlW1M=O~O$k-fY~u=h zJ6~<2&c$kw@y_Ke{H~*UaRG&m5QfneuZNy_?HI47cls?bmMwRX7LzXYV6PSP43G4X zSRlErd0jW5MK7c-| z&~4Py(Wc1?OFaEniHUFAATZZgT@@HJOeK>?$b8o{SxuSp1jFQE(?nU~osQUE-kw@2 zs+Y}-?VRaSje1%%RT~tyNm_YB&GZ;wM*!41{9a9s2xvO{$@Kj|R?hds1j@oo&Rp^Q z^7kei2_?#iva8#dlwgz9N1An!m#Z!RfNZ$qXr%*L>}B46m73i0KmtPzFzbcOn;XuOyp3W`S0y0^$7R53xt3!7GvCAgTTqa*OJVQh-7?BAmC#Bmq0t{JIm z(f@|NTFRu?|N6Vrp27htz$-#WjN~QO`+f&1(I+cMK^)xR)bNIC+wS?hv%2w(-&flJCiZTeUwYdPQcUWQ0goKX&+4O0-w`;XTwp ze62PcAXB*cQmS9?ykBoI0zva@GcNl~QBuI|)t>=K-LB5g!DQhZU<9?YvJ#+{^?^Vq zoTQs5boJ{iqXQG;;b$<(#Ff`0yMN@F(HM}3)aCzX2;pAgjg|pOw){bB<}BNwVz*|$ zdh{YG0DkArp|IckZEFjIHXq+*F}jyL$0BrRle@^Dkq7AO-!1``0L36vC_+6;E^KGh zkCt@A=C$If9qKJl4>!M6{-ynchLQS#clj!M&l1Z*-c|hO;d6vG2Ou}X&V!Q@Uf>$g z`HO9#0xE#*g9=^UFP#FpFlvhBKt6NH=V(a*@2Re7Y3B!g0~%$-AC5-&a5NvdxdJaC zt#2bmiud3-|2I&7yTy57qwR2s|BHieSd!vv5a=meN4g##?o;1o$|;hPd^I7NucR^( zG(;K;cx-8tEi*By;4(7VhcV`>MmkcaoYy1qte2-}QBT*y$+-+@uE}Y}hqMHrgr1*1 ze$-C%P$Bh65eA*&;ZY3BTA5yRt>h$CM;#}WSM;nm(e0qxiY-1~qp{!}N1UiK^P#;UyUJTSfTdfpzy2U{!%X?x=JWG#bN*+S9wzq%p{*13^2vX6 z<`R*rw6!{rVcMME=p#q>wughO>C6|RU%ZDnaX9yG!ctMv10njV86UVia2+Dr*`8@>U8FX-kl0&#A*IykvEFSFFOo zo^)b}i;EkZm_UnlwV+e~^wwQcYKzns$am%`Bs;EMc?_sppQByapVW&6|4M+h2q!OD zlo_z|{7S5-tOQbzED(m5m6Zh`sDN|b&Ydudh>7OyFTZR`5l>ejD0v$6`;pVt&5bSa zB%3ZZdQaT_L#=25k+ZSgulyDvp7eg0^UF8AX7#e;E}euN>`tdp4EtgBA09Q%6Jd#o z8y6eJGpYp>)WFsbn2-S-RFJIcYSs@@n25Ev_x*ziBi=muRcCM8BhF_}dA6qvyhB4n zVOo4*jPE|%lUZ2rGw^tRt1D7Vj_4!o>pP*11Z@WOWI(!ex?;?#uwpjw_^2bIvu0W;2-v7HGMibCSsTXJvM7V4Phv|=; zAHW}@L?F*)Mx#$SOXRd&{;{)0 zQ@1crG*%wId+AcDRs4Z)xG8k z;Q%?p(Rmz)rb0^gkQfC?j&}7jk4#KFYLXQDxwSLf)hiE)C^TqkZY!|&-?pl+tSXu6KX}SYA`~Tdlfa92)+)K#PK}kZY9FEBf z;W-xXxv;M@avn}73o9#ax;_N;(AHPu@WAGLr^y(ahQzz|2) zIwoBw-3c-e#dVg0A-+9R^Ec@!u}q1Nh8vBPsfsVW>6z8{@^uL(p9|NU8TtU_px58&GITac&+5a{&u$G(L{Y$Nc9m zy77UJNHnr7$trDYxv(w^+VnmNx&@*;^Dm&Gi=jqW^$wkz%YBEH-)PN&3lHtt6yrdF zxVt9ADQ8HNR%z_l111c}d~|hn-})< z$5Lv)zO+_Pir?*0g!Lk%EBPLxg#}$jRPRlDe=>y*x_9c)7h@+s8f&s%2W5$X0vJrl zs;-u9+51`$0h>ukNB~S$^U0tfFsqwy@oss=jD>>(v`~r)3Uj4K5^8GWptQr!*B3{h zz;->h8?IM0uz=OX^mC_)f!dQg-8V@bu?L1ZBuF_CR&w}p@Xk&p+nbu)!u2{f7#6X6 zk-oODevw|oIk1X@0P##=1dU8~PL4z0!u-6W0VcI?kdao5Q5>*&09sGd#=qG@x~o8* z22fF;@l0@o&avbd6@`X{0k5sA^Eq2w9fB0f`VWr04fg#=habF!JJAD-0&`z;4r>lr z`g$nh5tOldEYBxVA1sB+ShN);EG~3l%4~e$Ad){zj=E~mW4G}d{F?(=K={u9Z5Zh1 z4fuDRYDF0*4IP2G!iu@`^~dr$z~)XJohYI`Ge5PzR)tHM$YYZ(+*)+xP*-2TVqZNr6B>ThjuzylGGa_te4NNp|L} zY*p>%n#KJ|-@txsifBdcB{aT}7*#^*J%WP?-wczAY}-c`O`ibsan!d<4lnlK17&r) zr?quWVlL8TB?(w18}W2;2m5V+R&lm>|IC&YJLMOwqyeN%?q@r|^AVz5X#w<;&fs=C zIh|p1G%jy%*BQ2<6l;o$w}YV>Xu>Ois1)eYf#F|iZmz{hx)i{AEjGMR$$YcCqJU^oOa zu**8)hkJopVipJPMbd(4Txd}#zQY{|?IjLmn~yP$dyOm$C3%3*z=fT7^3 zgM>}{v>ls+F}hbOnB>9C<=mGPSuIL zmPw!!;D{ZHkq_@P}kw=O^O*@lJ&{wm!icVIDdF_Nm1pGp3GWG(L0OG~ zf%385`_O~D!sFW)q5})ZNSFPcy??XJ1XH-h4+1^4%I~tvyf>(9< zmabOYEAyosoHFe@u07W*57*84X@ zF_Qg0k(q@B*m@>S>3bAS>|4lNSZrm=MQseFa6G{P)|Vj6QdLm_dj6^Le3YY@r_vwzwKIgge2_~3;~w|3To<< zA3qFDOiV(|TyPSaZCagXF&;@~j-UK%drM#=vj6!XA(EmgG{?}OA*suTFq+&lX%a6f zNcJTM-#FHs;6g>+{tB$z(0Fzgwy`nlEj=zAx_L(=$Qx{xQ7$ML_GFB}jd3;Myq*+Qx7BU7r6_c;z<+mB&E-- zQlX6((PyP_WoifhwD)*=jFzW+m|@4)7b~;c5Hi!yg%KocNI^dYLVh#I7PD5l`?61W zHG1Y{G&Ql)*^b1sU@~}5E&_O_&TFXC8OJ@_6Ii}LZ~fVe9Yz?D5nBvxt2WpmE4f6< zyUl&=Tvyxn$J_Mw4xUzQ<7{V_rHu#+4pyqD=tUAcA*ymYxUG_S@uUttl-oroWRP{0 zSjjNelUC@qV@wuta~Am&VjPq|v9pUMWd?70JkpYsWZ={a>q07m(lz~z{+FHKoUeMw zFU3dcsqGpS3cdZR=0$69f5c;Xf0v*C-NWz?r;qpw4*Iue`}-27Z-%gu0vT_@i6tdj z3sfe@W6ZmmZC{1>dk=ZL`M=|J=nEM#T_qNM87(L>fUlHsZacOVM4F1);9>RbLd?(z z2HV}7aVj^TawY%9zF{4W8E4k`mpjwG%Vp2LEWBW#P&wTA9?imqtV!i+#OD|*Z~pkI ze&+XG$Jl%k5|V}1&|p~jrRzk5Ejrq-^)i7g7?Ob@J|EH-0yhtj zxw>j z82Z+?E(Jk+X7FsvK!Avu z>%EhqCde0m$Sl07T&d>Q^SxB8RKYoZwy2UOP71UYp)%Rj6fj$o+2B4dM z-oVa@mdV9q)A|Kjva&2)9F|X}E=kV9%%hI#_&kdKJsI$vmS<{+5t2^u_@Y_$%2IhU zdR_(3UfLdwap)A0){&u;^7(P*CRGoEJnA`_ofh zef{vz8*%M~-k*p|sQ{|iFOOHlDFvk^B||dIy{qaMKd_ijoSoT!)O#;NprxrvOHa?P zUD^GC1)WfWSt7~{aW-Yt5m3fT{(^}9^&8yE>T~XPc!~{6wE5b<-~avzWknjr{Wn`1 zU#>zrY6CVFzyo{()C^45pXRgIeQM>Mn-T1`v`Pl%8lmL;#w>Mu;J^+*hahCqi4!Dg z>|1|}K!Cy#flt$%gyWA5r7f$j!N3LxBsAR|;Oh%y)1(2fmIc~aLB8T!Utj9l#(>?9&QOsmX=GvGg=lVlGYx)t`hW+Iw2b}Vm*hn> zG7{1yN*)dpQtPw-ZYIWz3M%?QPJo2;h2ux>|E0T#JMbfg1R6be;74{1h0SO*Vk58; zrs};OQ+#6iPy4E)1)KeDE)p9sTLn8-|IXH;owB%U&fO3j>ii#U`PU5_q$52z9Fg4Q zF*Z7e9sC_QR^T*y0cUPorJ(~OqoGi0TVN~!o}(yfiGkGH6dDX9BqINZH3H7IX`*`C zJx=2|oeP_qnvkb(3{gPmQ{2f?AQ!o-)nw+?Cw}~&FK^v?$E~B4#}1q4K_NW)daZ=g z`_QRTY@%|(kK5ZfcoeR7JL9#=8P5Y=NlPJheo+gc*8^t#MZ_$cF}P2#2!H)T)7H7c z#9cD_e_!^zQ}D4yi7x3(L2R29^4;($3yY3u3zY+f&~?!7MKs+O?V?=fW=*?I$CUvN ze)usculY01q>5APg_O~52eZ(%w%)%E7ctgyPG~|r7HtV~cJ2%`h5RGLVwj!LJ34uY zoXhvX79dsPm6mEnPw-kFS-%2s!2(LxbH2zR*%wdVxZ~k$RlTOYR)v^dEH(irdDt{F zQV2T8w{MK@?~L;*!=u&H3kopZgT641OjLot%G9nqK=@hmq~C8B>da2y1CJpvnNMEd z-AXaB8=8A4~W3&sUsv|bIZagpNuVVDO#TeV1|MsxZDLdIarHWjxkI?XN% z29q3Dpyh&PJtXZ#rLei`ql1MybYYLvVb)EwY(MGgN493&4y83qRVLq#^KEi!@uvpq z=6NPCr4%))o1MzrBO{->xLj;t^G|rc)|#0?_qtmDZ@f+i!N(8RCm_g>3B$p{;Zd9; z_qjV~6l0cHEqAaF?3V3{Ol(*0QeVJ|Wj6>53ZqX~fEMFW@#IbnxSC)6;|&UuJWTBq7K4__qMWUR1z=P7L>X`)Q|x-$tfDUGd)fNeS6$SJ-iL9R zgc#)fowTK;qh+S52=l3A;rqF&zPUcDp1*(E^cpOCm-_oX4-pqGd-F`?mA_sa`5wHt z{O>s_paFPN6-i0aNeesCx2Js^9EwT3!f^q?mQ-~37b)$6E_V$MIiaBg4Fwt>Ww;3Z z1HLvkSVo6do$(7g&oePwI%PdP$GP5NJKAFXh2^)ZJl~eWl3tgYYz~5*eH)zLyCR%y zIXEEhwiC=UGBOZ%mkDMDnXYP{y!cg5QhwXJ-BA9@V0Z<*Au%!W9s5#+%VAIy!yyqd zD5`V9$4-1oy~M^-*(T7kRAse~P7O%6>AxEUTe%~5Cw*xWpfJg1ty}uy+TG1PjO>;1 z(8UtWg*COaGsXpwct&IvJSd zwE|;Q_ds9b8}q@c-6@@#WT@GIW$Ew9N#~Awr-7EffdOlI&)cvh!61uX#EQL#ii(ED zjPB5)iPCea>dTRl-B#Uc&I{d*s))<9`}VXn5`N&egI!$%~Op~Y<<3X2L& z?d#sr69)eYAnw>>OwI)rVUR+pE>Bw724J#Vb{lfH%1mn?h7tWy`UHvfRH6b&=*6E56f zM!UeQt*-`BbJzL8+UPB$O%E3zRt(vguN(gHLmK;+uuB-rmh3-gy%Z+jKyTiHm0{ZLS~Vw&O-EDuPIk!ZH& z5B0Fqtgn((U2OL^Vxrfn*{*lJ*}arJ9NiXqik=_$bBkjKql(_^I$j)flq@naUth%< zu(GlSh@OO;+h@sVx!ra&!$XT_LOzDxb(+hHHf-hvY_MQc5~ zV|(g>4(cOk9QnXvCnZc#41K2MS$#3QwKQv>M}z68XYF5J6oZGGyFGSY#}HV$LNZe& z&l!|&Lhli_eolOs!|yQ>p${NGPQXj;!yPg`XsilUMpEI|uWPMsfteK=mUQ!>6DFqJ z$&y1t(l=(OTWJyjnJ`lddBtR27n_!5INZ~7oOxp3*LRoS=F|112|qu-L(SCqykBR` z`n|TsXI2&%<|`C+xRdOuxp;&h(;TPWt!tfbv(->?-TgJxLRR)mOG^UG!NK9a-YM89 zuIKm|1UDs2^EFB-Q8`NY9#{Vc-@bKwDhKS%5=&Z|1zst8_~VK=s@}1I_vxD&nuL8# zY24~^X?bBtAX?&4;rO}LqYE#N%zheSa4F=$ubP6>&)Hweq>6S9#N;_C#riFG zn+D#Pk5H8G!wJgD9wyVhak+-0La2c{BM}(#a?DK~!BEWy`+hE@bN3L}b!Ed2=s0N+M2p>9TgH z#>wEm`l6tVe#ZrSzdgj-4LU`jd^x;O&CWJ7o6WXAEn4uMqxt@zVFiET7XeN&8v^U~ z>kV|Yj;8|=^1*8P)&~d8;znk0ME>L=6RB86b8#i-3CqE8(N8){bruE>WQ%7%dJQ>s zm|47|do~0FI%j6R{%&+Wu`VtYnwZ6`Kne^D9FW&i;o!idD%Gbwk5pfb)YY*)phtdA zphAkXnlFBwQeH00Qox_|E0A$^);-1#m0%!Ro@K()p$2cZKrnvk=d(Z3sS~met?cqo9Af&JwL4uP`ttWRYXp zJNmD<*>-nyOw?0^;Ek)$2Bkn!piLI5@&q)#&JL*)WGtw%jBEq>CEiPa=ssL77kaS- zT+I&*L>f6mq4#P!R-GGfJu+1$@0^^|#9r*odQ;NW^50|_TzZms8A|OZ{Tf%_u%`MF z0r<(H=7PK#bv;C5(t-oNp8SXet@htFNy*^M(T2F6NT(4|^(y*FRae+t2KrLV|nWZAk?{ymm{K9SU9G`4K4N0ao0?zk;=c!V|ueZSv z06Zmo`}=uZ98w=ENy=bL;mJ$+_gJb;Ktb~FZ0|g|s|R*s|Ibc223{}Ifx{RdBYpPo z)UoK#Cwo>Ez>ktR7=b{nN{4bCcd8~!a+$aJUSD7HFS*ie1Tl~`GDpa6uCz%#8BeXbOYsv9VjIxVDg@gRBTRIO@X|@ zOMJ{Ja!#I(;PJLYMt0$G|NME@E3CG6$FzI}jg0_1- zKqJO7%Xu!4`tngwd?g_f`nHlSmIjD?nztYlX7v|`)Te)lq zH274aHU^W65ImB9k^n-`=0mCfUPIeM?gK1* z39JiqIcGw2nbSjwi4*eA+PB=EZEBc2HyaNmko*}NYo0(>RiIyK=mVnBKjY(mJw5a7 z6`MoUmsguz=s3&OO_xJmU7LUMRD-rQw&Uw!38S4l>!uhAVm7F10aTLpQ&_~Xwd2Oj z>8sa-{c?&*67#B$g1Mv&bzB@dU-}BxuOCG7YFj2VV|083%n7K z(Nquzx7?iTJOo98&IdwvJ8E)rL5T%c{ZUGa$vko%8Q(mnE+=IuCpCWUWsRfN%I=K|Qr@Ck=-!N`N2m zywF=&%DJ5{hu^FhKpUL5jng*OQa4pp0=t)%QaqTWdh`v-X@LsBeYrKF<6CTO?7jWg zFu6iZp^=O;z5BWz+&E`Ahi@Q!)lND+ThRSV11B6X{HU0g=oe@dP9Gh2xv6Yh zsbna<-aFW+HawW`)^Bq3?)!O@vHA1T)uyLyZS6E9gn`Q26J$m>9i^-dX8W^aE%(Wu zmq*nDk)y>_}j1sCyTWK1I9Gq%gYELa_i*J!>rHb-eQ<}@foxBSyg ztbuov^8ndHqkzrr#!x)B!{D#Vhdf!p&E5s!a(Utt9zK-+038fO$>!(dxamj>3N|Pr zBU{gOOSD+FAo5W`Lh5iWDQz>?=r*+21Rw~1ppJX|7_FooxS!6ARd{GlJFIr;XjeP& zZyZN41Z~aKzP9;b_IzVzBtr?PTEYLwLy|!ZJ_+0MgiW#@yoJNz;Qdqqb1X9ACYC0`fgSqeH%$--AS2Jn`N1Lt(b4KBk1Jh#cBW~~8FDaJJFS)EA?YB%)@B_t z$LVmy!mz0ORhMSq(*q`p$AEP;!&dPF!fGQXt@lvDc3ZzboVB4w>s-({(kvFfO(KuCfUIhJ>K3v8o6nSKZ^y^oSYU?Oj+J{W3fZJ@Vfod50|2Zl- zIXNgc1V-Pej4msAnpQ%*FVNA^Apm+=pR?t6w`PHQa6dk+I&Ag9e#RlfZ!=FAE_$%M zT)I|N;|0#|-Qopq;cT!_=^Gp5GyWeC*!i$jGRAUGr@Uz zMJ%bYU_zQbUyps+)~1^C?q#~Zutyj0TiU1gm4x8?qv$EC`Z5v-UdI!w{ zL$TdM9WLtZ;9ynuC5#we|54TJZ5uiE zGk2>bC6=c_i<-5c@7n!k!YOy}r%gss-@mFmrNKb-zV@c$0Z1z4_YaJ^h?INJCX=on zpkdIf`@6Fv;I%gmh8yvWfsFyq{>GV#4EKw>?d3LrzubDA7lJCFlJFr_KlHiTnQaxZ zXI){`qSte_O1Pw4YiV+kg6NOmBQ`~A80g@)+d0DR?Z3_%VK28(5Lk#_)8{tda2#dl z)ky|Y_A$fhZP+^};fRrvR3__fXe*d(9-!a<|Cp}X{9bo$JOOgTXbdSoa)PhsN9_Ls csDJKJw{d8`G7|(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 index 0000000..de7d79f --- /dev/null +++ b/doc/fig-rs-processing.dia @@ -0,0 +1,4426 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Main +Loc-RIB# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“In” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“Out” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Export Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Import Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-rs-processing.png b/doc/fig-rs-processing.png new file mode 100644 index 0000000000000000000000000000000000000000..c0cac5c9aefae6e2a10f233d097e74d0ea3801cb GIT binary patch literal 59640 zcmc$`1z1&GyEeM8Kn0`(X;~m3DJ>u&At0U7AT6nMgCZp*Al)5GNl7b6cOxkx-7Wn; z%kSI&xA(sOz0W!SIsbL$b$Ri=YtA+1m}8FdjQf7>`&oesauQfKNNyky2rMZ{F(m}z zvJw3Az_I0lF*w#WEoZ9}~X3Ced&# z5tr@G5`Kk-YeXE4X0mWJXF7<{_3(1(u1kaa)aX_M$714)eTe+xX%8=xugGHrp>MMR zak}QI4)kd$*jMkp{(a03xcp6esNUauD_)G5FICbpGlL`hB`WH^-rVL@ROg@LG9e-f zu;4<4Cr?g?;iJ3AiadVA7DkKAyL>ESs}WKJ;%nkEFO!L}aS0!<{nC3+t^Pbxk+cW& z^kx$kR_t+IX5`LC^D!DsBN6wcgGZ1{;r(y!BM{z4rEFqZ3EWmYJ3C6}cL)fK0@Ns8 ztd0~23JN+nIMg_8%1KJnGcsz`I2PcJTUlH8baqmw>(n@A<>X9^-T5uOaqmGbIs&1u zd99tTzrWw-8rFbp)X@no7MdzrE`Go4*s2?+`P-oD*BI?a-f!RrsF zmS_nkZd%+GNA{P!7-Z3{9ZNfv7dj)Lpy*lLZ8mu2=_!b71yB6d8x5YGkFUD6R=acL zhvmUif5tsLDF+8G+$4S{>*KxU(9lqKK_MolPhY;Mmlyjv)zk*{*xK18B_;}??~BL!`V>yk1H~G(KNS?n%E)M!7&H%md=?oQ8Ac&MLP~03W7FWY zISu#LsdwjPVp==iU+s7bClr?baW&pCVHG4RMpf-h>M?XHQkb7>)v#%59-01s`oe< zD=~ca>Xo&%HHT@>>8~KxjhTj-BvxGL?N`h@MkIU>AF3bm(a`+fTOJgniF#UET1rh# z9k<#mbWxjUG1!OeEHJ3FCs)%Ozg^TbyikZW+n|6mul6YrqNNSwXqU9`D7Kf#(CR} z^z_(K7=n$Bjqvbr86}$ZG*N#%MHiPUzv73sza9w`BjvJGK#byk)>9{;L)8;l# zvoCv2{)`z^VZxFL#pL=t=JGlyDClOwnf^#kMTHtmjGL=#KH=8hUhT`-H#-t=7`q^U z*;e)R^yCZSDN0KR`B|@xmv@eiKBFhNcIC>0xbn}RKNl2O?Jjg{Rahz#Mz$Mu74jW! z&WvtZFV-x5%FO)zt2wAMn)zcvfpOuZ-7XFd4UK1o+AeD)t(^9^F@KBM?G_w3BJ-o$ zv3YppZ)3q5LiM^|?sF>zjlAhk!+$i0EK z9BojSS5$2LD5Zm$_@@aK2Eqi7F-%+8Vq=~8ZRjIOuvY4|J#+H{#rNtHNd0qjtEw6U zNJ}px5bEfW>qKV<6E>~iQ3nkzEjKrJp=SBhyEvKI*(B9d!NI{>e-Ak8d3LP&>{$x0 zonOGf;2`Bup~=PhVZ+CdA350B`5q1ZS@~E*OG5+wGC2LSeXZ(VknxgYOD|1?nlCc3 z_2%q0^3Q4G!a^OMRD`5a>H>`8r-=3*zht=n@#DwJ%7c?tc0?@N56${gAxs?|9T8Vg zef|11A|e9q@-^s9OG^uj#^>K1QH&i?obfZmOgudBntS58J~r+1+AVAyxtsWr@NrDi z&(Yys#}ydOxrN)kKIk9dcu^9L4|g*Q^j%hbAn34Myh~4QH>t-X_mX#JoW$kCk&^qw zpuy9f@S|#)Gfc}TEEknIrWj#|ZDBQ0QMkD0j_~bROC#J=4uMei4Rf=*>BN`NQks!S ze!|r_L64=dd#rFp>b+p5ct6}|Cv`rP4<&BYK?OIOi+6TTY>%FKoLswj<;lY_V%BS- zx3yjJtl1~(MJVCA zPd@ExqL=SrmzHJevZUB9hOX>0#03X9a&bOb)hRRjT}Oq6Ky=+`M5-rft*`6x@Zk3g z#I5N*BV|07U^s&%;9XE4H;iPzgb?0*TWm%$ZiGF2U17D8J}hziY%bbCu-m0l3`s}Q zIX5rlo=jutsX9y)5|ScGW5dh4tjR(3_lrKJgx{`k(uo&o5LG9&5Qw?Kw2dz?Nq4EJ z6Tn@Mk}Tux6xZR|VuCfYM{!5v3dg`MZjFtmW?^OJO?4)RR$+;qqaHH3EE%=2A%cQt z#r}@n>--JAicesh#(MA>qh-W+Yo*;Ronaw3I`80Mg}b%HxForxGDnE;o0*j8;e!qg z1Y!j1gejiY!o#FQv~0lLVoLB^mOtlOX)YCs=AaNQ4Ar&lv~NaCoSM%KNDv6u+i%4m zFnkPD4hV)tC4HCajiQFeTM;8zVjumlvLFO}=i%MyH{%eZ?OhIs!2SuZ5$P?6dS36S z$Ld%5gv6kC#UvO(2){RNaCW-xOmWlH$5ba&i8=i$fS>+_eyVa3~_m`CZ5!fp~xp zW7etPb&=0zqIJg@y2#-lF23s<-aLIR1J%4AjFCRe-%&8Zc#iwE_AaWM8qm!L5`XVc z3-|9LLft%;3?8Fukc>EFk{CwU3QjQjRJR3C!P7jTe=ClV{`)jfPEex>-@-?s0<5R6<-K;3bgis_R2?X@{91mf5i8t_FmAgl)sd?G|O zu!p*N6siI5WQb-P(_49~I~^6$><;?-<1Nzpu(=QGj__w6AZ( z#>f~n+z$Io@FeN5_1KRefz7`vr(LBPqVl8f)6?G(@~rju_ZPmqG}CbLNuxBBoIks2 ze6!&~kc9;>QzvW$!@~|czuO^tbZNbK@xlWj$Z56zyLazK3UxlFd9NhdJ|v^0gc!D< zUE?UrTA;-^&tsCdpKvP}^)R$;}bPq4dWe)B&p<{RGF7IHUy2@aOx&S6crWK)J8~cYSXIj*yXUttwD%e9WA=psM+jE z5n?5{u|I&$m=Qmle|m=1|5i&Q?lW^RJKhr*Q||!2x6MmSOOJi7y`X1~dRnDj__{_6 z7Z+ECXQ3;O!}fREEx|v+#Dp9i9R66OYU=83voB*AVZXDmvQm9E>@?#M^WnqZ-k|*E zkJ8qbg>T*UI~|ORJxLZ87OM&m0i6m8>RudXB=5)t=Gx5Ev(|1kE)V9=$t9X~394VA zy-z|<&A_(L5ndAWTO73(yd_+|UxFqHHNma_6_KWJhn!qTSGUl3QJrZdS22SIPfAu6 z%Py;;Vx+fMK8}Fc(a~|mTvA?s(Y6ZEMMq}a9YOa$ZQ<1X1RIaAJT@k)ppX1~d^<;{ z#l^+He*MCB($v;|u(2HQPi?Obq=TT9^|T7YE>HY#UqnU&GaoOXcO|MGfc2 zyS*nz zN4E(G9vC`2fBxK{#2N4nJcJBdpx9@Z-G!wkGvo~{to^;c$#RQP0Psidg1B6qoPaOX z)YM#^opV?5@zY31NPrN9lJQ0)B^8|Vw6%ngGBPqME1?c|cXwKu*e!tEp_D@Povtfz ztgc?ZbgBAq7_h*9fq{Do-apNPIg#-s1ep%YkDT9eZMx140s=$^WOzWg$%znyU2do~ zfI|Z4z13?xPnSECwAm28hDD#V@$dj!ig8*0`Ex7v?SBTwydMEILxW?Hbn*<0v$j-6+=a&nT(Vx;zHA%Q*aQ(D?% zje-7t#hA^sjFFL%xz;dN%`(Zj=`qWLp*&?fTiXxDS1zGN5)fa$h6&NePp;sf7&lE} zvf9C7Fk3c~R`&RKwFrorysLGT>$*b9bz)|Ah7?IZT^3&6A456_??MMMjkVgZ&CSie z-JTD$bhNY>V>*F;sNMwjr~74Vu?Xvb!s-0duU*Hn(b0rh9tKDiRaI3JlZ9;Agf~7u z5E?#e-hF^N-UsA={Ag%k5Y4JbK}qSgzcLKe&6wC%gbFVZ_!ST>E)O*G$p){6f8lT4 z5A8GgZ6&3NDA4_qmpU^jK#m3%YWi5hvvbeC|n9+prOPy&$A=fAO&=5?a z)(N&8kBigOMfMHE#eIPhP@H52E5JjOa9gTBecAz$?&_t-ixDg=ECBTR?H0P`J7Xxl zPG1dQr4MdvYishqH99o3bUc^xtLx^*Z#NG!6GrZuW;7KJ20-7<$*PRR)Ad@beIR-=VnEk` z1#4g3wKg};S78VV3w!1m`)Xs7cQ}h7Y1_FrARqvrPVUN;E10ga+yW4_szce;R8^GfSMu3?-c>U7f- z4{k^|oE`7Ob7-^hu(R9#{Q4R$LqkKu3;Ye?BgK8EJ(!r|8s?q8`BqU;Q65;}5b;t| zQ(32|00evnue3LjS5$0yc{yzD0P+P{O3U!( zfvLJYnuexpm>hq7540F9yif2B_CHfo)5?m9`h#(cuuo*1H|rc$$D8$5lG10}`HrEXq3UWr9-d0N6nCXgjb&mElkcsqMY?s>KqxvprKg?< z!8mr^baFa`Fh{VntsW$NKVpcMA(o!?|zu9!c+s+&=k- z<3{$wRLQI)Q+^((;)So(Z8MU?E`hJOjaTj_@A!4VL&Dm`=v7U^1ZR{-Ku>u^PyVM* z;q7>2WYZ(R(9{;lh)&o0{QO?bV_!6^D-5_Dh&P&>{kdF9LgKZI=)&<+|C$>c`$uKL zjFyX}`z3#kFE+NA%BQ)~%q__&&*<%uoMYa1deXS05OXDaCL zBwn}$C%VkSw@vdt1YFA(k|J}JvoqqfHANtVS#b!zja9qr_rDBRl(Vm?^U(4*yWrv+ zD^U;hB9p!1mqEqu$USl5;p2D>0JP-da38`iV|9lJOE*71J)dEiG40Vb16|HtmdeE_ zA8yW1u^ajohbJl1uFYEl0HQX&nwt&wnGjgnGzf)NhcXhG)4zy{Z5vx3?rU0Gn|w&N z@H9l`z|E~jj3w;nZmEur&c&BPG&RxH818Ufmy(z!b2CJ#Z@BmaWr4&fT_j$fb<$v_ zLamPSt``Gl<-5q=hy6H-#v@(MyuF4^^!+*jQ= zWHk~e4$QH^*ANfnF@pNEUIw^ZSy^NbFcuVKx~Z#VVTMYE#4lH0sutBXTDVt$SAj%F z+>4 zk)v(%S?lW|w!A@RRi&k4BNcSyqAxiwF@^?WE?kpuk$qsM%h~kXstmsC`nDLwsm2D} z(yNEYU6;c~9hsu({5j%!z1qJ$R|z}R5PQi32WERl*2v_rXb-h&m6obiMNu`sOL4SRgO-97=td z>QhPW-l4p$_1ei7d2eUQog0zUnCiTubmCd>f5f~edMpiNLU`?4$wWM(wNyu;OR(F6 zgwxgN@^=RZsZJGR<2O5Z1Z&@yxc{y}U2#^wdKSKXf#-bGk;yDOZ!EH%daUQgw$90k z-FjJ6VL=?x4FrtOmXVRefv60gJ%ry^Pc@0J@O|ev4c5lE7 zMy5A+ciw%NKZi>(FLYv)sZgdeQ&&N~x^mEcd6BMMMHMH=fn~iRs(DPiaL=>cPXFSY zl+Bc&{m-Xz*>yHvGp5N-#A(CRPoC&oyKLuNcy!x|SkAPmD?5E3zw;cMM3@dHAKC|( z{#a&`0%p;#8#DRPP*Mg5?hoE7cx|WkjHW+NX_7yp;|i3RXB7At`tKBGG-^xuwoi;R z$Wzq6FF~&PoK+Vlb0_}(ZG;@KF)|_yzA%3LZt+ihS5!-av1xSA;!BrV{)U0A$0;vL z*F8@6#_%CHSSmXqgc)08Y@(A1RByBL7My0xOC)!Yb)WV>quf(h<+(3O6?xxiz590M zMl!?a!vkt$;m4ZQNJEwjMo~>2ak2FfEY+DLg*KyZC+lgUL!a7$C@UaQcVSOs;%@C} z&pMK;3fvUAich>R`E%VZXDfB572ePFGRfE08Lj5Zm|wd}hDf_jdr-6HXQMzWhSoNuI;KU(OWNm+E-SF*`5B z^XJ>|$k1+GRjlDU}7!bB)V&3QKRMFb-29P zd9a~BU8h>SdCctYJ(p-{{2#M!Od3l9j0N2($O@C_Z z{xA#z>9fuuJjpO=LdHKqce!FT2 zDoY{3lw`6|M3fhlhhUXSubMk$2)u*szVfmcaMwnGe{!sKjlv$f3?_SR#xf zBqW4f$nzAa9Vsd4vrp={goFpXy+S8PFZOe-U%beJ^FBUJKT1uT8XNtv$>SpNgf{j?O(h8_}|X z921IGhavoYeIfS+qJgog>BkdEW#vdQWL{Cx#hRo z@?t=UZrr#5#A~9#O9=OcXG}n*(hJCy7#U?&KM^`!qOQ^ww3&W{blq+y0u8MW{$0IE zxOYmb0Q;@C#NbH)b#~@{;i5}F*F*O|7uko~l&P+YBLgC~;*?%&CF)jK`?tl>d9^)z zUB}&sk?d+w8`MbtdI<|VW7QX)HrPS=uIH+iWo3|m0EW&$6!ho#7!q^`zuO~81hdGn zjY`x(O9O$WprD{Nj8d=63^(#>AaHW|Bt9-WI{yI9hR))WlE8p~zP_cNBz|;kvO6Rs zPsJcT20RR=n-~Zq$^*0y1ycl^M;oQ%JKvhVU8=LG)4sKf3^JVC`xAgoFwxKq6{jd}Q+_&tFqEe9jj<67ckOs~vZvgTMvRNV*1-A6+=r+4EE zMCI#O#>X|Qv^SQQ^IZD}2X`B<%9gMHJ3Z;)!-qJE#>U24S`!Q136+*V(wQJU$LctM zELb$F6!3LJfy*!4<=-rKY9TsqL~5X9_Y@p?1~Im|@tmWA6&2PlXTjZidzn z>beTL6|aP?n774jx70PTmkx5wJR{4;}pYCJ*yP1V3xF+~c!DM*N%-$Ez z!8yJdMHW)ZxbsDNjENEJ$!U=!zCQltd+gIS=f2w0D?cfa_T=|>FhADq?$@oRxM8`9 z%X^ePa+O&GG1ij1=HC%%KlzY5Y;QEM3TW^GACk!d=W6u5$saR%TF4C|ZJ^m-)v7?b zt{&)GSy|b}@DJifBw|Uz-_THHH+@GuCa*u$C7d_;Z2;`ksWw8W4gMza$HIRH(By2^ zCjZR~Ae^)uG3_)q;^y0juhT-5Me(F1f8b2)_re9|ZbP|KeIditXhKu^wQA4dM-;3u z+N`WLkXY!F*$W}%0f|x1^T;@9e16**GMtcvRB!O~Sk$rMY&emGDFq1@U~^o}1EpkSEG;dy%gv*N?<#DJ7S#Q3YiQL671Cp-xCl(+Z(OpZ z8bT=o>PP+oJM(Us`vlZM>SUoIU+ZUr7bOeLm`>IK0Yu#Mx>#t=%OMgxJZZg9Y8ww+ z(%gt{*%_D-YCq~-W!0W6es^SM(OqY^nMv4yqxE?&t6Hy@G}8?smg#2jk>M;jAn`=j z#LijlyTL^Dv22!5bgkJYjEFsh&DFBWOLjp1uU@?>D=Q20LFdcM+0@5Aa!Gty^Yy&> zmo8tHe)<%W4)5dRL65I7YL8%iRwyPe{$jV#1T;{Qw3j2FG{_5k<2d|#7dbeFTwlY^ z@t{sff{X)mtYgaN#_dd;KY#Fo_$Z~2o@^!Oqk4QYqBDZW&F`B#*Uye;9u||LM|wYY z>8=*eT+d9?p4Rl3V?^5Cbk=m~AHIlt_mOh+1Le-mV?7v^yLDwqoltg1XGu0b)S;O$ zuImPSwXU1&R1LT%#!F4*d8R!!>kg;gHU%6gPa&aMQ_F%E7#|-$m=#fj| zhn80IjZ0XyCOUl%*vIGHhU*0mwSt}^qe4W*6Z}|&53!@md4@j_E!Bjn<&x^T6`}Uf z2N))CK`Wx~;>&9Z%~GR?DP|SVOD|excOIUi8#0fRNj3hg{=&xk@R8uGK3?7N-iYYx zgIKTQ8@`QVQ)~p?gGmDF;}P;=pSjzc(-&2XD$6IS)AI*BtkHVSHKGpJf=2erEjSv2 zgL4^y8ujH^{X!4YOs2CkMB<`u7`uUkTT-RXFJU?#79UE}KiKg&$*goxw%2g)h|6ZH z?j%r8X6?B^$2i3&w#vC3fjW^2WpjGE9S$YE(-|Tj`f&BCkGL|-^Zwe!*p}BP9^2MEksR`>i52srdF#Bhc{+oV#7ijzy;^32 z5|V&YvIpw;^LyPVMig4@cAYIBzw`Mr_>c{jBDO#45|4x0GTo+hN1;O<6)x(tpr%)3 zcuRLFXzrgBoE(}Q?R|Jgpqhmq(AI79B*v!ZShncCkc#R-RqtGo22-e@W*O$ScMHP6 z3RAN-u8Nr)Cb9@6Y)$x!mA!VgPu!+?`w4slu(`#MzW!sAM zL@(CYnt4UgztRVYN_59q=tg+ye%n!BA_MmIzf>rh2Xef$`^{#uh^69;#+Sr%C3@15wH+mFJ8cGFvx6)EHe>@%;FwoLZ)yKJpq zoTOyBsLpsz&8JOtWGy+$u_WWNHEeb)-+PXm*W9+{OFHBuTUGk`vw9L4pM6_fo#MJ5 zNLlJko~s3wnM!%)abOSHo@;e;cE-5!=KSoWH=b)1JWcT*&(2Ra=YIVH@&f5qZ5C|}!k%7srlAoR zbu%-11yTnf!467b#=+B+$n*lO%HuzOhDJvh7Z-D~vZQG6<{NMTvI}~hpT3%F0ZUV$ zSURY)Aj^_Ja#m=%{|?Y6pfexzTe`K*B@GQ;kbDIFN?UvK^=0&IIS;ex?^9DAZ8&FS z;TkF`AEKi6TS#rp=rcT8U3ra_IOXi%-FD$*aD}jvxcuqsQ$-UUWoy>*$DZV~7e1Pc z-1K;0w5`=JZ*pO}{fH11ImjNczIL(bOTV~3b><~hx9_v1 z7xfFgR26msY^nms=@v?BwyF!Q@dANP_*E%sX|2L*mU)K>Dx?EfW=@V>)nsBK znekmiOiDrb-)-UKH=L6F1f4b}!Hq{wPJZO>sjiNq9RB>wEG!ft*4pJH@YtA4Yt}k% z^KaHFKYP~Nm2>jP{#8V*9QkVZCpSLz1{J!P8i5AOfY9a2@$3m6mQQ-;>z%y>7o)!~ zYR?040Vl2i?EK&(DwI;mf;VLS$hn?8F&)tf<%z>m6bOrN-yd zQqx|bm009_oOE;&^aKmPe*-@AI9adkc<@Y1>jWf3o3Y~Okj8L3+>m|xl;q*dpFrMX zSoK&U+kp@CCh<#%k^j=~ z=&2`8C9X|>aPZN-{-Nk#19_%nl!}T9f(tD&PbEGniJX|YUE2#ZsI${kAg-~P1cZdN zgh8VPS}{pU-30F?Y2a-EWuZ+6Wf2So;D^!@cBUq{fvy{Fdw2vf?PgcV%Q2^N{EyYGMVrK z$1E!v;sXYoLLb|hV9Qg+D_D3~bx!e5OFy$s(g~FOIY+nUvXzqeIeKA0U;=bCa~o?s z0dAGkfz>YV*ZI{z8uc1})V!NEomNL-i(9|Af$JGtJ954((kSPsbm zdau2MgL9Y%y~zS@kQ1?KIO7%)65`=;>W=5KsXxk=p|vnGQ;4>=w;!5!Tgojj-~Y2c z7evTx01cM>Sj$t+S@?bGlI|mXS3d%22=-tQ_?DzH0CnxykG)Rv$?y=mu;4#mCf~JB z@qXX;493G1F$#P2x_8`kD|5&)~F9cAaFkf0BV(+U~vJCFfrO z)ynb#OaH439mOWOR7s2iJpmKp>WMtMe^G+hG&%QPj27y^5Q78`3m5#`H?Xmj`JD>Q zUxP34>sMi9K3GTs12yX1UEJMILB5808D$K*h9r&c%a`=b%)@(Eb6-K$55(?nn9M&u zi@PX7#=wYuK1?J{VW0Z12rivRNA5G7Ix5Rz5!UQ#FyE4ZzT4UGr>x}ENef+KhS&}s z{k__=sgquwjMBrgz{G$yp^YrqbztU70q|Ng13^xw#5;;A5Tr^((zq5S0vQ z|MVY)^*1a%T(+-X#epLwj>D8iul^J=P#;-@pNlB|H1cXwGGEB(8sxl%%Bk zmzU2gZNz-6oiNMnGxJ_}85#zvXx@u#514$INVm3@Jw73ikFnNe(~s$QtK4)@VhLmf z;5oE57Ij+M&iCjd7yDzH=J5Z6j9{*AnU}w$oqgWn+mjPzmbx*7Ps=oZL;RL`#T;Jv zr>ej`0>ZS)=R4`Rnr!y?`sDUEgZ|6(fvCM4SX*;)00=SC(V1@lY8JC!ALlChlZGz| zvB(s;v$chD!*_OemfT!YR`v`GC=fkVopPy=CjKN?tD!EBbj8HJ2jbI>6zR=`OuhM{ zUAUmw0?$jaTqtv(q&j@a%q;f1%|yt=)Ko%+D)Ou*Z+S*mc5{4;Y;9unZ1(Ct?d#bl z3WJGpmnU-d^llC-Tws5 z`A!5`?5(7*6E_IhV7!0>4<8?u|18kFhl8P{tc(R3y6eFYQ-DX%xs>v&Cn`0VqQS#o=j!nE={r9XLS{`)^P$|XzX$A< zfHHsn{MjpbV%hQGetZHpCMMJU`x;fY#^&bcIyy-S2@{d>f_d|w_P~|_v*}rpu8y+u z-2A-5?t-j7vXhwXPf9ZGV&gwQYev?$E6YRA9XOYY6Q^s^HbB}P3W zR^F*9VgJ)i-aE_oBQrKuR)*G75%?an%fuaXFLrSqV)3=Bw<|9L+cG?#&1adCl#`=P zf8};lc7$$@kC^8*W`%QeVnY{;P2%zc&wFUGR7nzGv6T+X2=-4%m=`viH@;>YP|s5v_Yp4vaP|D$xjsq6vZEVAsibOf*!j1~j3<^Ctdg zf~=S-L(bYA^ntW`{Rdq*vBNYA*GvZx>Nr3lDPC)D)VH)yW+_pZ`Fj76WHmm`J)S$r zqLvws)D5~mlf5BM^qv5X=_U~1Ff&1~dx{!vCr-@Fu6j5j_PHO|r6qC~nz4U`2b`Zz z-x9Om2z&i=HnAwmCT>wbmg){4H&ry6aPRXJ@m=FC4?@*NcemJg6ji+ji-a}b?L~7u z2m{Uj;kouIDHj;4ENW^3*OO=TQvplMV?lFVnaV?rx7d89-zCPw2ed+K#3Gb^J;3Zs z_~ftCpgB2n_)uFLu4k<*m#w!mEibhbd)9hJzB+Lj{v3BNtLe`#YOF=~5R9t`eKl|= z1n;@$j|O(1=iAK0Y)mtUKb>P>B>%An{__f^k)(qI()D$*p_!49t5W@6%6fzHIl%3r z5B6d(j*zJh&EdaJChGxPJB79R_{^B|6Nb8>aRzGJ#h2OFuMDoQ=U`l^U=r9UysX(z z%Zj{z2@y*Nub54giR?LUGsYFRORuLb{5FxS~g!pMYI@YD=jlj3fdn_Yw_JtNp3o>I>Vo%XCfwaEIfS_8yvLH?=oHT zwst+&M<(lCK@@|_S$~A!29&gbkp6M!kjFt!_!b*XA|`BT?2$<2Xpxz+ATMtfzXy0v z48akx)K>7RYe?q-(Q!!N)CoAmV+sEymxC`%#Lh%|ldWVo$MuK@6QO?}#_$sTZaSM5 zr>53I3(nuRiC|7$qh$rrdydn{y4Vxy(fD8-g8D5u5^?X+f}Mg7vO;wv21qDl0Lylt zG!^PYZV>!ki9YFp^*;$7V~;Q(5KQh^aOue3K3Bs7e~q90CI$|0DoFdSzKwvb%`7-+g!pj;Q77`4^^zMzJQ59_@V6K;iyZ$K7>n|Fj1G@!G}wm zzxu$v^xaVw1_bIEWUj)!KK zMd^0@QQXbsh=3u89Z8`M`uFvG@6P*13bZ`j-NB@~ySqC$FhGOX?uSJhTUqg6r6V3N zFg(?<`tK7GN%@)ssD#9OD%UDs=q;Gad8VC3?bsINz?lc4Nd4(fr$(uXxP%0XNIjyE zXPqi0>BuxoTmZ9p9Mu!iFxFqF*I$g`<@Z00;tYR{;%WR)e)-Mb5NIZlrqiE3w>~^a zNuj_1eSLi)p;`6W^2g_q5j6)@kHaY^NSi?7cr@VW>(;@u#)xRm`s*M3gw!EyNu6_p(uW@lV zhDGP8m_#TUSo1JbdWizXAk98gv%~{H*pT`=h6%_~jqk4KZj#gj?hbldw z#;|$<%#L>Ei|P*I73i6OJ~A^4cpR43FGi&SYe0voWc3QR~ysHt%?GNOqj2sB&jOA}Lpat11zGZna{yoPA>Fw@PDG&Vl*+G?JKmpZp^5_S&V%vgvn9&CsaT*nh3?_7Zwymb zI}&LMxyQI#T3X$QSy;5zC}8cs@BQNMroq+!T-llyLrp~`R~K06%U7@Z4gH@4-}z=V zA{9xCMas=|@18LfwOo3gY9~cR6a(3>B&g!qnePk}I{&hAiQtKm(Qn`%7Ut&m9+rwU zc&{ENzJ2@F_a>3!$&>2YPS%Fs+s;WuGu{UxHdECHtx8UW;ojYj`X%2hDjsWZ3?3Xb z4YB$U-26Lof_!hqDZ+@z$P!~>DnUm!p*{YG1srTxpQ&7F|GL0UyW^u^jX|VBRy(f8 zt23!F?}7;hKOB-$P~LRw)~14}x%moYop32Bi>e*4aoMyM&DxBBn<9}YCIU4+n=RIl zwOJ}jGlI1$t(lpav}>JT#kAkCXa9%$INiiIU6VyH8Q2)+wkxlkVx0p zQ!k+iS##NOa%RG&fgB%oM8?LBjg5UVA7*$h3^J^FYLCFlS}807xRbo3leD#5?{k{o+zt15oOLgT)*6Z(ab@NJ6`Oe8*EzhGcX`7;;_J zuSfoZ8Bg|P&P5q%{=RdUloY(`6js*Bu8Tcs^&UEVF+{j1EKlW;ti8? ze0)3<%45N<&_$vvs{j9+k6i5g;_lScltl2I@R*pxwbEYAvz2II2J}4PY?{${pMQAa z-^e&RUE+7@7@xRMR4yIvE?68!0+tiD(I30qok|B5Gu8Toc!$TL?a33g=QTU~Z!Gv- zm4=n$GNOutV(d%0jr@bNc%Lo1j!^D){c_~t84efE>Ln%BE6o3eb2e0@rx)TA)ODR@ z<=hb!u7x+#CfbnR)5LZkUgd*oyJ8-wtvfmS=w$Y?TiNSP)V>$6ynn<_CUJNqpv^QvIUrWZZOE4QGWI}YG1hjFUAyF3dWk}k^5&;AVx zi`HQ^BKMH&>3W)4fo8=U5>@}`Gx(P4+8Rhn@7ePD(Y4~=VhDr9K>!fLk2yGA)?1##5*T|_U16wgzM-<5jelO|_b$(uYQEJ? zeKWWcU0e5GsV@ZVo*DyBi$7>1bJ{bZ z6;=-L908O7Q;qmCKt~~9LJV_>Xiz+fIDS)%@x&K35c(R`E=y3U3B{Aw08_(}3N!-s zHy0`_QGd+=Rl^@|&3`f!T<|H!RYSEUqK@ESSN7pvV~6{F0G2UH>_V&g180y0W%>8V z*Zz8mp5O+m<`hi`#i9_`fI70@;i#hmeukibZiLHpjhzf`unqqSrx8N{$x(mH0r|rp zVP;?ua45iZK>htcd-^k*13Ka-?!Tx5Axt<-ARck-DBd8X1P^ZJ4eTUTCGuBt!nGH_ zcvFNXM0BCY4$$8&?7zR{5fytJ*A`F<~|+5WKlY(n&z^TBUo;S5^EHOB9ohUwNo zDQ}|)lykjL+qWpNI%b?isT|ylIupsk?wdf*4U5dM;Z&>a3kWfvHIo?s) zPQX6jODWwu7{9oDU{iLHve#(qQvv9567#p4kiQ(6p9-lak|Hoz=T z

5o=$m|6`E9UBsLUa>EKpEG)L$rb_*K~kMhS2A{Cze@}vXkr)?xH56W8?O)Z;5O5(hd^geGgUE zK9+F9W^k-eF#PuqH`hb9&WkOJ^?IjbrM3Y-79G8)CZbN3P=+2vPCJ{J<(r^4#&qJ)(au5O<= zuqjM^jR=b9FyI3;jhWnpdL?R9XR^L#Pa>}U=30GI{v=&uT5~a}j{iez{S4e14StxQ z&qILBtag7H$Ar=r)`S+UiBxpWLZ9U|crAi;#<{wXJ?MQ-LW0=yi%S@BSnz-fplhf| zkE;DfETt4eDGx5e84b&%kcFv$1Oi_ zOq`s_+tj7I-h-k=eF63oKz*^*HxUR;RGMWy_`&p+x@EtDbH7h!uu*5kxqp;w z|Hoj}{ta&XiMQ6PUIZ_EkP44OWDJl^5APEr_E5F?GQ=!R#IlvogcDT1lxRy% zBT|XR_|Oqw}Ox;IZ zR$37EPKJ=;t?Mn$gzeuv`w;uOJm8$WbC7F68qB9}_ie#Pi6R=MEx zgOh=$p~J?;Qvb)e>mx2jsu-^SBy>I>NI<)3ST-1oz`O!mifj17JFSS%78G+7U9=a~ z#s%qh0!vBE6$g!9>>)BsOE4#!gJjw;DpbS4&NYge` zQvX6K+8%^f#HShX>*dRlKWj6#ins>aLfF=IF`9zH>cbS9C#IVv_`JB`UX#+5*yLtR ztn*t}#BQ{2K3gZ#vAl91vVB+C=>qLHc=E0%{=9@3>7{;TZsgh%xhjP5^u*Mzx!~gj z55A(b>eUkU#ewr0nj@Qg?Oyz)d-u3w80+-9?GDKj_tcuVMRSO{&X*~J^1U8et!!Le zZxy&~_Eia`uxqMCL`7wte)#{wszYhqjxFS<1v)|^#O7Dfjd}--O zNt%uZdk{_jU;Hk@y298$5?*}!71B`u4*4;g+HJoDgRZ0wqtD`v8%#sQ;ptiVJAXYxLdaj$)ir{=3p)+$*XsB9Mw<)jN@mZiD7Af3%&XjT zvhtNU|GcUwNGk9NXf`MrBjcxwS>T_~t`8Af=DgEuy zWc6Zsn!0^6Oj+bbB)3%$FRzr~=BUTiv-rf_VETim!^B12^Ryz_g-nZXH8DI=Eqi^=8cCrby6?KvuL@2EK~Ud`X_{o` zKt$ELx;m1RAN5V0-QeLfiwXK@m%Ttyu;wG@p*zvOHdZxMt3&E>SpBWAGmg?*L0M&N zq;zU+o-icXb$9pInr7`o)fZ+^~a0=ohyh*JSw(EX)AQ-zR5W23L zFZedktu&KH#x>9gdueCoOrz#xV*Koc`%jn7>!z0SiH-3ffr~FwK%q-uqv1AqezD&F zjy6sxw_gZLpku$$3;tVhe*p%x8xQmcc<;=@kZp~P;;Pr5Mlu6)BlF}*7mb+8(e4T) z5NPe~YeXh@CJG6?Xt)ZFA3nZH@Mn_q*rK)+w6x;DClg97QQ@>^X>3dgJ7L)P@1}xk zg6Hu@6S!xVm5qRfaHm}Y@dT6uH6wih;`!`|d*)UiSynv%e#Eyoy3X0NvAgH$$b@vB zEjCAc(k`3QK z;6#UNv=|0=XuDl;`Swc2)UeZR4Ous(8Xpz)2r`Ty>)95RDIj?rvf>wpi{@an3}N29 z&IQ2a9Km;HW25J}+@0E#r(Fq|{DL5o;r@6CU^viCsh|DY2-wRacof0ajfaPKmyGN- z9v-A_y;>e(lWA>lYlHVk{O>S;hzG!11mJ;yJRDFH0eVC;OUZJic;Wb93#G*Hc|Zvy z9h8(Bbi5ND8VBd+LsqxD8QI%bFhs7I*}8A^v>tr%-^^p+a#Y`yG1OU|;S3nwW64+7 za-+NVLe?!msJ7U{I4?+lg_PH;N02c<%=V%ITtmm8 z%K_S2Eb4`63D1VEMkFNEK&Cl#I(K;VF(>RG2{xQ65mHw6$P@EA1J~UNr2hBy%Yw@g z{44VE^1b4Mkdp)6R*ZjzgwtR9_HApN6vcpbOA zqJkbD%MXP|gn6mSWv+~s7lFqQq7~pkhoHsY^02;@?vWLBCgYohDH9*fZbAIEp4sa` z{qOFv81D6MG2e60t^CJQ0_<3{d5(?L^`u79SuXHaH9h~HMmtFAq$d| zN+JFNNC1;Y@%&qCL3#QJh$qBQ$Hy-nRjRzDr>DorhX5&L+7pXH5(~P$di?m!EK#}E zM2VSdM+hKd;LQ(OobjRX04YiN?0!h^*DNvZpR9X}O(A%f6t>XBZbFwtdTX92 zhNpKB-ssDG+G98z9jIiwwAym|a&ATxH8ZUKr4>sYS+HfUvNd@maay+db#=(@#1gr= z$jNHmw-j;dfV)OJ@@Q?r=z3&$63@BmA<*Vn|F`_69>4=mUse!+{J<1Q6TTk z$Z;T91s7KY1r(pC_l;-)S4VsMZP18u-JVb85zkd&z{jGa`wU4_BV_YbAXHFTXbU+n zuy*%XWhp_ZCVjR^qwN!TEkIz8r2pw>*?5lYTHZH^0VF5_T|a2(e*Vjl>|d|nvhWa3ebDNgpG# z>C1^JPR^0$Slm#JT*GU*>R;xv2hLn!e*W}?2J~w<(e2I6)^>K;bBr?YNcdk_!z2W_ zaf1731W4+622D`7S;|m_`orh#(@AbZ#Yu#tApIQvS=Qs*- z70SwYj%2~x3k%31^Pes$=13Op3Jw7QZM%(ctEK(0TvH=iSXd+^nshk7CPK`usH%z| z5>p-`20)xrE>TeXtodkX$!UGE4um?^n-(70g8#fxg&5#&2x|!N^P5(Ov?6`SQ`7DT zzV$Qr>sH(0l2OEO9H{KSC*DU`!YlA_WGz~uWYHA)xuoXR+Mf2F2Ri+rpsg@pQ!dd# zzqjIJZz|CX%-VyJc_Du5}z@ z=Z-HV#P97c(|8v+Dn0sUN^!iXsiDa#roo`YY2#+0k^AL-TYGuY-suhn4PLAfQ`3Uy z7iX)z)VV!=Ah4;RyA!h;fcOq9KOoHVv27;iq=Iz&$8{F_<)MvUIiYwClQ2++0YL7b zJWUdOd=bI4sDb{dqc?;5O5AczIr=mbQ$pw|F-2TMInvjFP2!{o5zR1LyQ zTu!nH{KIIp5Fg(WR~R$5k8CA%Mj zLy&u%otYWMZYY(j58KnlS5bsa?`TYoygk0ULK`-h(^ zDEf=^Lq7zPM%-9FIV@NxnXuWO@8N%uQEkR28pA1cQ@jy0yjEbTg`g@-pBO#I(Qj*j z{BDnN`}h)ht3$TcSG{`Rx`a%6(jc-5i-jHZJ-CA#_3ymBnbp2h0`i+ckRcln8-YQv z3gHDedLQ2({Ng8jwP$3YmUE&lfkMIbD+CE#5I!;T@Tl?ek$?#7b``wD_N^@lKrV5z7Z?2w`w)ee%Q^PfkE5s(t_BwFQGsog%xC+0)P z7_xHBY>$^F*6STt&b#wboPAKGqi<{L*)=1L+{3LoI| z-+E6aPG1r>dK7Zk&TQ2CwG3G zuQjqak2RZqQKk6DU6~U~i1wU$)81S8^PQU5BY$2>9Q1%y{}``?vT}@z$MDH?MRQDY z&E52jtc$#)UliTet*D=8u-<%TM!d zM>C9WLprs^5b3j(3vb}xaLBaaIYGIg=4$IkIJ-ut1=l)pQ%D>abs;GIT_Dft<&l>||Kv~Jiee}aVjL+h@r?yOu z>)xBKbaF;Vrzyf-v{=_5cF~7!TKFzf@ygHERx%w) zXly$C=5RZs#=UvO?X<-6GfVpTt7Woi0p}|UgLNGC=ZATSOw>?HYl{TAud0m|iugFP z3n|xr7Y4P>I*8J3LwOmtHwwPft4lbeD{L>jHThH_{p ztKysyuj+Q}?Nql;`N)Igfs2`+K8o5Abk!32`rakWaz?0Y_P#aeJtibO4H&RW<7CJR z9i0!*5`M%}ej7I{Jlr+WQaeeLaDJXSrZ(X2MV8l`T-1g8+T)5og*jm7_chBIzu>^s0(47p=h?YZ;>1s^&C6pkj3Xt+eYqcW96x=$D`XBBeuOr6itn<|WC^MZ!yxncOX@5c`y2PIH*gUb{dcS5f=Gyc>2{qt&&( zC-ZfF)MT@{#Wq=1eDghlu43Q9st>Uauikx4Rde+My66?4TA?iuZ5JBjWqTVWoD|0B%8p-U^}(v*Zlg=Zj@HDiB1o) zjz*YNc5k8Z?{aSR1#a9aA2t`>UKLVORW#tHQ_K#J5PP2>aOAr4`=o^fK}qyZ3@2uQ zA#4nlX*THVt5L#{iJQ0a2+i9^-&!r#CaZz+p1bx?OS+Vtie9L&E( zO3^#5aLy|e?$eiuBzbew&sQt1oD{dV%s-G};>(F__7fLFuJ;@c<5ztMV|aKjE$U6C z8)vo47KPH_wv6%jOBuq!kBq>a{!-9uAaFg7GxqjyS()=5sqL1}MTjdE0dH4AT$mH# zJ8vNx%l_!GO_5#`EHtEt%JWU@%|t`q_}HGYgtPK4SlT?2LLO2dRaLCAQn63w%$nsx z@yma<$oO-%RK+~dD5>38nPZxW5uxu7TC1^M>B>Eg#m~-?%vHk6YAakdCKq_VHX1kj z`kWUoboD2k_cD1He0^>0cqjr9H?C!L%D9>@#ckq^RQc{&rd>)kX%P&2!b3!E6S@@` zf!}$G#$P)p8oNV5ALdy0!&%XwBtdYgF~foGvjkg18Or{eUrbu3I+%l1-;C+l0!vbV zlq(-isxF0Bp8IY9=gV{_-{i$z*lF|k)hJFn@$8w&J`VD+T6Xi}BI)tXjnj#hXmae0 z&$c(EaBv-Y`Qdi)zgd7JDw~M)N7DZHOtKybxy5_#yeZ7-8|c^W=1rMtc$7zyztDc= zquXVnrSW_hy?pe;)a8=~MbbDVbFq2p$d1( zp34Ti?s&LzxO1%U6I!bVby^!yJuwj-b)$;4!1Co*t#VzvgL>1%!W<66rNiZC64_Eu z@tH>61;^5Et$%T-7|;(-zxQC2%OH_gMLmjTYu%KM?bm~1<;{$epH7OFmaIxy*U+CbjG zna(>;D^>H`!ioupGtyxpWyZ$yTH?}e0-lCHZY2*GGAIZX704yi`jUmdS&$|MeJrZ> ze7@=3>E^>fhl6i4w~~D@-#Q}Kr>qRFFf)s_S7t|z0d2-kI>%zUm#Q>E%BzeD;I>}K zR<(~8(VXO=OvOw-?CFxCW6CQP`e8CP>4oCVsq;r1B1)DSIrp;ZKRbW#&W?c{h4I5u}RSrlh^mtGC)(S-VoUwH5Ny$x?%l=Sra4uZ?|sipgjDVw<^AS+06xB$XC>qx-FIPMtUs>e+4PBOM(}iJPuEeX zI>914_U2v7m1${|rM8Qf`nNQsP~=LR^%MUL*^h+pmleL&e6ND>(pCIvxGX2Hyb`Y7 ze)$pE{zqo*jBtF#FU|e$q4RjSXF9PI;%)1?PRuvhcq;~W`j>qcjK!wT->{btBjxkO+?JH*TQyVXIVtR z)f!z`R~Bz|Om&O&p#Yv4>Ds9r`gFl3CEOr?;(}jheN}|Ho*YATrRunTD}_PEX>mEm zp`c91VtQ0P@x8PkvGdsb!ZRASUBq`=8R?1VcntJqFHE*(Bn?F-U?#g?gYLrO11#Ye`q_*O)tkd9 zg;&5`M8CIzow!+Jw{#VWm3Vx*LWYiyCDe>gG+vocdwFMyT-~*ZG`H!U+zG2;8@I8s zOg24pxbv%TaYRij#3vGgHM=vL#SOZ5-)a`0%|DZ&mKiN2L3~GH>kL+xY88)$sW9&0Gd305qpusiKN{t-9a2&ZNAos9POq7cbQ_PFg5iL+ zI3i8JMtIFnfhQhb^M3<@K!sxhhf#EnW{sk$SBP9G5zj|p(+Xb@reg#Q9#_5Kg_ybd zGMM~ddEbftW@P>y&4I``A0dWQvUt<{=qCHF@Wmg$oB|UIn$FEWE`BXteBpvu1+)-? z4bPY@5ZkSZwt*eN3Y#N@gPJte$9oBnXPpfbw^{|Q82K!gUthfK8ku}fn6iur`iDQ}?x{GU@D)@pB5)v20WqmPp607b zbPkwy%o(mrLLfU0)9@jSlK!03)OjKo5WWNiWe9}scK!n>ORy;f+{{ZXC4zHagoiXg zZ`#Cjo)Bw=D$RW_|BMIcolKC-XDkyJIv-s%hX&E6%425 z!JPVrDXc295dBA#y-tIW`6qb1UKgHV-o(mIuG?n4Q7=)HqWzKK^x$t^1txWdnBOp? zVkLlvGp5xo6d&>)NP#x;ZVKzIL+KpOb3f^g?B06OabQ{9_LNZnn0%*y`d~oHA461O zP9!btVEg+4wdvQDJ=muI3Rre?ZuKQGKQcEkANhMY#&ZN^UPITy@j1yI8}^i+Yrm89 zCy5Cr;nSsocEA3*6)8-19y0)EOOd3*6T|~D=xyT#kElUZyQ5C|FHuBXPK&jw2 zZ<~jseKS+uY;MkvIrG{Db6KUXhHkI&P0yMxZ`d$W#>I{K`6@|)W%-S{EBCs+Jz)o0 z>=yIol0ayF#MJ=G`&@48<)N zJPoZUKZ)98VLHT4A5TgYOjR}Ju2#o15#?CX)${tjjb$^&i&6=7WkO zJX~dEt_^$4p}RoRg!(oI`|gU2El-w`{cN1DzyMXd+duX>=ZqHSOPR0zJMwecK zbopai^IvRX6o(-{=U-~Wkz0T6#^?D4q7nSsP=#Dt z%|)?776;$+geN>!mF)>h?3s?}*IeYdiOHjY$L~v^E5gK*=-b>98rgXBd9zz$?C#J2 z&&<7IXWW(XC)dS$!`x{?xhh03U~boUfq>_dO7v1SA}FY$w4A)&tRkD=K`lX8VHY90 zVz#~T_xfRCPzw{4$V_`jgSR+THzYb0i81m}D5reRRn*@mxfGe)5tmSVRGV2KQ}Ws- z*{MoLhVxZ_gn*N19F@NF*jsarqf5Bm-%qDk2PUUTfAeM!lin?-Tz8B3PkZg}xgd3i zNaUo&@2FW(@$r>gJ=~-ZxFg-%UfTX~3TF%azQy$4jU`BR`3W->?i3@J#bJWunG{M% zLFC|){Tg|e5(+g=y)f?dgsSUpGHFA1@8klCFuALA0I7M+ z085l?!c=6VrCk}Vc=DK*`pOmmmvXrpJzl5ycGRj(gYyV4XXsiY*EWQO z*b4sseE^p&qOM^Mt0;VWPzU@Fypk}JFueZT0tFW+3tpsuee4^f0li|8yLspW?o~cC zn@1x#0C3+$|MMHdWCYKe4U#`xLm+nGtOd6ZrMByXzZ~6(!x(#H#AY=xh-_cIxgLeU ztU7@oE(!girE{!#OmK{uFoX$}26(-+o-kz6%fX+>QG6?i`KW*;I8vkRHO*LH>Luo--I7=uxFrj2Z_7uzyAwW4ko-|%upH78hJqhRVIU{G+ zkgk^~1Ymfl3mf7JQ^u(h_geLA==%9Kve3HHtu$Sfu+@m2iO~amX{1k;h(*4~CT4WIbm**Up#T(y%z5^ZiuSRGsdBRkc z>>z>5UBRF4;Zn+)p9|9?>Ny(jqNtTVBTr7YQtZx;LGa1kOKEC}W z&|HS?>|CV&WDev)vJzq*u>D*-xklQyqmcSLXy_TH^ z;09-YNR{2#hm3Z~w;M}SLNCP6xZEG#wNv9nq8wUPyM8CNgG;3SnjffHZ3V$ zvDkNA2jDdd2ioYBs;A;M^tF%r!?6J^^@*!Gsp{1C7bvroLj+r;)2-%umW{I>>E>rF zs&FVk<|YCL`FGzNEaN<7UB}wR$e6tNjGy@*s^7$13XQlqV<2xtj~nWQ!&AZag1_WE z65Uw}Q{D#bASWXS4)6MGwEYa2T}gT9XTNM*;o-fibq~54IX+K#1@>o4QOS`gA;{K7 zPK7~+sv-Oc2cIGM3}OAUPqwQmi73CC#bPNbLcjdE^>WqTOZN$5_+*%cCL{bBQ8#m)aYMn#NdcBm*u8jGdm5&rHgFapZi>S9zJdt=(BR)U zG1mX#52iq%Gl4Bckccd~kYPS}2QUG^SMga;?`4fs2y7MySqSn5G;t*c5S^uE3fm26^f+|6Ga*`PW>~n}>_!;8STTGp-e~jC{A#I|ylcAV7T)>q@Fh;KH-@}<>z($tE z&Hzr`kj)dRBsQ33{=lF`=vrT=hBQ&kL_xk#ZU%CY2t*17jX&#h@7}KU|J1O)9&JpnUvli~_NKivczP(y(-6_7B81;cKcw6S3c$ zp2IX~VoeN#2D2K#74IvWi3KFQ7srRm4h5hs;$qLcE8xWPUnikRr#NVgkuDsN~swSEl#ng=Ml3G1F=H+lf z;QoXoktLwQa5>+}wIH5Tf-x`fpVM$XY#`N*?%q~kzK9(}{@qLmUvHCu@!kc5>Q@pH z>C0Om{7TVkpSW~C4YfX>O9g(|-+WFFo0{HrwG5u9=BE@VmrV}YUHWtX zO$2y^9I_v}nc>P6vY9;5M|{5qr-gvJ-{-*0x>B?i4l$ol!Pd;*EkRMJ!aHZ&vNzK= z5tn}$zQo~s9hTBq9~|i5t`e1&nY{4Cd@vQB5Dh%QMI6Q`|F_TU@ZnhiQR*9$9#Q$v zYU}|ssPs80cf@mXMmr%!R&979FbpP-dR}L^r-@Hu?ER>6Pr~ZNkK5R|YC z=db^u6-!`GxjZUJN*qe~8qAL8Iyz($POJR8!SQ4V2a5R#+dW$}k)$SSReW1l$VO!b zWuKj1P<$XcC0n=hz?Mkt7Bz#JKDO+Q|HVIl`Ka`Nbj=}C0GyjRK?i^N{kU;tBzupt zL`vO_Zf17&U0*z!@8bK?;CTJM7B4?s+6Shn;EKSit79-H*x}zr{rZJM(cdvN?%gq{ zNRaltn^eNk($X^Y_K%tlL9SasUK|awQ1ycqk@~2-?B)0|QM$dZ(sT|_ZXQ=6*YFgD z7@Pcgl|ohYVs?w_fJaK+m&N@J4Kvp!%~5V0Ey2pI-z-uOSI3X1vVT3T4L`bgzpYJb zb)~hdO5*d2sM@#iG>yHvIgx8@+Ye_d;j#CJ)g`Rl+0yr z`z1Z`74kXz_5SF$7P9;5{bF%bwTo&PDCOrK(!}{Wqbvx z%{nI)OU(T`*!QoW10Sh)SM&UWBuXhAKZS+tQKHqM4DTreZukCAOf1IwqH1+CmtSsf zP!R*}f5ZXaecfPVcVa;5@pYa*a+Zu|J~b7Nk@tem+y zBQ8{*^LoC1k&u;LW-3TmH6It36MM2c)!X_Jy4NpFB~;bU^iGSUtC5V1X}ThW?mEgM zowvQJt?U5k*|T5UZ0Ki+j5IdJRZzCk8$e%y5QWL%_}JEG<%m0YP}+;`Qax)R%Jk1~ zl^BoD$(HZx#l;0qD(6q8Wz5PK57A<46eI$ZUGwK8bjVy5$FD^h5$WPJPtN`L{@apkBRgoh!)mP2CTu%HF1gsy{14aX}r zfeYE!z}ZCNYFIiD>!wVsY})D}dgLusaJp?CeRFbiveYp*^hhX4{g|Ts&eecI+3;ts z1*uezqF-n;f3?PcV5|QvCQ0JN%LwGE(Kn=`a`@+yjircA0-lTBt?tJTWZq-}!bE^a z3|1wu>OF=6V30CBOmWA*aXfhIU@q~WLRnBxRUb-%0?`~JWCrP&6harX8!@Mnh$?;7q)H+8SLX#>@Ktz+`m~B*op9-{@Hq51^r@+Iqsgfjo&;T z$xdm+TWZ_~PoFB5S~Lqfpgxyh=k||ay`>z zP?N_uFqvxlx8ez}P~z%%>K5UAY|!?U>$Kw0Du=cUQwaX=!?L=qI~}?9BgDp%AMcsi z>Qoim)1<0 zk&pQ5P-nG5L#@I-3jinpAZb4ojC`}o-`R(RWsl7kI_N0NA#jIh^Qj>q+N7nUGXcn? zoZvwk&{4}0plXQrJUf95YBY#5K;d4{dV^GLfB}Piq0iJn3b0EV5qfYC_^3+n8#f>Z z)|!;GyExQT`s)EhDobrjqDO|)M7mWCjUSO!zr_!?;g}bv7s#8wTU3b}OiSMWR24oh z*I60f=e|O~;Ccm#6&&-*v~k~m%)CS~+I>;&TR*xW_T!S&>tz*GtG}(Xp;*ARf?KbX zckZf~H6fqviOrLuIM|}m>~=aMb<2FUcLX1)s-JrG(KBeOUKuRS^N=e)ylF%;WL#?l zDpsJ+c?k1`$D5P^arMjF9swVnJ!WF$9yc85XbosW;z zXekp^*n$GmFaZGrUaFV{bsu{H#q-NlxoiI8I}SK6p_|a8)ZQ=`qfO2FXmTbZRW9a9 zmMU0XMnu9gbbuG%H8+_AFel+XyJE7g`aFtM&L1REyMZ3cBU+qa8q_|;w>(KNT}@aH zanun+DJ8@Mk@%bj4*(uc>>AJM5C$`XqI}mpv<&t)5fI`kIQ#sti}#<-K5>*3Dw@I#RKY6-gsgQwR)3Y0uR-%C$#3fgrKQt&Ouc{@C! zA+@3klJ>PQFHnL-d!4(0k<7-F%`)Qy`WYdn^{# z{H_IZ!&}U;=9&1CgZ1av+x3qtJY$&?aqLCx^=})D-xIvgwuw5cx^K6o#1H`{M5!e? zO36_u-q(DbzfUS%Bb~mj{e!bWYk*Gm6_*{4|IAvr!S&ZfHOJ9%Bq6$^>~#EOnh7YfPqztfhRhlo z8lC|(2Y1fdU*hHE1>+m!b~6)-IMVr${-Eml$R=f00)`-HIe@~$3KZRj=^j%m5c(A8 zYzXd8UPxC2!IMm$o?&%#WkT_n%*=hD@Brp*r4>VlJ)wbLLWX|VUFyiT;k0I~Yu7+q zf-~)zHV(mSiJi39m-8zM=hz=Us-Tw3O53(aBBP}{A7WatzUj**n{FbG|6Oq)Bdi3I z4k{&AGiRb=C8w9g@+N|a%|3D@UmoVGaRp$m-;)9nJl`Gw)l@gT1@_tHti-Z+xob|U zeri5^l3uYfPMAkl0X7S6sOIO@!5V)n9+QmRY*&*;0dZt5@m;e5N84S})1KViB`{xJ zEKW=&10XAz#@tqsKU~m!Y)wRL6`!E03q6^9+{w_*!N&v4NLJ}pS^fO>5ZU-uiK)F09g!2<>wP}=m zfkY#KB6FREQJB|7T&RhG4QMHt8IZ&>n6C?rsLw1>iAJvceZYrdIVIsVQh^d4hK4hX z{n>+g?!{{nia=9SSP(7G?+i&fA$uAHZom5%i~V z=R4XBttHJJHowXl!}%shUMNFFEU0^h5D+yLRBuUd@&27cRrw;y!b0%Bfu9Vyc?;Q;F{Y}GzRIIZ zQzKdu;j8l@aH>>2=-=BtNpp*tj^hgnXCVV-895Irq&JC=bDV@k23NRDeO72WGR?M< zRM;r`-Fu}hDY!f|hW1S=Nb>t*`eaWV28H@hG84!G+M}pK*|=Ba6tfL(oeL({E+db; z_K-EE&7cs_R|_z+>fT&hj+h6{Tu?f~RNXw>W{zwF>h-YaDa$ym+5oZs&EJnNcTAwiJOd%#Egw)4&LV2BsLxOZqwPW-*eNhf?W#l$0to5 z8B50Vzmze1O_5O1tRw41uJFb2gR}>)q&}D%Okb#>ct02;opcE|T%7r- zOQa@>OZR14J36jq@zpwA#j+sd^YNv(C67CMWpo;Gon3B?;P#6x$BU|099hlpkytmz`}xvk0l0+>gf^s>=r3k z&S@Ss5((m5iD2e}W%E!-2wh*F3fXBcIuVe1W&qhBZ!TT8wY7y`FRm61Wxi&+JMja1 zOltHr^TzC!qWibs1MHRu#2rfz%U-aP>NraxdKK%~;U@3t)kVjpyryE!+WmowQ><15 z)zFd{Oxy01{Yj|rnB6C-izP9LYhpN$J~#w5VtErOMKhTU+jnvx7+8xgryAh8jo^g%Z``agS3G0LCYy? zb06{R1v`}A&~$XSz%w;~+cA+wO6qstwr}QgNR{nNt<#g*YE2a9+WPCMlZ5?(I|7UY z8nwHebbsR%|yx?AO|az6n?8r;#D=YPNns${|6@sFNqFw$2|?b&bcgcL>DHfc7jo?Jn|EX7yXrKGd2pq;8`zN(JMxvbaU|MAzn5t@n-g zA5^rPnAGK0Y1uhy)%j`ia-{>yw>rR>t1L5jj4eB`B%}7zTRnvedGm?=iNt^OMh_bd z6HN2EF5aa5nIT3%nCOuunfU2WI)PFAZ&Q(C=XZ1`sXXhLV_v-HZJPamL;4aqz4F%Y zHGX@bSd@j+#+hRFaj)`$bdBxNX9}!59r;fYE&SE6N^g^%hLxnInB+z?iy{Ns*_!)3 zt$)R%pUHMwJbWI|r_m;O^pl4$XNBKW!S z(@6KQ6tj~FI(^b~hlKqMH#OhAoS$ifuxXNDMd+}v?2mbjxc0!_zmZ)t=ZgWiObzcm z)0fMLvpdt$;>sRQGJd1$(K}F|&Cdr3CKF?6&l0NNsnt-k<_^EluEOhcmW5_bzr@YF z>dBK-@A^weU`cQ_e>{oIKvJ)O+}H072Jz7GHMR*EFpWO*?LMV0=6|R8ceg50`8SzHdYf3)9P>EMJ+#}2nzX`hdA3h#u6dlnt=EAK6)D>u&Kugu zFdmL?g(XRvM$SRANg(lpn0!z4A|-vwAge1qVk>uC{0Mp*U5E(YcAs?I{r{o!BG*|X2t9r z1IxdgCv2M?vW6#&dxYFCQVP^oYwe!n^_gD{rln7e9V^dvUleLH;g?v_8~j-w!Q>vT4UUOrGB*rzpYhq)Qxm!Eql29I(YVDd%3$h1kNw#C1G*_ z(T52&Wu&E0<}*GVcWTsfG|U^zFF)2RkyBTRDRgk(%}P~HzvoY2@wiFmqjR4}*Zcq1 z80srN%g-TcFKvgiFVcCK-{Dy-END94g@Ru8T*^EyZsp{{M{c)SzG)txBvw8v-42QW zgKyiEskz^!9l-Vo!*pD`--fFRkSKFckpYS2Zed#&ah;Pz)U5BD#g34tLsaFDk(Ptg zWcNrRO^k=*)?6^qKH{&T((!XeqKx{nh+h`G69)%P&uspL)C$mAtXU%IJAb)YZ{|av z8NIJX#E|{H`wt{s+(AobIe!_@FYHXfc*DfD`Pi{pUJ#Xs)V61<3!Fs$b)w)O9^!-D zVkYrctRWWuZtjDL?Qkp9T@Re zAEu`Z64iNMa+>)I&omxawP!~|2FwjRkEzO-dYaPGnet%_kLm>O>_zft)+`mm zLL4*twqH4b6PihgIka(=qS6?dsNP=^Xdx`d+m|Y)eek=+coGW}dZC9&)9LK9)!DYF zf>fdE+G|vw-h@=?*Uu5{14$KvX-%5+qM;z_@$T_3S9WLU&m-9H= z4PN`z5s=&7S>$N1SnO{dl2bh#G@$?Evqx0nOl(n6hVmW4R{knY*+Bj5z*A~*-6LCP zjo&Q6bxb3k+gTZ*v_OjrH(Nh1#Ke=rpBOqa(KeW#HOrD8w`$hqS1B)@1XJW^oe?pV zyTcwt@6YFcUWI?wp3b|y#i^a8{di`vU}kaiQy>uC^@cAoQN;homaxh#4+Q#%W3%oG zBfb}7f|?jmMhzrJkcBUP`h#>IK%LJWuKGZ_R>MOR2x$U_i7Daguh*D{3eHXJHaQ8Dh5R@Z;jjx3<%X{Gs%}>7Bs-8~ zCEW)%8Q_Zx5tvw`P@n))ytGSA5EENzB>Aif4O}t@2A-MZL%$p$Y}wN6FshMZW&`4f z2=G0VSByMl($nc79&b6^i3L~&;?@Q#tL)ht*A#Tmw>!Z(Z_bK^gY+^A!qop~uyES7 z<}myY232eT_NGBLDJ|26wY$t&0b+^;JIuAtxT=W|p9M)`VKVHkl=ofJVwRlrv?)0d zz|~N@c{7D}R;<;HHRK=|8bejFui_C2FmuA~aP!Y7hd5u_0AoXQNst*-`-`vw6Fn2dIWKFgv?;3D@I)qAMjmgXNSAv<=kMJCnq^$+kH8%6=A`w=@>`g5xV`ieW$E-;6$+W(5a~%0Sij%U9A` zLaDvpk5(^3sQ(zk)&Gv`-MJGUDKYn}H`=b)nbaX9%?VA#t*Rdq^3++qc$2BZHq5tK zN2Ep%Ks+8|ds}RX?u~xxIUb*0t}_gYQ@U+NewUQgY(5IQ3@DTVm1w-~5wuyNnrW3N zAw&?_b$X%LgPKf8#U>P-Hr$1o|m~12o8Aef#UPc^HUE_YW9Zs6{Zk+dve1 zWm@NE`^T3hh$qmsxtKOO0WA+_2-|>JS&V<<5!@hUuZQXY@ z$<6|7&Tpxz_k!2*oZYA6|5V&ETmQnqcbz$r?3glkzyoR%e-$d{uA(;()iS>IEpS%9 zMvw}hCouR-3hgGm<@8Z&G=1k^ZbKJN-$0;W9bhEj_9e!Zg@E{~{UoJ(QK+q5OOp?+ z+;rKA_c}M4o3J;CrouJIC@3kDr>`dDQ_~64sWuu06F^GPKL2(c!87;AYGp}s-FuRb zL@ts`uGNTQy(YggPFX*jF@;Am&@ZFPff$#Dzm*AVQU- z6ko|t%P$JXRouMy;4~cQtaIyT7Xt(TMD35eFl+ufN3nN_{Xd)!FdYLN!2hW*|=1ahGXzP2}gFqbvW_#!;m}~oQ%Mtwjzwvgk z@r2j^??}j&)*# zf`k4X%16eZ-w<`P)SZhR$pR0G8P;@4<{va-H)SN;bawzW!>mEi7cO4U0RGk=^OK7+ zfbrsx!ZEu=vM?|t0G9t_(=QiYeg0!&;i(Dt7u|<9hFc|9A!!N!u83S<+IKE-GU!K9 zm*6NL32|g)S5c@thDsk~2+uOEODxPe^w}V)cOLM#lDN9Td0XMUufj{KZ{i+Q*@q75 z==i2L>xFnQGNq+f&Nms_I|_9;2NN7i%UV|L!WGTl*aKG5H^|Rqbo6VM0+C}d$GY<2 zw3F>_+R?X#(egJtfa)2+d@oFSUP64(XnTYonW8WBOxFAPIJ7}IY}RqYKZLT`-XG@q zASFo{g4Ig}7YkbwgojDBo$?GeTBHZwZ7;9p-! zMmW~V9*8r}=Hy$P#&H@_z0zjORk@AJc7wtK9+(&gM#F#gg`Hp;2X~lniSP0+Sj-V; zofGa?IBa%p5NGdMe@r~h@Xbs@mff+jGPAJgA2D6&`r8tUKyHocH~X?*8|w^>7}8%*;@0&-!8D#SC^#H<`K3#QgGx2eaLw++v+dBeQ6s3SMy>m zyqYA^+Unah(3|d#Ld~Q@$1us4q@h7#%}b(lsq9EE9a7)HvB9E-t71H#sgnN3t~E$k zvU5U!m7VI@_01LGAPt2%s_Cih^$hx1u^&_>2(Lq^C?T919$WDv?}3f}8mmCgqrc#z zywFq}T$jROxgWi9LH48fxQHYV@WaVdZSZ*V;@|#>!G*n`11d>L=%=Wq|0N%jbfAxT zeNl>W3WZM>F#{}<9OzQvYK+BZD*_N?W+MDXSm*EY$l-X1OG+-)w=mx!1CuRD428tS z4YKaH7y9ztoB@WJ2X#be-4dIblaa5w&T!^oIJ=u!RHKF9p#`jn$5BFVPwJPAVB3G@3eeU#L&n zTHec0yCmGVun*C|@Q;0Im$?IPUAv~B?!;jZidHeJFx0g$cc2eVMddd{Q$yks)y6!V z#T(dCw-^|x5mpr%cFrDCJmux}+4&-aj0Kl{|JjOEjY01{nv@S8aFl)Mm|A>aECK*2 z|MY4=k}Et6dM*Y$Kc#}2=_5vFG@>Ml62f9V6J${#bQo#ZUN@2!i-?*POH>_V9hu@A&no657T07UlUX%Cq!gSU-_~*dZ_YAC`ix! zs38smNuB|GhWr=g*L){=t#^Qussrk3&EjS>=Ly6O_;nYwd+HzD3kyFW_=>6xevC_Q zcD=IwLXdYa;5_~*Om%R;%hu0d-OWv`<}_62%FT-<0sFcp^g>_wTeC<~{fUYUX}Yfb zW>tRw<18`x`@ElbrG4o=XEyEE*E@3-gFPLfP0&O)|v$EUp7ehQ-31ixSe^ zm&)xap5jHYo204_Ppis;g0e#uApN?5V8U4m4(Tw!dHwS4_-IXfJ$e^^#3jDDoiy7024itr`dI1cdu9utE~4zDAXREqNq2W~ zg4@AQ;<2+3P3e2CUvmxSx0UJxB)**kQI4?%ks0b&`f}lTLO7suFRuUY_$Fg}%%-p= zo~b3RxWo1fly^fYOSR2$uz_1-?Xp0GaI zaGzqy*{O%-YJ(>8jkK6AdQnk{xX)}~Zb`th=ZX;CiyLLTKhm?=A+G3m+^ekt87os( zq0htf{!U1V&}n&MDcSGmB<$VtbO*^ZeA0a)50y@SY|+u4!>CUjhn-EyA7VZ&56y_X z+OmxDXRB)ITpPEorcZml!#91#MwZ7@y#)}xe; z{<9lu7Mvi4kB^QHgL!=6Mn(t~Ur8A)OY1SFhhR}}VTUAr;E>%iSC!fX>R&*0JUqR+ z7=&Tz>AuzVfO2Ru8KhdX;@8hsNej=0n&e%i6K+hbsdmG;%Y4RQ3z6FKmidJKA=LM^ zBc^TA$bWGGf(Sx|9_OKhdE7S6wsm^_6cX;&&`Vel%KZU<7UPW}E9Q%h1DO-Hybc&S z=0C7UUJ`DSYXi1Qcom=T9iiZR|)Euwr62@qy%? zdIiWeB(uZF8ghoZdXw=F>2Etf(5-jw&KDHrkQOJo!(E0f+uU_Y*?;d9mB%Zn(ZR(V zHCIfVGO0qO-`B_5;u7bPNYwz<$-cU92CW)@J!3PojosZQpvyKos@UT=m@2T?}M!Ws6Hk**&Cn8 zSsaq#t=Uhdc)RLahq8tnGjP3&e1_WBhl##VBP#=qA?f-x;(TeqM@EMD)uy+j9_zMQ zRTnGQcVWra!1EF9B7yGLRs@vfHDTruixHMhidzP=R_F05 z2{@ui*}^}#KbygatH3hdjbJEjSAL!1v#J-Z+m~0v0OJ zP^1TuSbJLwj6OmL)hey0KD)>TP?M7z0g+Td&rJe+2k=I`4_J$Z*?ATUzW=j2?_X5B z$T!%R8HMfkTB_w%OknV>TWsDYH;yw3?rV@Rw z$cnp}S31?PlLE=x)E&nwa6#MwLt3D(xY**oI6VCB{d*U{Hn98Guo2K(7FxW4?|zEI{V!BG%T7mRvMd^Ajs}#)C*M9cmd!U{T`^) zb$53I&l;d92UrpL0G01oy61Z=-rYJMZ;SwIEk2jOKRY^%0drL;NBV6BAXze-ht>7= zc66))k-Ed3{qN7Q?gcHAfz_pEb#Ht7cd&X`PSECs5L~3@+q>{0%Y#BwGd3$Xriin0 zLvptYsN>d5u^{oiu|Mceh3XE_!xcm=YzGQ&)T?h(!mF?~ve&y0X@*cQ6OmpYsjQ0zk79$VLNx8sJA>1C>u; z>(YI6*xn8UZlT*}ZLfew67Q#__Ik%{Ft_%$wlsJ9+S=N_eft)lmY)aMMGkv&Gebi* zU}6G`Bp|N~=rU?RzYhdvJPvpkxzh0!VCn)=D)5PQ0W%i(l>wRJJ201@KKccuLctRd zhbeMZTabGE{RQUw{z4NW5s@^AyVb9g)l?aT)pIZWL?T;q=^)44U#wvYwm(?vb1?gF zF-7T&ErlOa@KZwVVhxeiXEJvmvLQ7w=5os)vbSk{d|$iHN#iEMjn1q)oeaBBV9LD% zm@%>C$H&3MNfc^>wO$u9F)IDbgAH<+;fC@|L;8ljM@LKEi!nzVDfUd8tR=7eEyyts zYU4k-*EG&G8Hs484GfOX&z21eM?`Veb;K45<=EG!hNl`ho46N_Sj+$o_JR#?8$Q z*gyqx^crmdGRvCWchO-BP7@dwM0DIu#LaDG2X=806(^q6q#r)BDA5*OS-oMetSIhg zb-10CgvNp`^8<(J;Mc3n!0uE$t{Qacm$axvVtr{B+I9 z#O<`XI=w28yUVHs0hIw;BFp}Mer9C`+3xH!rKiz@>dJ}4eSh-VhCKndG!x4K5Mts)aY^F zQ_2SRf+f05+`tVC_-loyq$~hp5Fq=|=yrkWED;zi7D50_wI^~EKym6r&MZaU3Z;%u zdY*>NEa`VrXN_{GNfs3BXUaO8a#Gpu3^t=!V3L}@k^)W4E1<*gNFqEL+KViZW`F%{ z2XpeRna-7S%QHaB15 z%IS)FzR!qpI8kiU(DT(8*+v~Bh6V5rR3 zD-N@I%<3mbRz%!em^Nzw@mb)crgl(p zb9L6x&|qq84CH#@bG7Awxen-ZQQE&2?_a6FCCnO7-f$VU-&@bs0#kUfo+~tlij!AV zRaKRhx!hbE`O>Ha0%SXF0Bs$}?{gV-zgqi>K?4l5fOd2FM_@#KB~qT$w~M7&y+buy9vb zvYqOE`xXkyWqbRhv^SfP$tFW7o?ztwrHX`XvIwkyoGYTMf$p>s5Q2fIGH}adW?_lQ zMMMBEM2mO5wLq~O@YtmjIb@Q8A-F>G%AW?lSE*rU-k_iuSxN~dZ?s|L)upm%g@+NG za&FEvJQJfdvJh$}&QqY<;{DDW^)B0zLOJS9e0LtSe`olsqV85lq#P$Br_PmSb!F4% z0?+Ex1(7eum~Nzx9AdYiWI-Jq<<_b{91-OE*-7(xM=Rb4z$!(S>VbQ`^aMwGL`6lV zV(6KfnIS_{I>kUa8|BFpz|A)S$5()n8p!-+bF~M6YEJ-iy1Kdxupz~6ij_W~qJpfX!zesv0bER7qS zc5i`mJvc0-I$zb&atG=)VDrffPV4|7W_R~1z*OyaRlWqwb0SXbIl%S=0_=orW^XK0 zI)HD&tMKyj@^9ZnK7RZSPGN!LD!mbL>9N`EO6Nk(H#;}c=LhHSCl+#qiQcRP$m|EQ zFGWZ;CaUf}WQg z3_m0tUvG9o@Uto)n(Cr!JZtqVp^zuK4QakNCf&7AH;Ew#hWNMR+6(k!fJ zV`PjD({Azso(&w@#7Y>nXE(}$V8_a`?C-dc5?>sU2l_vUMg9Gi)3swLJ%#BMO~DBf zH)|2!rgG6{>7%Q|H^>GyZr;K9z6Xq{0OOxD)f4z&)MZUebOb96%Q`*n%?_Wh3#NHZ zy25fPWn#U1e7!rfTs)ht&9JGhtkp{_TsphGlP`77N>IB^PV$4W#lAzkmkh;pI!Zj| z`ieyYPDB69DNTHgZ0(h*3=m(GFLx)J!txI&0I~)aDvJh6p0kStwO}whojGGjS$WuI z{o6RaoSQi~*G?11K1i~R`HQRfvN}b$VxX8%3PTO-gYUOd(%d+>)sx@`v-#cDa=*$F z?H8N|3#vT93m2CM_8R`d44wr#b&M2LA&s+d?&Up{#V5;_8eAzR9H%S}c7}rwujjff z@h%6nEeMBG)QdRRLyhMfFZZ})=ESv9YKt|MLisA8!8V8j?aBr43`>>V4rP58jhMyTqyEp zzG~}ife%nNTsWIRAB%`sYN3+qO??p=Xi6CWs8lbT{VThOoOLsG0%o+$KRi4w@%rc!Z@wX#5i??Rpj0Ed!l-B0+wR8ZI_HUzAl~z))_mJdo9p!;y+Ex?~dkw^-Gg49Z6M;j+Q1h2-rl}bVD1Q zknVZ|$UKKMsq9~(Kbe*&lijfz%9FZQZEb%G(LK>RQh&6xWrX1$0!L z+AQlLGs>v@j&19lF3<_F0wJIsp zR|us2D%HCT$3O4`^)@vF)4(4bN|vJ?EiUI1iw5b%yxSC(ptw95fT5;gB41%`CnEJ| zkB!e!#5%u@mQw- zm9fK>i8{<*Ty9Z)@n>smb8);3s@B;TN>v$^@eC)$p$4==z~(6a7cIbb+%Eo-kIVjL zW)V_C5N82#&_RnZKxVVXPcgBECCXm@YM=RUcXP&%GL*s|9s*>DrJ^v=bUffb#)7=> z>}in|g9>fnJMP>4!*F&Id54YBv7<<)_%0OOmi@%A#pwECubhWx{iEdJXZ_#t{8vY&A9d5S*x(y{etjwPGe%a8vNm&r$|Bqew;b@PuaO2 zvi;v*Dn{|7a&?W&RA}^_FCvbDg3f)O9$b0Vm`})-hzvn7pFe4>7+vBFD7{6HEh9U` z44c&qx*iBt0?>r}_aZCa#B>G{Sy1J;)-UQDm3eN(p-1BDPO*%M;D7?wR|>Cj3*?2BsdS6HuZmW zbd}Y#w#@Uk_{R%xRK!17jBekwtnh{5{FMy0+M)NnNeouox^Z3%`d>|S<6DQDk8$~h zezXJyUyxKClBU=!RTbtOj$R$B5la8`R1!bPlLDJ~(+%yM{A`d54>1EsiZ4fc5ysoY z$)GjX7+kgE=R7f1;pzABgiV>9eNK`v%KzdIo@bT@=peJt<9#N$pSwDWF{yoh()RJ? zadqEY+b7yqZp>0Ys~uhrTkp*$UPl;6kqYdW=GyKMXY1;2a(|6jn({ld-4~0-Tlus@ z?AbX~_17BrzgbY*{n88L>X4Lao&OqagA3LLjS`Tc%!&Yg2ZJD$eh?ve$28Ek_jY zc0dk^wyRvY^~^j#jDZD0_RtHk1X|IfKr%j5H%s}W-^?KO>c-ES}Hi~PH5WV*mSFm~nYMcR;y z&UYj!X869QvVKHW{@L!{%F-dYIlgYaj5(#P^Pj#xmQp>}a@OGG4KOjVf=+BN=npL1 z#Kf|>w3VEh4ls39-8vi(^>|G-H}QFtab8h?CV)!6HvFUYg>|2=7>t~nhO+O{ol5T& zR%%^dO3nE2i*dU9+*5e`@@2Byu^ex6%U{PN!&qI|xJ3_&$;VPfBA%|m?WpDfX>PC2 zvGKY+pAC%hvea1Z*+0MBieCaccghd_z_7(et*>|PU)BAdKoyc;ZmFd{8QmN$dKpCN zCFX?g1n1^kEH8;l6op-9d@a&~K)(ncR!5f@n!zmzr~h=6lqp){2EagjJMQ`e%y+Ve z!n{Y3IPao=LViC$S#GT1AIEQ|DRHG~z^Hr%^k@@g6D(J^#-$Xt2lbBtNdLewW)g*^c=z=K z|EwqxqckAxe>L zN~vJf4<5jQh3qgN{lxIGbx+pVRpG`fGMk8&KsP|(Wd8=%)T-vqlp;2-)@wVh` z2QWSIfj-2c%{xM=og%twq!>}l5V9`XbHjHmMj;bydxKzL-`PK}<(pU&eM$G|ff-;$ z3_skDhzw-OcwKp8-6S#Q8a|_kV+><9xsoE%FBx<+NnmL8uuIJB4WZ8JamL z%TLHfnLmlpA{SGeT3R2P0ZI?R#s3q8RCuRCO=+P1A5igE)v>jpD3x^U9UC0x^3kBo zd-`P+{-DMaozLm~#0nkq(40+_2g3^Y!Tf|hGyrSKyA68|+zB{54yqlF%z%?E5`YbJ zzqtQhHr*bO-~{pB(hCziZgcp27+$7|N){1*tD&WUMfze-Sm#?$+FsPnaY+=x-5zt0 zDA;Kk$f6O1#|k)J{@}e)o_5$ZTmmD2rQSkQECO(F0>^bNtl!ByR+?48BpsNL_{2y* z9{ktCT8yF#Y2AcXSDeU-FIHA;^HRokHj=nbFqkoK@A~Mn7Pfn&EIjjb%JDowg`K>e zy(GR&lr|SRc~*@lctFvBV^D2i=ldc|VGVCM%RDMUY|r}m#>HYb=6FF)EHXy6D|DJa(;P<$ zFWKdY=AjBbryyu~1}Kxtz)zP=>;gi2fhGzAIu_E=vVjIN9#NA?U&o2?;Ypi)6jh%d zi6&=+A9Z);`_3v{jt9I3T$5o2&?wdK4VG6xNKgZSpu5EfvnGcCVD7umew5=!V~;K; zjvME-GIhP21#_l$o-|J{n@c49>Yu*jrKb>uO3Lc_P{n*(@50P3FJ~c#L;DV0^^=>+ zA0RHaaDwT-;^yz>X<3s%!mFbX-Lf<-UK<-R!fE=$ojrqrvGix`@1j3)d?8G(-10$Y ztp#27&X3 ze`!nZ>A-vUV{j)EHJk(@Fsv2r*)G2l?d;qaDQUSfaW@o~GBjFPFQ^ZgA>r<hLW(kh1+ogUeyVpx|%^K+9^>{LDb+fWOvD#orW1%%a$-^mCD&H zDr#~=!V6I~G{>6@CDV)IT&(zXkRM>mx{^<4qD~WF!qAL=W1U59%(Lv_gEVV!HI9W2 zaWe6lmQFQQ#*ankbN&_{uh2Ak$vk>Hqpmc?IL;7P|0KLwvrl&wbR~~><{txuzxvH0 z`gk4==uVO}OIm?+yG3)~0EkYa>M~GR4;`<|JVa<)_RcSsSxU~m1`$L79;4yb>?-yN z-247}K{Z^h*oL-OCFwb<2gs=I;Qit)Eh|9B0E`qz49Zo&iTKYPexI}fTLHX&{HNOD z|CZu~3IkwJXvTfz3Srkjb^czMa|QDJUlPEI0IzN^-QU*FubXu#|K`|p4L|MPZM=_dLsNVz(_jPt{) z1aa~L2QJ!Y7LOgDB2n0%BYpEFfO#_b^3T$m%oW}5y|OvcDh$lS|=GW;PwH5Dh|BSu^PB z?|gr5qo}wx5RWxpS1GLJb-po@N+0wwd;yMvtJv1Y3&n>QY7*XG+o|>9lCTmkjS*Wbcp&Qn8}5p@V~eKAtA}h zNyr*=R+ry`psS;DESc;mO3i?ixAg5zMh7ZxmCdYb@{Iy}{tf%PcV#s-)_>N|M*K)Q z4i5`{p&XE=*-f1D$U0B_Nr0&<2YhdD_qUvKJKO02$G^@NPfkv5uCJpiK7pPhcoMil z6t|JZKv1|`>KlrQiHRaA0|UeA{?89>z6UOik>1z(W@glPg`eNb!HrBz7NzS7X#_$$=L?$J*L@ zdjtIe78$wNSZ8-)0W(@D+8k?e+RUg(5%&C&fGq;9wCUu2ESNgrHueLV5E-aGNU{+Sr)5dI|kn@Vpw-sV%dbD7YU#%Q3M!ViBDQG!>6mj-%^jV*Y2G z0s`IxmnY8q^RrcsmLCgM0nS%F_g)guB29LZd{9HVc_%4p*85Hd23m}i8TNRLufosi zT260W{|f5qd5?F8!>|bn`__;AJpO*Ej#k-4@TTdty#4lUd)ZqoIy{%7#%ghJ)oy-% zPFSrZ%A>b+&f~B>K!pEXkQ}pEtED-wR=v(sMCVIMWm#Dq4yM7!kMi2u9-Ndpr-ZOg zVTOtLg!Yz)t8`3ErX(Ain-B#JSFRrC_oZNR0EEj_F3G>~)AD&wBlG^9B622TG;D^dt|HFY)igPtg+_vJ}}t+`O`QEmpPZ{ z-6VY4mW#L1bG9aEZOAl+b$>m6xob$T(dD$&htp=^H9mf|*@6dn7AYhoIEbW_@J}mY z0tS1F>n5i5!NI^u?eb)EtjSWX$C36XxcTaImbm4*wdhUZgB2s75i^=5By3;+AP!;T zLgnmiEk#AUwYj=PgmshaQBKkjqP*tRe6CimRJZu?te5HkmB8oMLJ@Z~ggGaBDSB;$(`vHREt*267<^=Js(Kp;}Z%#3ef%; zt36mx^>jMlP~cA$FqnSFkvKrV{+bX=LCj>W(FavYQHh~{Ao2VhxN|lFhq=37-#9Zf zK%^GEzixKlU&!nVt*fs+cX7|n%FZ{%V=F`={co>q!o^osHZ~SBM6l?)H)xUrH{`@(0a$?Pq ziIn~UtMXhQtXv;LSdBn|S)y90-Up!hb+*v78ia>1ThO+vp6&|6W6)C7*529pJ)G`& zb$xx^C6~hMalD}`E!_c^^aRi)dqMpJ?AGN2fAKrdR{1=`2}vor)D|-Z3(W*(zp)~7 zB`mDS{-PoPk&-CQOwjcNI z?Jblf4B>UO*1bLD4Qxdxk2fO{0d#9Rn4(i|B=J%3!I9m`&eu`859tZ{H5^Mz%K>=- zVRATx=3C1mFtGSwZ%{x~E)F2!PLnt^>Y68&CD;aLX6MU0Gm#>~r@M=btwTcyjY{B? z$1sbioY}d*KMEVLd|mWLR}0*XgF!Xv`PJs<4LB<&^^OhBJFmPIc}Z*lJ?zt`gW`v-t+o!Q{wG1AhBGB8aE_{M;kS ztm?%Gkn5JRr7Ts8506eLGC+XD+pkknQplvT2l+c$%`B~~z+k0Vm^lMUuDh2fkTze5 zP=X9fiu__qM|%2#yh}Q3y>EUcVxG5Lk=Oy^dU|(-Bbj72b1tt5`=V@i5iJHM7Wov! zvk1NSHo6R)?%Sukwdd!Q{QQeyq)dBMer@p_u7}_JFvy@}U17v@Cw#{TA4r++SO7B8 zp!#$|CDhK6l8GtIzkt907cXFYftQ7mk@A&Tw6Lh7Xke@=Uuj_==@SUx>m_kJNnhs% z1@*1$w_xZXtS1uK_0!YoR7)8d7;Y~6n7|+wo+B|(&@jE}utxm#+e*a5D5Sj6(#@b2eNy}5upUb82RU#@a7fX^A zooZfiA24uT9u367c*l9Q5NqM-!^1-bI3dxyf0F3Y+Mm>#3lw%#*#y|?A~bkUr-45(1S~O2-z6dg1A}It zp!9MV3T^Syg1F>+e|~mzMau6+erw?MnotVH*Wf&Lwcj)2>2`Jt#&f)ijWHG|OkVAC zM4#cSyzD|hJq=nLFf4f>UuCG$;J!}+IoTS?T)Lv^G$=>GAgTrVjdgv{M>@Whp4an~ z9)h+pB`3F6muDeXL5b0%Ab~}ueFrb>yjzx#x!7D<5{6Bxsw%2oG2i*wR#s-~h9+Pu zX~}F!s~g>-(xmMlJb+Wr#+}#&PBAT^^T2phY zh;G#sG-Ueb-6Fp_Qrtab_S%_tKifAoG11h}98BQXnKTQG%*-6e1OPVF4Zw~r&tv;2 zpa*e3;+Xvm;l|OCiC3pBi{6sg!R_|QdjhJK2K(6&D*6k|yRrKL>g(@?LOIo@3e>je zE7xtVPDNp2URM)ppzHz`s%oV=V^dT6v$j<{Xc@N$#8Wl*)|pUt&T z$NBHbSS_XHR$Y#h$fw zg!k6wrpkeoxQ22vb_Wu}y&$S_dV2N>d9po}B2a3OY|5kyO1rOA8ddfWDp0U#85}30 zUjV{%dp5y}C|3u+S5kDeDD0_^4^4i4-~zt*p*L;LH0@1lp#d@N8EZt+n5^oyos+ai za7c*z*^cX0e|$@cHa@pe_v&9;lCUX;_v$oQ3OyMqS=3G&G6P2$MWuB}@3VAD<9tt< zEpYh)fCSAoGdpun0KURY^~~99HuHVYG%40l1NXk!MH~kQ`-PEI7wvrA030b$+lw!~ zN4Gc2%*ujU$gD+eai=xMPF^+UY0q$RS+=&qpbDwFO}F(P7rE?~HP&dr@uG^q`*E}B9>unogyOe zl+kxV5p$Z%BjwT{?{9tG!`aul|%O*|b_%Xo@ zr(@75qN9O^Qv+;#uCehgsmCi|T{}d=$5L6*%3)pPV7%59uwn_mDnR_OOS*4uAb^?| zDtx{ewhvZm*!a&Gjg&Tvf;{PL2k9cmk8dD4&2G^{>uOCoK|xX<1qCfMktvJba+0#A z7Sm;;L4Hx$=;K+!@{tA>g%ZU71ih>#{^o1J+~OV)5kZcJ^6aUrn@d(!R*^1(k&5cW zA&s>YhdpDlSl*x4RdzBd??*b3+;5pb4TcFcyWMCuHkRd?&4vV%XkA&B49K>W|X)`oK zV!5lKu6`jfl>gJELr1h+3fIwf2kJrUhfrcEX~~N68gwwJn09c9fXG#HM8YMoXkLO! zi%ZXWZ@hw_-E5G#x@8CUDob{?*E{U(?5)K_L{vE4xb>7@FD)&BQc|***Jbz(l-q6R zH3W^|T^fh`TPkqtlkfk1xR`$FLi%9C1wJsT#pM&ISL`1h*4i`!o`*&w;C@p`WdNNh zJgnu3vh;6Fo?NM^V7Q`^tc=Yy&O%%B{HvoU>hGz{>IW>0dsq2NIy%}!JG()FLCbng zOGg(#85ayrbiI)msM^C)dAUHn-+yyuT~Y#Kw8v&4n1IU;RJ=F{rtM{=vArJ3LPIMK z!fc9`#nauZ<@1BUzAI4Dm&%bs-TrQjd=c>jTC!eoe2Mt)!LCAJFrL-1Gg$9z=fHAO z`Y=$LkFQCW&rLd&Z?;7g6^NIftCeD6Vxgg-nGVIhb@c?*)WxUM9UrM!5sy%q1wBCm{jC>$|A@v}v2~^Yd#$1qDP#W_?bgh^taWuakk!y|v#l(NLCh&bQ1yD8?IIEpa4md-dgC?}r2x0M zch*UXh1;N(Cl&CtFf#)S_Z5>^=&{ALyv5bP&28^82;RPAw%_`Duu zo{Y>^KUa`ly{4i;>W^V+8k*{gNIt4&Ps`LFe>KE>FL_WNob)9(j2CmF5eaK(MDsQ* zQEg2ouF?KM=S)kMP323BOq?x|A)I?jPz8fqRFn|=?pa%1`_x_By?7y9JJ)uXj5reh zbTHB?EXCLKj)zCa5u-{;_cFvZ+;9eEjo~Vpg)Jt9j-B6aaJF4Zb?}!={m!uXnIKg) zT;%c9*vwIt37=2N^^g*$%}6$#k%<_q2-`T9d^D)a4XI>7eX!rrzOlU>hR>%t#&;R^ zY|3n69bgcaZ9SbDmovpm8<(lr!{64fO9BSJSWstEZX>&)5Qgd+r;#ZPCYp=Xc4;V= z@Sm5AvHq&l)9)*161IuW$fR=LJS&xZvih`ipFYZsmWurvzC=^=MYN4WihY!GSRbn^ z@*Cfcr{7@8owL5z!{S*%7;sSh<7b(C#KCg~3ULe)eyf$$YO`C9{6qrFByY8?1AH93 z;ZTiHIUqJ%awn04%f_LM#rHT`K~iS2Ivg+srp9JJT3(rpb+#rY?R9oCUS7i3{Q3s4 z|G7+vds%8Vsqw)1j;n5y({8NPwE42_*EW{P+ZOu$lX+3t3jW{B^zvi0oCgK^7Q&UL z!~JztyQV;hkZ32spTjC=$7*Z*%O$OM0erP}2EqR7_kQW>{%oGl@oxe?|Fpl~#+Pq+ z&wT2tEYTq&u~I!^m%jsXM-IqQfz%0MIy0H0n zA|e9K)wQx#8>(q;x$XaW_)TKAAr*K=Pgi|P_Xeplb~s~eCDxD87zk@LGB!)TFeaW8 zb8!zRyC1iF1Ckk#k(K?P$(;Jwhk24gAGf$7Bo|17Z?AYolgU}>2pT}>Jcg^7wq?Yp zETCvDuUU2xPNTJ^lRyG`o-6i7d}DBT3=ZOskp=$5lt?f@8^#=BK!SB%Fh`@eDSL=vdQ3 za1`p&8jM!@MATUf@-*{J#~$mm6Db|^Ak)C0I~oQGXtTIOeV+K({KNT=o6{rXIb5L8 zx#L@mXI{@EPX*J>U1#$5fAXjvH-B^C!7E7hhOq7}3})qdX=3bXDRQG~+GbEA_%o^C z{bcJ`(JFc>s{9J``iEIYEf(9lMUXA#3McqdIxQ8#F*zJ2GwWl8sr&DqN2HhfmYKy>>M1hs&3?^okR~B$v)!i*V6t0m3g=0 zMQ_rJ<*=!M0MNGs{gxw>=)pd2r`GSBG&D;BfYMu3T|;du-wnT?IgQzgQC9qs*k zjnm`pE9(qeJG9cUMj#^%j=sYaIi&C;hXRUCN)2OTV(sACS8h+PsWA^(moS>^Sz9y^ ze6Sabb4@7nTsWJOtKHZT2}rn+`Nf`c$ARJI>7v6ODYv69zw-8G*`ZpjvOY*%S{fSc zCkOa!A3uBd#o$cIiA(c-+8WQCo#J=lO#&Sc2a_$QN(I0^ z>R);{*pzUbp6<=%E5!~bpPVduYM}uY1nD`^e-cO(Rm{MQ3UcvG{!K^`NlCh$ zoz|DMbK77S+~<86;E#NQf}Eay6<)WsOalScp}K|!1c*)RA0~3DmFR}}2Pd(6x=k_a zj1Rk2Yd%9c-5y(S)$@h}#^uaXM}!=Z&D{U%{|~FuSpK4SY5GD_WU|Lwo{O)|;nk+w zKCqQF0angkO)nD%vFo>8VyP-uhnm8x%92#HA9Ch+k!1n?)J8AvaYlT?v7%QAT(MpY zqQuDX%mfp+QX$6x)Xcm*>WhIyP+)LoWHjTmu0@6k#Axr9_5p41dD>V11apDPT#c2r z5_G$Ahf=`C#^Wy4o1;jF&J@Gm&Q7&R_vX9@3(CUEYH#ldfQH|8b(}Wy^{(5K`&Vav z#kz1fT<4poT1J>J+VX)5)!lj9@Nn4{dv&9=O=|kAY4_aPe12f+Q0CR2s zRiOj|PDRCHrRq#f1G4@aVlcGFcm%I!UO}`Lv-D?;he|vnuRj(?+pCd9qR{R>p#6Z$ zP?}Uwv?yNZdJiBzy=y3w*>*k)Mn}tZv`CmLL7AY?c1T18P=hfG5Ux|no z2FfB*!pmJ}Ilc(KyN;9-dr8>oD&Vi!x;$U!&dJCqJ6HqGUgQkLN2a9gt!;7dCE9>E zs93uLcIFvnqff%3PYH+<9LiB5y8Y7AR(t2P%X z37YukgVI^o$H!tdRb%7x&AX#$e0_Z%efhm`bG5XJ$jrR(@B(!;bLIzoa&!c~{rg`+ zLV98NWb3Dh>uY<^eQ!wAnoeMcYa&b}h+a6_Ib83}+O!Fa3>YuwI|{aViJ)LiPE|0O z6c>Z}oaVvk_!>rxa??;I`tIaB*hMsYj#X1sK5|PfF=4!fFFzac=vUH?70|`=exMmq zm!cb2yz3tvEXu{jx3I{n8F1_T_xJ6$XYpNx2&Ammo{BeJQ79D1xKfk~&ISfi?<%h!AZ62{pIF?gxE2~!-R;D)8sgw)L87|Yy$>CHlRJht-?o`f>j2x8eR@+Ps zHYkumEzXv;7SwA^t7f980mpk1=dH4qk9)Hm|m%iGl=tYt`OAhRI!n}7==P>BdyFMYxzi(;mw31C4GfT z%^I^20BIv^7CT$qCen3y4xT@RwJ#s2`uYMy7lW8(Wt-XX`1shLnvvfN3>#R=PT-SP zVCwx}zybuIwSnOuv9hCi-3?& zE|F!m>nZB#-s}e$F^|K>LUAH!^*Nx3)f5W^7Z^OHTmKE(xdkPU^N5Ixi3TDnISTR^ zeaI=OOpT1nW_BhYK3wtva|r+xsga31UWY5P#N1V83*h`{CTKdpAoetrC9_;wR;mnD z0V9k3KLIYz>#1y~r`9{Wo;&BC_*3s@%JdI`Lc7Ym=JZI3pGxs?0S|FOhqiuR5ABBA zze%@R31(y@D=T{s=(LbR53R?|R{S9_haJ$6V7fTZ412VA_gH@8s<|wW`f@Nin@$y! zjbPXQS+&@IV{&&E?AbvWHj@hIdJsJO7dEH>$2P!c3JHC=E}XdYjLZc@A?hHy+mVBRCek=8a`I}=b*1|&qUi$6<`9d4rQ~m*pqfZkEiwal zuBqvEf~=O&LdeGRdv5qcP;r@>?axY*W{3Lv$e?t6FP?2%3XOavI=NWyN3cLPx3|67 z-Pj;}!6C)JetL+WWv?T8aUuts`k$Ivt>@Z zeYLgj%*^+A@4=z5lkDJpTwDV)^S5Vc37{#_xJ%>cB_N7gVR&^KQ3x0c_yhzXo{wC3 zm$%TjR0?m$87wMZ)ayq66SVbn)h-}!=>n^hC=@{TvetXEFYdmRd(-xa@IH+G^5q`C zw-;#no%V)|jBIVD3YbtJL4E;E9y(91qeQX635&5IfG!o!BCr7O!owEyc==lH7E0h+ zR3W;LiU#Dl4DyI**z?eQ-ub-63;=Cf%*cq|zP>5=^s5@TYS4b{h>j;S=wAk@*z*}W z3?QB0BYvtDBN3J#VCmbFI5;=~Q;6znoK$%mb90IPQYm>{pBE7w1~V!ynbI=Sfbjr6 z&hnx|yL1beUE`@vDQGD; zb>`%}P%D%X0Es&#F&1yU?&!SS@WSI3fQ zg)x}cg;T||wbnWl`*pc~UDx2WxpXQC9;r7OZQz9gD8wR}4sJmZnm|)FwZ%o@ zia&@(`3F6&l%tCB|8b?(#gUZEc+bCeEw8SvLKHd{U-cT>n0|nf?Ct4!s7@q{uV@!{ zmMk{9-mJ->q6UE;DAow4o~PsbLesM_lH>+keE@(Km?=!9+r7?93LMuU1U;oA?{odp zDsup8GWz+Q?afqfooLXCOHHSkgk>ffv? zFaFmsYX8naXGr9v$^e!58p3Bfn@-I??@MK6LGGc=SUo!gz!Wjj(Gy|Mo$fjvNjTTABODbS?6rFb~tka>f zC_$6M2>Wt#3EZx~J-hJWuav(|%ZM6RhE^2Y+J6rO%h*?LM`#AaYz+<$o?882U+w#~ zSGh^b%0K!88o%B(|EVz3nlE_xzXE!g;r%oc$xXq20!q{N%ma{;uP}%KzmwE6YYY9! zf781@-K;dl(JVFrw3L+C*r{y$bwTsm^W) z82~g`Sy-9;{8*(zlmDMtY)tz4YR2#HtW1p+TZQD_A6V!NoZRowh}dV{<3G#d;>ClC znwFNpg;2m8I;mafvawMaaC0|s*$6im_tW>Qy0|7uN_cyDO-g@S6|o&svc5Wy_~P?& zzpwH(H|!K0OFw6>mLfbL8(PYKBj?*AR&Ft!NTJf#*Y?hkSl`Ag`{wQGpMR!InX=?b zinjM@em1@-^;@Mn_SF4#3z@QX>C^9zk9*sietW(C{jno=>OVMq%A2~&L)3Z3iaEam zEU!*5Y+3p5)`l%x{ycfy|NZU#`VS{20>_Pky;N@2CTZY8lO2_pfg7$5-@SY0%$fxY z9smzVP~G!?d--vl+?GO_|kQ^t0lh<5cP0-oO##G8FoActs4S_A>$h*(v~T0a`95`>$Md&s`LXBTMTdlxiiopb&u`zon?G&3eO~U{V@We} zzTJMf@bt@)-FF`boLHZEb>A8-dw;)s>(+58Dj6H+iqDIAT3`|9rnI@Z`2F>3ay)#e zT4lc~)jJEnwsW4pO81o3(aqUYzB(7VC{GHWd1k6aWz26qo#{WKHUAlhzuGJ)C;xxL z&Y7p5+U4fkbFv*?cwz3oeScFvWov!?`vQ24+>N*2)}8a4e_fh~<=wAUKYl-X_aY)H z!UI@`-@EB4FO#zAX41xr)qDRgy#GHV1LVBC&Dkyr^L>5(*xBCgJ^HA#WzU_THCeA7 z9mu(LN6SG?dqas@{*|Y4OQlZogi55kA3xS2pp`DR+eor9;QL1T(223@r(g1(9Q;p> zYl7Hng>Zw}QpTG8;g*{%ZdR8$*v$^DR9m|-Eii3k$N|Y-uZ<^HymY@*ba#UGjP^60 zD;tcZramf7o_QL0K7O}WbjUTa9=H5M98b8O1xD>$vB|Zxcj3Y{hMP53o>9FIJUVWx zf!860X)`0IcmPiZtddYoWYIngS{UhI*accv`eG4i!$5-`ZjSIpcyY+dsM*B=T3!?JAJR;OXk;vd$@?2>@8XpPm2! literal 0 HcmV?d00001 diff --git a/doc/fig-rs-processing.txt b/doc/fig-rs-processing.txt new file mode 100644 index 0000000..eafe146 --- /dev/null +++ b/doc/fig-rs-processing.txt @@ -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 index 0000000..cc8025a --- /dev/null +++ b/doc/fig_topologies_full.txt @@ -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 index 0000000..0bd1730 --- /dev/null +++ b/doc/fig_topologies_rs.txt @@ -0,0 +1,5 @@ +(RF1) (RF2) + \ / + [RS] + / \ +(RF3) (RF4) diff --git a/doc/filter.texi b/doc/filter.texi new file mode 100644 index 0000000..5a9a15e --- /dev/null +++ b/doc/filter.texi @@ -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 index 0000000..4ad282a --- /dev/null +++ b/doc/install.texi @@ -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 index 0000000..5be2bab --- /dev/null +++ b/doc/ipv6.texi @@ -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 index 0000000..84e6cf5 --- /dev/null +++ b/doc/isisd.8 @@ -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 index 0000000..bbc2896 --- /dev/null +++ b/doc/isisd.texi @@ -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] } {} +@deffnx {ISIS Command} {domain-password [clear | md5] } {} +@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] } {} +@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 } {} +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 } {} +@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 [detail]} {} +@deffnx {Command} {show isis database detail } {} +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 } {} +@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 index 0000000..67fbb5e --- /dev/null +++ b/doc/kernel.texi @@ -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 index 0000000..6d42c04 --- /dev/null +++ b/doc/main.texi @@ -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 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 index 0000000..b0a4a46 --- /dev/null +++ b/doc/mpls/.gitignore @@ -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 index 0000000..afcfaa3 --- /dev/null +++ b/doc/mpls/ChangeLog.opaque.txt @@ -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 index 0000000..c60d0ae --- /dev/null +++ b/doc/mpls/cli_summary.txt @@ -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 index 0000000..ae89ee3 --- /dev/null +++ b/doc/mpls/opaque_lsa.txt @@ -0,0 +1,365 @@ +1. List of "opaque-type dependent" callback functions per LSA-type. + + (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 index 0000000..2b15fa4 --- /dev/null +++ b/doc/mpls/ospfd.conf @@ -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 index 0000000..d157866 --- /dev/null +++ b/doc/next-hop-tracking.txt @@ -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 -> +- better statistics diff --git a/doc/nhrpd.8 b/doc/nhrpd.8 new file mode 100644 index 0000000..e227c20 --- /dev/null +++ b/doc/nhrpd.8 @@ -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 diff --git a/doc/nhrpd.texi b/doc/nhrpd.texi new file mode 100644 index 0000000..71d1ce9 --- /dev/null +++ b/doc/nhrpd.texi @@ -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 index 0000000..0643226 --- /dev/null +++ b/doc/ospf6d.8 @@ -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 index 0000000..31f4db0 --- /dev/null +++ b/doc/ospf6d.texi @@ -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 ". +@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 index 0000000..82218e6 --- /dev/null +++ b/doc/ospf_fundamentals.texi @@ -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 index 0000000..ccfad1a --- /dev/null +++ b/doc/ospfclient.8 @@ -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 . diff --git a/doc/ospfd.8 b/doc/ospfd.8 new file mode 100644 index 0000000..8c819cf --- /dev/null +++ b/doc/ospfd.8 @@ -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 index 0000000..d60ecf2 --- /dev/null +++ b/doc/ospfd.texi @@ -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 } {} +@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 |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 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 ]} {} +@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 } {} +@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 index 0000000..2301c4b --- /dev/null +++ b/doc/overview.texi @@ -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 index 0000000..0dd170a --- /dev/null +++ b/doc/pimd.8 @@ -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 index 0000000..602768a --- /dev/null +++ b/doc/protocol.texi @@ -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 index 0000000..13e988e --- /dev/null +++ b/doc/quagga.texi @@ -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 index 0000000..8fa9bf2 --- /dev/null +++ b/doc/ripd.8 @@ -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 index 0000000..78d63ee --- /dev/null +++ b/doc/ripd.texi @@ -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 index 0000000..6e63dc2 --- /dev/null +++ b/doc/ripngd.8 @@ -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 index 0000000..0e58de6 --- /dev/null +++ b/doc/ripngd.texi @@ -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 index 0000000..b3ef7ca --- /dev/null +++ b/doc/routemap.texi @@ -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 index 0000000..cfe9041 --- /dev/null +++ b/doc/routeserver.texi @@ -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 permit 10 + match peer + call +route-map permit 20 + match peer + call +@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 index 0000000..0918a46 --- /dev/null +++ b/doc/snmp.texi @@ -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 index 0000000..6c67288 --- /dev/null +++ b/doc/snmptrap.texi @@ -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="" + + # 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 index 0000000..f5fa4f4 --- /dev/null +++ b/doc/texinfo.css @@ -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 index 0000000..ddda670 --- /dev/null +++ b/doc/texinfo.tex @@ -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 . +% +% 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} + +% 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 `\^^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 . + % + \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 to achieve this: TeX expands \the only once, + % simply yielding the contents of . (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 + \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{...} +% If we want to allow any 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'{'#1'}'{#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\ }} + +\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 +% 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 +} + +% +% @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% + %@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 index 0000000..a2afa9f --- /dev/null +++ b/doc/vtysh.1 @@ -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 index 0000000..66562a9 --- /dev/null +++ b/doc/vtysh.texi @@ -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 index 0000000..ca99164 --- /dev/null +++ b/doc/watchquagga.8 @@ -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 . +.SH AUTHORS +Copyright 2004 Andrew J. Schorr diff --git a/doc/zebra.8 b/doc/zebra.8 new file mode 100644 index 0000000..6f70389 --- /dev/null +++ b/doc/zebra.8 @@ -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 index 0000000..b133c52 --- /dev/null +++ b/fpm/.gitignore @@ -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 index 0000000..83ab31c --- /dev/null +++ b/fpm/Makefile.am @@ -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 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 index 0000000..318e80a --- /dev/null +++ b/fpm/fpm.proto @@ -0,0 +1,88 @@ +// +// fpm.proto +// +// @copyright Copyright (C) 2016 Sproute Networks, Inc. +// +// @author Avneesh Sachdev +// +// 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 index 0000000..ba18627 --- /dev/null +++ b/fpm/fpm_pb.c @@ -0,0 +1,28 @@ +/* + * fpm_pb.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 index 0000000..8f74ac0 --- /dev/null +++ b/fpm/fpm_pb.h @@ -0,0 +1,63 @@ +/* + * fpm_pb.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 index 0000000..b703808 --- /dev/null +++ b/gdb/lib.txt @@ -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 '. 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 index 0000000..984104b --- /dev/null +++ b/gdb/ospf.txt @@ -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 '. 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 index 0000000..00e56dd --- /dev/null +++ b/infra/buildbot/master/master.cfg @@ -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: 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 index 0000000..34a9340 --- /dev/null +++ b/infra/buildbot/master/pass.cfg @@ -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 index 0000000..dcb136f --- /dev/null +++ b/infra/buildbot/worker/buildbot-slave.service @@ -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 index 0000000..d4177a7 --- /dev/null +++ b/infra/buildbot/worker/buildbot-slave.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/infra/patchwork/pass.py b/infra/patchwork/pass.py new file mode 100644 index 0000000..6514232 --- /dev/null +++ b/infra/patchwork/pass.py @@ -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 index 0000000..1683c0c --- /dev/null +++ b/infra/patchwork/production.py @@ -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 ' +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 index 0000000..bfa6752 --- /dev/null +++ b/infra/patchwork/systemd/patchwork-delivery.socket @@ -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 index 0000000..d152b2d --- /dev/null +++ b/infra/patchwork/systemd/patchwork-delivery@.service @@ -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 index 0000000..90ec4bb --- /dev/null +++ b/infra/patchwork/systemd/patchwork.service @@ -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 index 0000000..30b4bc9 --- /dev/null +++ b/init/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.nfs* +*~ +*.loT + diff --git a/isisd/.gitignore b/isisd/.gitignore new file mode 100644 index 0000000..5e8028c --- /dev/null +++ b/isisd/.gitignore @@ -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 index 0000000..80b3a28 --- /dev/null +++ b/isisd/AUTHORS @@ -0,0 +1,5 @@ +Sampo Saaristo +Ofer Wald +Hannes Gredler +Subbaiah Venkata +Olivier Dugeon diff --git a/isisd/Makefile.am b/isisd/Makefile.am new file mode 100644 index 0000000..bfe2e94 --- /dev/null +++ b/isisd/Makefile.am @@ -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 index 0000000..4f13ff6 --- /dev/null +++ b/isisd/README @@ -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 index 0000000..bbcb421 --- /dev/null +++ b/isisd/dict.c @@ -0,0 +1,1485 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * 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 +#include +#include +#include + +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 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 add value to dictionary\n" + "d delete value from dictionary\n" + "l lookup value in dictionary\n" + "( lookup lower bound\n" + ") lookup upper bound\n" + "# switch to alternate dictionary (0-9)\n" + "j 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 index 0000000..93edb7d --- /dev/null +++ b/isisd/dict.h @@ -0,0 +1,123 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * 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 + +/* + * 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 index 0000000..dd5bf7c --- /dev/null +++ b/isisd/include-netbsd/.gitignore @@ -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 index 0000000..6bc3d25 --- /dev/null +++ b/isisd/include-netbsd/clnp.h @@ -0,0 +1,547 @@ +/* $NetBSD: clnp.h,v 1.13 2001/08/20 12:00:54 wiz Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * 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. + * + * @(#)clnp.h 8.2 (Berkeley) 4/16/94 + */ + +/*********************************************************** + 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 + */ + +/* should be config option but cpp breaks with too many #defines */ +#define DECBIT + +/* + * Return true if the mbuf is a cluster mbuf + */ +#define IS_CLUSTER(m) ((m)->m_flags & M_EXT) + +/* + * Move the halfword into the two characters + */ +#define HTOC(msb, lsb, hword)\ + (msb) = (u_char)((hword) >> 8);\ + (lsb) = (u_char)((hword) & 0xff) +/* + * Move the two charcters into the halfword + */ +#define CTOH(msb, lsb, hword)\ + (hword) = ((msb) << 8) | (lsb) + +/* + * Return true if the checksum has been set - ie. the checksum is + * not zero + */ +#define CKSUM_REQUIRED(clnp)\ + (((clnp)->cnf_cksum_msb != 0) || ((clnp)->cnf_cksum_lsb != 0)) + +/* + * Fixed part of clnp header + */ +struct clnp_fixed { + u_char cnf_proto_id; /* network layer protocol identifier */ + u_char cnf_hdr_len; /* length indicator (octets) */ + u_char cnf_vers; /* version/protocol identifier + * extension */ + u_char cnf_ttl;/* lifetime (500 milliseconds) */ + u_char cnf_type; /* type code */ + /* Includes err_ok, more_segs, and seg_ok */ + u_char cnf_seglen_msb; /* pdu segment length (octets) high + * byte */ + u_char cnf_seglen_lsb; /* pdu segment length (octets) low + * byte */ + u_char cnf_cksum_msb; /* checksum high byte */ + u_char cnf_cksum_lsb; /* checksum low byte */ +} __attribute__((packed)); +#define CNF_TYPE 0x1f +#define CNF_ERR_OK 0x20 +#define CNF_MORE_SEGS 0x40 +#define CNF_SEG_OK 0x80 + +#define CLNP_CKSUM_OFF 0x07 /* offset of checksum */ + +#define clnl_fixed clnp_fixed + +/* + * Segmentation part of clnp header + */ +struct clnp_segment { + u_short cng_id; /* data unit identifier */ + u_short cng_off;/* segment offset */ + u_short cng_tot_len; /* total length */ +}; + +/* + * Clnp fragment reassembly structures: + * + * All packets undergoing reassembly are linked together in + * clnp_fragl structures. Each clnp_fragl structure contains a + * pointer to the original clnp packet header, as well as a + * list of packet fragments. Each packet fragment + * is headed by a clnp_frag structure. This structure contains the + * offset of the first and last byte of the fragment, as well as + * a pointer to the data (an mbuf chain) of the fragment. + */ + +/* + * NOTE: + * The clnp_frag structure is stored in an mbuf immedately + * preceding the fragment data. Since there are words in + * this struct, it must be word aligned. + * + * NOTE: + * All the fragment code assumes that the entire clnp header is + * contained in the first mbuf. + */ +struct clnp_frag { + u_int cfr_first; /* offset of first byte of this frag */ + u_int cfr_last; /* offset of last byte of this frag */ + u_int cfr_bytes; /* bytes to shave to get to data */ + struct mbuf *cfr_data; /* ptr to data for this frag */ + struct clnp_frag *cfr_next; /* next fragment in list */ +}; + +struct clnp_fragl { + struct iso_addr cfl_src;/* source of the pkt */ + struct iso_addr cfl_dst;/* destination of the pkt */ + u_short cfl_id; /* id of the pkt */ + u_char cfl_ttl;/* current ttl of pkt */ + u_short cfl_last; /* offset of last byte of packet */ + struct mbuf *cfl_orighdr; /* ptr to original header */ + struct clnp_frag *cfl_frags; /* linked list of fragments for pkt */ + struct clnp_fragl *cfl_next; /* next pkt being reassembled */ +}; + +/* + * The following structure is used to index into an options section + * of a clnp datagram. These values can be used without worry that + * offset or length fields are invalid or too big, etc. That is, + * the consistancy of the options will be guaranteed before this + * structure is filled in. Any pointer (field ending in p) is + * actually the offset from the beginning of the mbuf the option + * is contained in. A value of NULL for any pointer + * means that the option is not present. The length any option + * does not include the option code or option length fields. + */ +struct clnp_optidx { + u_short cni_securep; /* ptr to start of security option */ + char cni_secure_len; /* length of entire security option */ + + u_short cni_srcrt_s; /* offset of start of src rt option */ + u_short cni_srcrt_len; /* length of entire src rt option */ + + u_short cni_recrtp; /* ptr to beginning of recrt option */ + char cni_recrt_len; /* length of entire recrt option */ + + char cni_priorp; /* ptr to priority option */ + + u_short cni_qos_formatp; /* ptr to format of qos + * option */ + char cni_qos_len; /* length of entire qos option */ + + u_char cni_er_reason; /* reason from ER pdu option */ + + /* ESIS options */ + + u_short cni_esct; /* value from ISH ESCT option */ + + u_short cni_netmaskp; /* ptr to beginning of netmask option */ + char cni_netmask_len; /* length of entire netmask + * option */ + + u_short cni_snpamaskp; /* ptr to start of snpamask option */ + char cni_snpamask_len; /* length of entire snpamask + * option */ + +}; + +#define ER_INVALREAS 0xff /* code for invalid ER pdu discard reason */ + +/* given an mbuf and addr of option, return offset from data of mbuf */ +#define CLNP_OPTTOOFF(m, opt) ((u_short) (opt - mtod(m, caddr_t))) + +/* given an mbuf and offset of option, return address of option */ +#define CLNP_OFFTOOPT(m, off) ((caddr_t) (mtod(m, caddr_t) + off)) + +/* return true iff src route is valid */ +#define CLNPSRCRT_VALID(oidx) ((oidx) && (oidx->cni_srcrt_s)) + +/* return the offset field of the src rt */ +#define CLNPSRCRT_OFF(oidx, options)\ + (*((u_char *)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + 1))) + +/* return the type field of the src rt */ +#define CLNPSRCRT_TYPE(oidx, options)\ + ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s)))) + +/* return the length of the current address */ +#define CLNPSRCRT_CLEN(oidx, options)\ + ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options) - 1))) + +/* return the address of the current address */ +#define CLNPSRCRT_CADDR(oidx, options)\ + ((caddr_t)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options))) + +/* + * return true if the src route has run out of routes this is true if the + * offset of next route is greater than the end of the rt + */ +#define CLNPSRCRT_TERM(oidx, options)\ + (CLNPSRCRT_OFF(oidx, options) > oidx->cni_srcrt_len) + +/* + * Options a user can set/get + */ +#define CLNPOPT_FLAGS 0x01 /* flags: seg permitted, no er xmit, etc */ +#define CLNPOPT_OPTS 0x02 /* datagram options */ + +/* + * Values for particular datagram options + */ +#define CLNPOVAL_PAD 0xcc /* padding */ +#define CLNPOVAL_SECURE 0xc5 /* security */ +#define CLNPOVAL_SRCRT 0xc8 /* source routing */ +#define CLNPOVAL_RECRT 0xcb /* record route */ +#define CLNPOVAL_QOS 0xc3 /* quality of service */ +#define CLNPOVAL_PRIOR 0xcd /* priority */ +#define CLNPOVAL_ERREAS 0xc1 /* ER PDU ONLY: reason for discard */ + +#define CLNPOVAL_SRCSPEC 0x40 /* source address specific */ +#define CLNPOVAL_DSTSPEC 0x80 /* destination address specific */ +#define CLNPOVAL_GLOBAL 0xc0 /* globally unique */ + +/* Globally Unique QOS */ +#define CLNPOVAL_SEQUENCING 0x10 /* sequencing preferred */ +#define CLNPOVAL_CONGESTED 0x08 /* congestion experienced */ +#define CLNPOVAL_LOWDELAY 0x04 /* low transit delay */ + +#define CLNPOVAL_PARTRT 0x00 /* partial source routing */ +#define CLNPOVAL_COMPRT 0x01 /* complete source routing */ + +/* + * Clnp flags used in a control block flags field. + * NOTE: these must be out of the range of bits defined in ../net/raw_cb.h + */ +#define CLNP_NO_SEG 0x010 /* segmentation not permitted */ +#define CLNP_NO_ER 0x020 /* do not generate ERs */ +#define CLNP_SEND_RAW 0x080 /* send pkt as RAW DT not TP DT */ +#define CLNP_NO_CKSUM 0x100 /* don't use clnp checksum */ +#define CLNP_ECHO 0x200 /* send echo request */ +#define CLNP_NOCACHE 0x400 /* don't store cache information */ +#define CLNP_ECHOR 0x800 /* send echo reply */ + +/* valid clnp flags */ +#define CLNP_VFLAGS \ + (CLNP_SEND_RAW|CLNP_NO_SEG|CLNP_NO_ER|CLNP_NO_CKSUM|\ + CLNP_ECHO|CLNP_NOCACHE|CLNP_ECHOR) + +/* + * Constants used by clnp + */ +#define CLNP_HDR_MIN (sizeof (struct clnp_fixed)) +#define CLNP_HDR_MAX (254) +#define CLNP_TTL_UNITS 2 /* 500 milliseconds */ +#define CLNP_TTL 15*CLNP_TTL_UNITS /* time to live (seconds) */ +#define ISO8473_V1 0x01 + +/* + * Clnp packet types + * In order to test raw clnp and tp/clnp simultaneously, a third type of + * packet has been defined: CLNP_RAW. This is done so that the input + * routine can switch to the correct input routine (rclnp_input or + * tpclnp_input) based on the type field. If clnp had a higher level + * protocol field, this would not be necessary. + */ +#define CLNP_DT 0x1C /* normal data */ +#define CLNP_ER 0x01 /* error report */ +#define CLNP_RAW 0x1D /* debug only */ +#define CLNP_EC 0x1E /* echo packet */ +#define CLNP_ECR 0x1F /* echo reply */ + +/* + * ER pdu error codes + */ +#define GEN_NOREAS 0x00 /* reason not specified */ +#define GEN_PROTOERR 0x01 /* protocol procedure error */ +#define GEN_BADCSUM 0x02 /* incorrect checksum */ +#define GEN_CONGEST 0x03 /* pdu discarded due to congestion */ +#define GEN_HDRSYNTAX 0x04 /* header syntax error */ +#define GEN_SEGNEEDED 0x05 /* need segmentation but not allowed */ +#define GEN_INCOMPLETE 0x06 /* incomplete pdu received */ +#define GEN_DUPOPT 0x07 /* duplicate option */ + +/* address errors */ +#define ADDR_DESTUNREACH 0x80 /* destination address unreachable */ +#define ADDR_DESTUNKNOWN 0x81 /* destination address unknown */ + +/* source routing */ +#define SRCRT_UNSPECERR 0x90 /* unspecified src rt error */ +#define SRCRT_SYNTAX 0x91 /* syntax error in src rt field */ +#define SRCRT_UNKNOWNADDR 0x92 /* unknown addr in src rt field */ +#define SRCRT_BADPATH 0x93 /* path not acceptable */ + +/* lifetime */ +#define TTL_EXPTRANSIT 0xa0 /* lifetime expired during transit */ +#define TTL_EXPREASS 0xa1 /* lifetime expired during reassembly */ + +/* pdu discarded */ +#define DISC_UNSUPPOPT 0xb0 /* unsupported option not specified? */ +#define DISC_UNSUPPVERS 0xb1 /* unsupported protocol version */ +#define DISC_UNSUPPSECURE 0xb2 /* unsupported security option */ +#define DISC_UNSUPPSRCRT 0xb3 /* unsupported src rt option */ +#define DISC_UNSUPPRECRT 0xb4 /* unsupported rec rt option */ + +/* reassembly */ +#define REASS_INTERFERE 0xc0 /* reassembly interference */ +#define CLNP_ERRORS 22 + + +#ifdef CLNP_ER_CODES +u_char clnp_er_codes[CLNP_ERRORS] = { + GEN_NOREAS, GEN_PROTOERR, GEN_BADCSUM, GEN_CONGEST, + GEN_HDRSYNTAX, GEN_SEGNEEDED, GEN_INCOMPLETE, GEN_DUPOPT, + ADDR_DESTUNREACH, ADDR_DESTUNKNOWN, + SRCRT_UNSPECERR, SRCRT_SYNTAX, SRCRT_UNKNOWNADDR, SRCRT_BADPATH, + TTL_EXPTRANSIT, TTL_EXPREASS, + DISC_UNSUPPOPT, DISC_UNSUPPVERS, DISC_UNSUPPSECURE, + DISC_UNSUPPSRCRT, DISC_UNSUPPRECRT, REASS_INTERFERE +}; +#endif + +#ifdef TROLL + +#define TR_DUPEND 0x01 /* duplicate end of fragment */ +#define TR_DUPPKT 0x02 /* duplicate entire packet */ +#define TR_DROPPKT 0x04 /* drop packet on output */ +#define TR_TRIM 0x08 /* trim bytes from packet */ +#define TR_CHANGE 0x10 /* change bytes in packet */ +#define TR_MTU 0x20 /* delta to change device mtu */ +#define TR_CHUCK 0x40 /* drop packet in rclnp_input */ +#define TR_BLAST 0x80 /* force rclnp_output to blast many + * packet */ +#define TR_RAWLOOP 0x100 /* make if_loop call clnpintr + * directly */ +struct troll { + int tr_ops; /* operations to perform */ + float tr_dup_size; /* % to duplicate */ + float tr_dup_freq; /* frequency to duplicate packets */ + float tr_drop_freq; /* frequence to drop packets */ + int tr_mtu_adj; /* delta to adjust if mtu */ + int tr_blast_cnt; /* # of pkts to blast out */ +}; + +#define SN_OUTPUT(clcp, m)\ + troll_output(clcp->clc_ifp, m, clcp->clc_firsthop, clcp->clc_rt) + +#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\ + rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))\ + - trollctl.tr_mtu_adj) + +#ifdef _KERNEL +extern float troll_random; +#endif + +#else /* NO TROLL */ + +#define SN_OUTPUT(clcp, m)\ + (*clcp->clc_ifp->if_output)(clcp->clc_ifp, m, clcp->clc_firsthop, \ + clcp->clc_rt) + +#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\ + rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))) + +#endif /* TROLL */ + +/* + * Macro to remove an address from a clnp header + */ +#define CLNP_EXTRACT_ADDR(isoa, hoff, hend)\ + {\ + isoa.isoa_len = (u_char)*hoff;\ + if ((((++hoff) + isoa.isoa_len) > hend) ||\ + (isoa.isoa_len > 20) || (isoa.isoa_len == 0)) {\ + hoff = (caddr_t)0;\ + } else {\ + (void) bcopy(hoff, (caddr_t)isoa.isoa_genaddr, \ + isoa.isoa_len);\ + hoff += isoa.isoa_len;\ + }\ + } + +/* + * Macro to insert an address into a clnp header + */ +#define CLNP_INSERT_ADDR(hoff, isoa)\ + *hoff++ = (isoa).isoa_len;\ + (void) bcopy((caddr_t)((isoa).isoa_genaddr), hoff, (isoa).isoa_len);\ + hoff += (isoa).isoa_len; + +/* + * Clnp hdr cache. Whenever a clnp packet is sent, a copy of the + * header is made and kept in this cache. In addition to a copy of + * the cached clnp hdr, the cache contains + * information necessary to determine whether the new packet + * to send requires a new header to be built. + */ +struct clnp_cache { + /* these fields are used to check the validity of the cache */ + struct iso_addr clc_dst;/* destination of packet */ + struct mbuf *clc_options; /* ptr to options mbuf */ + int clc_flags; /* flags passed to clnp_output */ + + /* these fields are state that clnp_output requires to finish the pkt */ + int clc_segoff; /* offset of seg part of header */ + struct rtentry *clc_rt; /* ptr to rtentry (points into the route + * structure) */ + struct sockaddr *clc_firsthop; /* first hop of packet */ + struct ifnet *clc_ifp;/* ptr to interface structure */ + struct iso_ifaddr + *clc_ifa;/* ptr to interface address */ + struct mbuf *clc_hdr;/* cached pkt hdr (finally)! */ +}; + +#ifdef _KERNEL +struct iso_addr; +struct sockaddr_iso; +struct mbuf; +struct clnp_segment; +struct sockaddr; +struct rt_entry; +struct clnp_fragl; +struct clnp_optidx; +struct isopcb; +struct snpa_hdr; +struct iso_ifaddr; +struct route_iso; + +/* clnp_debug.c */ +char *clnp_hexp __P((char *, int, char *)); +char *clnp_iso_addrp __P((struct iso_addr *)); +char *clnp_saddr_isop __P((struct sockaddr_iso *)); + +/* clnp_er.c */ +void clnp_er_input __P((struct mbuf *, struct iso_addr *, u_int)); +void clnp_discard __P((struct mbuf *, u_int)); +void clnp_emit_er __P((struct mbuf *, u_int)); +int clnp_er_index __P((u_int)); + +int clnp_fragment __P((struct ifnet *, struct mbuf *, struct sockaddr *, + int, int, int, struct rtentry *)); +struct mbuf *clnp_reass __P((struct mbuf *, struct iso_addr *, + struct iso_addr *, struct clnp_segment *)); +int clnp_newpkt __P((struct mbuf *, struct iso_addr *, struct iso_addr *, + struct clnp_segment *)); +void clnp_insert_frag __P((struct clnp_fragl *, struct mbuf *, + struct clnp_segment *)); +struct mbuf *clnp_comp_pdu __P((struct clnp_fragl *)); +#ifdef TROLL +float troll_random __P((void)); +int troll_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *)); +#endif + +/* clnp_input.c */ +void clnp_init __P((void)); +void clnlintr __P((void)); +void clnp_input __P((struct mbuf *, ...)); + +/* clnp_options.c */ +void clnp_update_srcrt __P((struct mbuf *, struct clnp_optidx *)); +void clnp_dooptions __P((struct mbuf *, struct clnp_optidx *, struct ifnet *, + struct iso_addr *)); +int clnp_set_opts __P((struct mbuf **, struct mbuf **)); +int clnp_opt_sanity __P((struct mbuf *, caddr_t, int, struct clnp_optidx *)); + +/* clnp_output.c */ +int clnp_output __P((struct mbuf *, ...)); +void clnp_ctloutput __P((void)); + +/* clnp_raw.c */ +void rclnp_input __P((struct mbuf *, ...)); +int rclnp_output __P((struct mbuf *, ...)); +int rclnp_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); +int clnp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *)); + +/* clnp_subr.c */ +struct mbuf *clnp_data_ck __P((struct mbuf *, int)); +caddr_t clnp_extract_addr __P((caddr_t, int, struct iso_addr *, + struct iso_addr *)); +int clnp_ours __P((struct iso_addr *)); +void clnp_forward __P((struct mbuf *, int, struct iso_addr *, + struct clnp_optidx *, int, struct snpa_hdr *)); +caddr_t clnp_insert_addr __P((caddr_t, struct iso_addr *, struct iso_addr *)); +int clnp_route __P((struct iso_addr *, struct route_iso *, int, + struct sockaddr **, struct iso_ifaddr **)); +int clnp_srcroute __P((struct mbuf *, struct clnp_optidx *, struct route_iso *, + struct sockaddr **, struct iso_ifaddr **, + struct iso_addr *)); +int clnp_echoreply __P((struct mbuf *, int, struct sockaddr_iso *, + struct sockaddr_iso *, struct clnp_optidx *)); +int clnp_badmtu __P((struct ifnet *, struct rtentry *, int, char *)); +void clnp_ypocb __P((caddr_t, caddr_t, u_int)); + +/* clnp_timer.c */ +struct clnp_fragl *clnp_freefrags __P((struct clnp_fragl *)); +void clnp_slowtimo __P((void)); +void clnp_drain __P((void)); + +#ifdef TROLL +struct troll trollctl; +#endif /* TROLL */ + +#endif /* _KERNEL */ diff --git a/isisd/include-netbsd/esis.h b/isisd/include-netbsd/esis.h new file mode 100644 index 0000000..ded864e --- /dev/null +++ b/isisd/include-netbsd/esis.h @@ -0,0 +1,146 @@ +/* $NetBSD: esis.h,v 1.11 1997/11/03 15:01:19 is 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. + * + * @(#)esis.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 + */ + +#include + +#define SNPAC_AGE 60 /* seconds */ +#define ESIS_CONFIG 60 /* seconds */ +#define ESIS_HT (ESIS_CONFIG * 2) + +/* + * Fixed part of an ESIS header + */ +struct esis_fixed { + u_char esis_proto_id; /* network layer protocol identifier */ + u_char esis_hdr_len; /* length indicator (octets) */ + u_char esis_vers; /* version/protocol identifier + * extension */ + u_char esis_res1; /* reserved */ + u_char esis_type; /* type code */ + /* technically, type should be &='d 0x1f */ +#define ESIS_ESH 0x02 /* End System Hello */ +#define ESIS_ISH 0x04 /* Intermediate System Hello */ +#define ESIS_RD 0x06 /* Redirect */ + u_char esis_ht_msb; /* holding time (seconds) high byte */ + u_char esis_ht_lsb; /* holding time (seconds) low byte */ + u_char esis_cksum_msb; /* checksum high byte */ + u_char esis_cksum_lsb; /* checksum low byte */ +} __attribute__((packed)); +/* + * Values for ESIS datagram options + */ +#define ESISOVAL_NETMASK 0xe1 /* address mask option, RD PDU only */ +#define ESISOVAL_SNPAMASK 0xe2 /* snpa mask option, RD PDU only */ +#define ESISOVAL_ESCT 0xc6 /* end system conf. timer, ISH PDU + * only */ + + +#define ESIS_CKSUM_OFF 0x07 +#define ESIS_CKSUM_REQUIRED(pdu)\ + ((pdu->esis_cksum_msb != 0) || (pdu->esis_cksum_lsb != 0)) + +#define ESIS_VERSION 1 + +struct esis_stat { + u_short es_nomem; /* insufficient memory to send hello */ + u_short es_badcsum; /* incorrect checksum */ + u_short es_badvers; /* incorrect version number */ + u_short es_badtype; /* unknown pdu type field */ + u_short es_toosmall; /* packet too small */ + u_short es_eshsent; /* ESH sent */ + u_short es_eshrcvd; /* ESH rcvd */ + u_short es_ishsent; /* ISH sent */ + u_short es_ishrcvd; /* ISH rcvd */ + u_short es_rdsent; /* RD sent */ + u_short es_rdrcvd; /* RD rcvd */ +}; + +#ifdef _KERNEL +struct esis_stat esis_stat; +struct socket; +struct mbuf; +struct snpa_hdr; +struct clnp_optidx; +struct iso_addr; +struct rtentry; +struct sockaddr_dl; + +void esis_init __P((void)); +int esis_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *)); +void esis_input __P((struct mbuf *, ...)); +void esis_rdoutput __P((struct snpa_hdr *, struct mbuf *, struct clnp_optidx *, + struct iso_addr *, struct rtentry *)); +int esis_insert_addr __P((caddr_t *, int *, struct iso_addr *, struct mbuf *, + int)); +void esis_eshinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_ishinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_rdinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_config __P((void *)); +void esis_shoutput __P((struct ifnet *, int, int, caddr_t, int, + struct iso_addr *)); +void isis_input __P((struct mbuf *, ...)); +int isis_output __P((struct mbuf *, ...)); +void *esis_ctlinput __P((int, struct sockaddr *, void *)); +#endif /* _KERNEL */ diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h new file mode 100644 index 0000000..42b9bc8 --- /dev/null +++ b/isisd/include-netbsd/iso.h @@ -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 +#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 +#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 +#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 index 0000000..8afabed --- /dev/null +++ b/isisd/isis_adjacency.c @@ -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 + +#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 index 0000000..99d0c49 --- /dev/null +++ b/isisd/isis_adjacency.h @@ -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 index 0000000..889b4c3 --- /dev/null +++ b/isisd/isis_bpf.c @@ -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 +#if ISIS_METHOD == ISIS_METHOD_BPF +#include +#include +#include +#include +#include + +#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 index 0000000..1d1a59e --- /dev/null +++ b/isisd/isis_circuit.c @@ -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 +#ifdef GNU_LINUX +#include +#else +#include +#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 index 0000000..9ada1e2 --- /dev/null +++ b/isisd/isis_circuit.h @@ -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 index 0000000..d158961 --- /dev/null +++ b/isisd/isis_common.h @@ -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 index 0000000..8b21894 --- /dev/null +++ b/isisd/isis_constants.h @@ -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 index 0000000..0f642a7 --- /dev/null +++ b/isisd/isis_csm.c @@ -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 + +#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 index 0000000..d6b13ac --- /dev/null +++ b/isisd/isis_csm.h @@ -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 index 0000000..7c7e090 --- /dev/null +++ b/isisd/isis_dlpi.c @@ -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 +#if ISIS_METHOD == ISIS_METHOD_DLPI +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..bc6ec11 --- /dev/null +++ b/isisd/isis_dr.c @@ -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 + +#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 index 0000000..bad6836 --- /dev/null +++ b/isisd/isis_dr.h @@ -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 index 0000000..412f098 --- /dev/null +++ b/isisd/isis_dynhn.c @@ -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 + +#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 index 0000000..f06a067 --- /dev/null +++ b/isisd/isis_dynhn.h @@ -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 index 0000000..abc4471 --- /dev/null +++ b/isisd/isis_events.c @@ -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 + +#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 index 0000000..e7cfa35 --- /dev/null +++ b/isisd/isis_events.h @@ -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 index 0000000..ec0eaa4 --- /dev/null +++ b/isisd/isis_flags.c @@ -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 +#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 index 0000000..e2e42ad --- /dev/null +++ b/isisd/isis_flags.h @@ -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 index 0000000..aac8451 --- /dev/null +++ b/isisd/isis_lsp.c @@ -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 + * + * 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 + +#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 index 0000000..a35bfa7 --- /dev/null +++ b/isisd/isis_lsp.h @@ -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 index 0000000..bbb1fb2 --- /dev/null +++ b/isisd/isis_main.c @@ -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 + +#include "getopt.h" +#include "thread.h" +#include "log.h" +#include +#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 index 0000000..f19b441 --- /dev/null +++ b/isisd/isis_misc.c @@ -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 + +#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 index 0000000..37eaea1 --- /dev/null +++ b/isisd/isis_misc.h @@ -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 index 0000000..e1e10df --- /dev/null +++ b/isisd/isis_network.h @@ -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 index 0000000..1dfb462 --- /dev/null +++ b/isisd/isis_pdu.c @@ -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 + +#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 index 0000000..3eca731 --- /dev/null +++ b/isisd/isis_pdu.h @@ -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 index 0000000..2427047 --- /dev/null +++ b/isisd/isis_pfpacket.c @@ -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 +#if ISIS_METHOD == ISIS_METHOD_PFPACKET +#include /* the L2 protocols */ +#include + +#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 index 0000000..ad0a31b --- /dev/null +++ b/isisd/isis_redist.c @@ -0,0 +1,825 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.c + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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 + +#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 index 0000000..cc9c2e6 --- /dev/null +++ b/isisd/isis_redist.h @@ -0,0 +1,59 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.h + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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 index 0000000..c0ec01e --- /dev/null +++ b/isisd/isis_route.c @@ -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 + +#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 index 0000000..0d2379c --- /dev/null +++ b/isisd/isis_route.h @@ -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 index 0000000..3443a0a --- /dev/null +++ b/isisd/isis_routemap.c @@ -0,0 +1,565 @@ +/* + * IS-IS Rout(e)ing protocol - isis_routemap.c + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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 + +#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 index 0000000..1cb063f --- /dev/null +++ b/isisd/isis_routemap.h @@ -0,0 +1,25 @@ +/* + * IS-IS Rout(e)ing protocol - isis_routemap.h + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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 index 0000000..6b2456f --- /dev/null +++ b/isisd/isis_spf.c @@ -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 + +#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 index 0000000..aa543b7 --- /dev/null +++ b/isisd/isis_spf.h @@ -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 + */ +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 index 0000000..6cb8551 --- /dev/null +++ b/isisd/isis_te.c @@ -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 +#include + +#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 index 0000000..4f29fdb --- /dev/null +++ b/isisd/isis_te.h @@ -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 index 0000000..1d29d78 --- /dev/null +++ b/isisd/isis_tlv.c @@ -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 + +#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 index 0000000..5a39d56 --- /dev/null +++ b/isisd/isis_tlv.h @@ -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 index 0000000..4148eb5 --- /dev/null +++ b/isisd/isis_vty.c @@ -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 +#include + +#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" + "\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" + "\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 index 0000000..40157b5 --- /dev/null +++ b/isisd/isis_zebra.c @@ -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 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include "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 index 0000000..dd36d7d --- /dev/null +++ b/isisd/isis_zebra.h @@ -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 index 0000000..9844ae5 --- /dev/null +++ b/isisd/isisd.c @@ -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 + +#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 [detail] ] + * [ show isis database [detail] ] + * [ show isis database . [detail] ] + * [ show isis database . [detail] ] + * [ show isis database .- [detail] ] + * [ show isis database .- [detail] ] + * [ show isis database detail ] + * [ show isis database detail ] + * [ show isis database detail . ] + * [ show isis database detail . ] + * [ show isis database detail .- ] + * [ show isis database detail .- ] + */ +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) .- or + * (b) . or + * (c) 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.- + */ + 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 index 0000000..47b1595 --- /dev/null +++ b/isisd/isisd.conf.sample @@ -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 index 0000000..eedb451 --- /dev/null +++ b/isisd/isisd.h @@ -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 index 0000000..294fe99 --- /dev/null +++ b/isisd/iso_checksum.c @@ -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 +#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 index 0000000..5f8d41f --- /dev/null +++ b/isisd/iso_checksum.h @@ -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 index 0000000..dd75c27 --- /dev/null +++ b/isisd/topology/.gitignore @@ -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 index 0000000..fe73ae5 --- /dev/null +++ b/isisd/topology/Makefile.am @@ -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 index 0000000..2c457c5 --- /dev/null +++ b/isisd/topology/random.c @@ -0,0 +1,166 @@ +/*********************************************************************/ +/* */ +/* current processor time in seconds */ +/* difference between two calls is processor time spent by your code */ +/* needs: , */ +/* depends on compiler and OS */ +/* */ +/*********************************************************************/ + +#include +#include +#include + +/* + * 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 index 0000000..91a4799 --- /dev/null +++ b/isisd/topology/spacyc.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include + +#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 index 0000000..e1c87ab --- /dev/null +++ b/isisd/topology/spgrid.c @@ -0,0 +1,738 @@ +#include + +#include +#include +#include + +#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 index 0000000..1c1ceea --- /dev/null +++ b/isisd/topology/spgrid.h @@ -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 index 0000000..1c1eb19 --- /dev/null +++ b/isisd/topology/sprand.c @@ -0,0 +1,501 @@ +#include + +#include +#include +#include +#include + +#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 index 0000000..02aa432 --- /dev/null +++ b/lib/.gitignore @@ -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 index 0000000..cb9e1fb --- /dev/null +++ b/lib/Makefile.am @@ -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 index 0000000..5d7d057 --- /dev/null +++ b/lib/agentx.c @@ -0,0 +1,315 @@ +/* SNMP support + * Copyright (C) 2012 Vincent Bernat + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU 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 + +#if defined HAVE_SNMP && defined SNMP_AGENTX +#include +#include +#include +#include + +#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 (¬ification_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 (¬ification_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 index 0000000..ee93101 --- /dev/null +++ b/lib/buffer.c @@ -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 + +#include "memory.h" +#include "buffer.h" +#include "log.h" +#include "network.h" +#include + + + +/* 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 , 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 index 0000000..6c3dc76 --- /dev/null +++ b/lib/buffer.h @@ -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 index 0000000..43940b7 --- /dev/null +++ b/lib/checksum.c @@ -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 +#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 index 0000000..b310f74 --- /dev/null +++ b/lib/checksum.h @@ -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 index 0000000..662f8a3 --- /dev/null +++ b/lib/command.c @@ -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 + + +#include "memory.h" +#include "log.h" +#include +#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, ""); + 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, ©_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 index 0000000..cc5dd08 --- /dev/null +++ b/lib/command.h @@ -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; + +/* "" global */ +extern char *command_cr; +#endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/daemon.c b/lib/daemon.c new file mode 100644 index 0000000..c473555 --- /dev/null +++ b/lib/daemon.c @@ -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 +#include + +#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 index 0000000..583befb --- /dev/null +++ b/lib/distribute.c @@ -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 + +#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 index 0000000..e9625a3 --- /dev/null +++ b/lib/distribute.h @@ -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 +#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 index 0000000..e94aa4c --- /dev/null +++ b/lib/event_counter.c @@ -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 + +#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 index 0000000..f40c6cd --- /dev/null +++ b/lib/event_counter.h @@ -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 index 0000000..47a1e88 --- /dev/null +++ b/lib/fifo.h @@ -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 index 0000000..a472941 --- /dev/null +++ b/lib/filter.c @@ -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 + +#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 index 0000000..e6ccd33 --- /dev/null +++ b/lib/filter.h @@ -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 index 0000000..7a58a8a --- /dev/null +++ b/lib/getopt.c @@ -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 . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#include + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* 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 +# 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 +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# 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 +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# include +# 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 index 0000000..b359a47 --- /dev/null +++ b/lib/getopt.h @@ -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 index 0000000..bd3099e --- /dev/null +++ b/lib/getopt1.c @@ -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 +#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 + +/* 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 +#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 +#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 + +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 index 0000000..8ddd9ff --- /dev/null +++ b/lib/gitversion.pl @@ -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 () { + 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 < + +#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 index 0000000..920c668 --- /dev/null +++ b/lib/hash.h @@ -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 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 + +#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 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 index 0000000..e4a83de --- /dev/null +++ b/lib/if_rmap.c @@ -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 + +#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 index 0000000..e6c2966 --- /dev/null +++ b/lib/if_rmap.h @@ -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 index 0000000..6154c34 --- /dev/null +++ b/lib/jhash.c @@ -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 index 0000000..985ac94 --- /dev/null +++ b/lib/jhash.h @@ -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 index 0000000..8a2fdd2 --- /dev/null +++ b/lib/keychain.c @@ -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 + +#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 index 0000000..f962864 --- /dev/null +++ b/lib/keychain.h @@ -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 index 0000000..9265ca5 --- /dev/null +++ b/lib/libospf.h @@ -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 index 0000000..8b6a852 --- /dev/null +++ b/lib/linklist.c @@ -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 + +#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 index 0000000..96aaf43 --- /dev/null +++ b/lib/linklist.h @@ -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 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 + +#include "log.h" +#include "memory.h" +#include "command.h" +#ifndef SUNOS_5 +#include +#endif +/* for printstack on solaris */ +#ifdef HAVE_UCONTEXT_H +#include +#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= 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 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 +#include + +/* 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 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 + * + * 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 +#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 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 + * + * 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 index 0000000..b8305dd --- /dev/null +++ b/lib/memory.c @@ -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 +/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */ +#if !defined(HAVE_STDLIB_H) || (defined(GNU_LINUX) && defined(HAVE_MALLINFO)) +#include +#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 index 0000000..5013529 --- /dev/null +++ b/lib/memory.h @@ -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 index 0000000..bd13327 --- /dev/null +++ b/lib/memtypes.awk @@ -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_[] .....' +# +# 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 index 0000000..e5b3546 --- /dev/null +++ b/lib/memtypes.c @@ -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 index 0000000..b982640 --- /dev/null +++ b/lib/network.c @@ -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 +#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 index 0000000..0fcb575 --- /dev/null +++ b/lib/network.h @@ -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 index 0000000..5eb2182 --- /dev/null +++ b/lib/nexthop.c @@ -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 + +#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 ""; + + 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 index 0000000..0c0dfca --- /dev/null +++ b/lib/nexthop.h @@ -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 index 0000000..5261bab --- /dev/null +++ b/lib/pid_output.c @@ -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 +#include +#include +#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 index 0000000..644506b --- /dev/null +++ b/lib/plist.c @@ -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 + +#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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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 index 0000000..aa14e74 --- /dev/null +++ b/lib/plist.h @@ -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 index 0000000..6459579 --- /dev/null +++ b/lib/plist_int.h @@ -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 index 0000000..69ab8e6 --- /dev/null +++ b/lib/pqueue.c @@ -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 + +#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 index 0000000..8bb6961 --- /dev/null +++ b/lib/pqueue.h @@ -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 index 0000000..43fc317 --- /dev/null +++ b/lib/prefix.c @@ -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 + +#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 index 0000000..2cf0b20 --- /dev/null +++ b/lib/prefix.h @@ -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 +#else +# ifdef GNU_LINUX +# include +# else +# include +# 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 index 0000000..3fb96ae --- /dev/null +++ b/lib/privs.c @@ -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 +#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 index 0000000..46d614e --- /dev/null +++ b/lib/privs.h @@ -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 index 0000000..48b363e --- /dev/null +++ b/lib/queue.h @@ -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 index 0000000..4cee464 --- /dev/null +++ b/lib/regex-gnu.h @@ -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 must be included (by the caller) before + . */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +# include +#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 \ matches . + If not set, then \ 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 index 0000000..122f447 --- /dev/null +++ b/lib/regex.c @@ -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 +#endif +#ifdef _WIN32 +/* Windows does not provide unistd.h, which is required for abort() */ +#include +#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 +#else +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +# include +#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: must be included before . */ +# include +# include +#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 +#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 +# 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 +# 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 +# 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 + +/* isalpha etc. are used for the character classes. */ +#include + +/* 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 +# 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 + +/* 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 (®_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 + set_number_at + succeed_n + + jump_n + (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; + + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of 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 ? ®s : (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 index 0000000..e1595af --- /dev/null +++ b/lib/route_types.pl @@ -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 () { + # 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 <{"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 <{"cname"}, $protodetail{$p}->{"char"}; +} + +print < + +#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 index 0000000..68129e1 --- /dev/null +++ b/lib/routemap.h @@ -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 index 0000000..a120028 --- /dev/null +++ b/lib/sigevent.c @@ -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 +#include +#include +#include + +#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 +#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 index 0000000..248fa2c --- /dev/null +++ b/lib/sigevent.h @@ -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 + +#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 index 0000000..03da99f --- /dev/null +++ b/lib/smux.c @@ -0,0 +1,1502 @@ +/* SNMP support + * 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 + +#if defined HAVE_SNMP && defined SNMP_SMUX +#include +#include + +#include "log.h" +#include "thread.h" +#include "linklist.h" +#include "command.h" +#include +#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 index 0000000..dc91cac --- /dev/null +++ b/lib/smux.h @@ -0,0 +1,116 @@ +/* SNMP support + * 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_SNMP_H +#define _ZEBRA_SNMP_H + +#include +#include + +#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 index 0000000..f6f9845 --- /dev/null +++ b/lib/snmp.c @@ -0,0 +1,133 @@ +/* SNMP support + * 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 + +#ifdef HAVE_SNMP +#include +#include + +#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 index 0000000..3e6ee73 --- /dev/null +++ b/lib/sockopt.c @@ -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 + +#ifdef SUNOS_5 +#include +#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 index 0000000..a9b8aca --- /dev/null +++ b/lib/sockopt.h @@ -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 index 0000000..8e0ec24 --- /dev/null +++ b/lib/sockunion.c @@ -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 + +#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 index 0000000..3613073 --- /dev/null +++ b/lib/sockunion.h @@ -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 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 + +#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 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 index 0000000..ed677c1 --- /dev/null +++ b/lib/stream.c @@ -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 +#include + +#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 index 0000000..9127bcd --- /dev/null +++ b/lib/stream.h @@ -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 (, NULL, ) 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 index 0000000..da21361 --- /dev/null +++ b/lib/table.c @@ -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 + +#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 index 0000000..2ffd79b --- /dev/null +++ b/lib/table.h @@ -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 index 0000000..de4d76d --- /dev/null +++ b/lib/thread.c @@ -0,0 +1,1381 @@ +/* Thread management routine + * Copyright (C) 1998, 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. + */ + +/* #define DEBUG */ + +#include +#include + +#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 +#include +#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 index 0000000..3f16216 --- /dev/null +++ b/lib/thread.h @@ -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 + +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 index 0000000..7c14862 --- /dev/null +++ b/lib/vector.c @@ -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 + +#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 index 0000000..6b27fd9 --- /dev/null +++ b/lib/vector.h @@ -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 index 0000000..aef1d09 --- /dev/null +++ b/lib/version.h.in @@ -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 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 + +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#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 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 (); + * 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 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 + +#include "linklist.h" +#include "thread.h" +#include "buffer.h" +#include +#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 +#include + +#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 + +/* 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 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): + <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 index 0000000..6453e7b --- /dev/null +++ b/lib/workqueue.c @@ -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 +#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 index 0000000..aac7860 --- /dev/null +++ b/lib/workqueue.h @@ -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 index 0000000..bf0a851 --- /dev/null +++ b/lib/zassert.h @@ -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 index 0000000..a32faaa --- /dev/null +++ b/lib/zclient.c @@ -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 + +#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 + +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 index 0000000..d46728d --- /dev/null +++ b/lib/zclient.h @@ -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 index 0000000..a405d46 --- /dev/null +++ b/lib/zebra.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STROPTS_H +#include +#endif /* HAVE_STROPTS_H */ +#ifdef HAVE_SYS_SELECT_H +#include +#endif /* HAVE_SYS_SELECT_H */ +#include +#include +#include +#ifdef HAVE_SYS_SYSCTL_H +#ifdef GNU_LINUX +#include +#endif +#include +#endif /* HAVE_SYS_SYSCTL_H */ +#include +#ifdef HAVE_SYS_CONF_H +#include +#endif /* HAVE_SYS_CONF_H */ +#ifdef HAVE_SYS_KSYM_H +#include +#endif /* HAVE_SYS_KSYM_H */ +#include +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif /* TIME_WITH_SYS_TIME */ +#include +#include +#ifdef HAVE_RUSAGE +#include +#endif /* HAVE_RUSAGE */ +#ifdef HAVE_LIMITS_H +#include +#endif /* HAVE_LIMITS_H */ +#ifdef HAVE_INTTYPES_H +#include +#endif /* HAVE_INTTYPES_H */ +#ifdef HAVE_STDBOOL_H +#include +#endif +/* primarily for __STDC_IEC_559__ with clang */ +#ifdef HAVE_FEATURES_H +#include +#endif + +/* machine dependent includes */ +#ifdef SUNOS_5 +#include +#endif /* SUNOS_5 */ + +/* machine dependent includes */ +#ifdef HAVE_LINUX_VERSION_H +#include +#endif /* HAVE_LINUX_VERSION_H */ + +#ifdef HAVE_ASM_TYPES_H +#include +#endif /* HAVE_ASM_TYPES_H */ + +/* misc include group */ +#include +#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 +#include +#endif /* HAVE_LCAPS */ + +#ifdef HAVE_SOLARIS_CAPABILITIES +#include +#endif /* HAVE_SOLARIS_CAPABILITIES */ + +/* network include group */ + +#include + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif /* HAVE_SYS_SOCKIO_H */ + +#ifdef __APPLE__ +#define __APPLE_USE_RFC_3542 +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include +#include + +#ifdef HAVE_NET_NETOPT_H +#include +#endif /* HAVE_NET_NETOPT_H */ + +#include + +#ifdef HAVE_NET_IF_DL_H +#include +#endif /* HAVE_NET_IF_DL_H */ + +#ifdef HAVE_NET_IF_VAR_H +#include +#endif /* HAVE_NET_IF_VAR_H */ + +#ifdef HAVE_NET_ROUTE_H +#include +#endif /* HAVE_NET_ROUTE_H */ + +#ifdef HAVE_NETLINK +#include +#include +#include +#else +#define RT_TABLE_MAIN 0 +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ + +#include + +#ifdef HAVE_INET_ND_H +#include +#endif /* HAVE_INET_ND_H */ + +#ifdef HAVE_NETINET_IN_VAR_H +#include +#endif /* HAVE_NETINET_IN_VAR_H */ + +#ifdef HAVE_NETINET6_IN6_VAR_H +#include +#endif /* HAVE_NETINET6_IN6_VAR_H */ + +#ifdef HAVE_NETINET_IN6_VAR_H +#include +#endif /* HAVE_NETINET_IN6_VAR_H */ + +#ifdef HAVE_NETINET6_IN_H +#include +#endif /* HAVE_NETINET6_IN_H */ + + +#ifdef HAVE_NETINET6_IP6_H +#include +#endif /* HAVE_NETINET6_IP6_H */ + +#ifdef HAVE_NETINET_ICMP6_H +#include +#endif /* HAVE_NETINET_ICMP6_H */ + +#ifdef HAVE_NETINET6_ND6_H +#include +#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 +#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 ; + 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 index 0000000..3f3bd0a --- /dev/null +++ b/m4/.gitignore @@ -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 index 0000000..49a29bd --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=Makefile.am README.txt diff --git a/m4/README.txt b/m4/README.txt new file mode 100644 index 0000000..ce06853 --- /dev/null +++ b/m4/README.txt @@ -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 index 0000000..27b0f0c --- /dev/null +++ b/m4/ax_sys_weak_alias.m4 @@ -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 +# +# 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 index 0000000..a9f70e2 --- /dev/null +++ b/nhrpd/Makefile.am @@ -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 index 0000000..3fe1f65 --- /dev/null +++ b/nhrpd/README.kernel @@ -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 index 0000000..af358fa --- /dev/null +++ b/nhrpd/README.nhrpd @@ -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 ' binding, or specify 'local '. 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 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 index 0000000..b1f49aa --- /dev/null +++ b/nhrpd/debug.h @@ -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 index 0000000..75a16ea --- /dev/null +++ b/nhrpd/linux.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..32f21ed --- /dev/null +++ b/nhrpd/list.h @@ -0,0 +1,191 @@ +/* Linux kernel style list handling function + * + * Written from scratch by Timo Teräs , 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 index 0000000..f05596b --- /dev/null +++ b/nhrpd/netlink.h @@ -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 + +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 index 0000000..a418eca --- /dev/null +++ b/nhrpd/netlink_arp.c @@ -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 +#include +#include +#include +#include +#include + +#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 index 0000000..93998dc --- /dev/null +++ b/nhrpd/netlink_gre.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..e5e3bbf --- /dev/null +++ b/nhrpd/nhrp-events.lua @@ -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 index 0000000..447a814 --- /dev/null +++ b/nhrpd/nhrp_cache.c @@ -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 index 0000000..34cb065 --- /dev/null +++ b/nhrpd/nhrp_event.c @@ -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 +#include +#include + +#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 index 0000000..4ea8076 --- /dev/null +++ b/nhrpd/nhrp_interface.c @@ -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 +#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 index 0000000..56ea271 --- /dev/null +++ b/nhrpd/nhrp_main.c @@ -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 +#include + +#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//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 index 0000000..d77ec0b --- /dev/null +++ b/nhrpd/nhrp_nhs.c @@ -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(®->reglist_entry); + list_add_tail(®->reglist_entry, &nhs->reglist_head); + nhrp_peer_notify_add(reg->peer, ®->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 index 0000000..5d2866a --- /dev/null +++ b/nhrpd/nhrp_packet.c @@ -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 +#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 index 0000000..5095d55 --- /dev/null +++ b/nhrpd/nhrp_peer.c @@ -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 + +#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 index 0000000..a4bc9fa --- /dev/null +++ b/nhrpd/nhrp_protocol.h @@ -0,0 +1,128 @@ +/* nhrp_protocol.h - NHRP protocol definitions + * + * Copyright (c) 2007-2012 Timo Teräs + * + * This software is licensed under the MIT License. + * See MIT-LICENSE.txt for additional details. + */ + +#ifndef NHRP_PROTOCOL_H +#define NHRP_PROTOCOL_H + +#include + +/* 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 index 0000000..a80f3e0 --- /dev/null +++ b/nhrpd/nhrp_route.c @@ -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])) : "", + 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])) : "", + 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 index 0000000..60c6392 --- /dev/null +++ b/nhrpd/nhrp_shortcut.c @@ -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 index 0000000..f9e1ee0 --- /dev/null +++ b/nhrpd/nhrp_vc.c @@ -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 index 0000000..91269f2 --- /dev/null +++ b/nhrpd/nhrp_vty.c @@ -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(®->peer->vc->remote.nbma, buf[0], sizeof buf[0]) : "-", + sockunion2str(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 index 0000000..9222ad4 --- /dev/null +++ b/nhrpd/nhrpd.h @@ -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 index 0000000..0fbe8b0 --- /dev/null +++ b/nhrpd/os.h @@ -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 index 0000000..24b3199 --- /dev/null +++ b/nhrpd/reqid.c @@ -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 index 0000000..07bdb73 --- /dev/null +++ b/nhrpd/resolver.c @@ -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 +#include + +#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 index 0000000..5491bac --- /dev/null +++ b/nhrpd/vici.c @@ -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 +#include +#include + +#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 index 0000000..24b900b --- /dev/null +++ b/nhrpd/vici.h @@ -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 index 0000000..ead7cfd --- /dev/null +++ b/nhrpd/zbuf.c @@ -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 +#include +#include +#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 index 0000000..73d7073 --- /dev/null +++ b/nhrpd/zbuf.h @@ -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 +#include +#include +#include + +#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 index 0000000..2216d97 --- /dev/null +++ b/nhrpd/znl.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..2cd630b --- /dev/null +++ b/nhrpd/znl.h @@ -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 index 0000000..3fef0b7 --- /dev/null +++ b/ospf6d/.gitignore @@ -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 index 0000000..75f2257 --- /dev/null +++ b/ospf6d/Makefile.am @@ -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 index 0000000..258f533 --- /dev/null +++ b/ospf6d/OSPFv3-MIB.txt @@ -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 index 0000000..f5a0046 --- /dev/null +++ b/ospf6d/README @@ -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 + 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 +Kunihiro Ishiguro + diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c new file mode 100644 index 0000000..7e94cef --- /dev/null +++ b/ospf6d/ospf6_abr.c @@ -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 + +#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 index 0000000..e5e2660 --- /dev/null +++ b/ospf6d/ospf6_abr.h @@ -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 index 0000000..1861fe7 --- /dev/null +++ b/ospf6d/ospf6_area.c @@ -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 + +#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 index 0000000..655967a --- /dev/null +++ b/ospf6d/ospf6_area.h @@ -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 index 0000000..a442506 --- /dev/null +++ b/ospf6d/ospf6_asbr.c @@ -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 + +#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 index 0000000..14113b0 --- /dev/null +++ b/ospf6d/ospf6_asbr.h @@ -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 index 0000000..815db7b --- /dev/null +++ b/ospf6d/ospf6_flood.c @@ -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 + +#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 index 0000000..3a6f300 --- /dev/null +++ b/ospf6d/ospf6_flood.h @@ -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 index 0000000..fa6509f --- /dev/null +++ b/ospf6d/ospf6_interface.c @@ -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 + +#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 index 0000000..2fbb83c --- /dev/null +++ b/ospf6d/ospf6_interface.h @@ -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 index 0000000..591dab3 --- /dev/null +++ b/ospf6d/ospf6_intra.c @@ -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 + +#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 index 0000000..c9660b6 --- /dev/null +++ b/ospf6d/ospf6_intra.h @@ -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 index 0000000..3f008d3 --- /dev/null +++ b/ospf6d/ospf6_lsa.c @@ -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 + +/* 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 index 0000000..a85ca66 --- /dev/null +++ b/ospf6d/ospf6_lsa.h @@ -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 index 0000000..b5a4587 --- /dev/null +++ b/ospf6d/ospf6_lsdb.c @@ -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 + +#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 index 0000000..425f615 --- /dev/null +++ b/ospf6d/ospf6_lsdb.h @@ -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 index 0000000..1afe84a --- /dev/null +++ b/ospf6d/ospf6_main.c @@ -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 +#include + +#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 index 0000000..d382f03 --- /dev/null +++ b/ospf6d/ospf6_message.c @@ -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 + +#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 + +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 index 0000000..b085a96 --- /dev/null +++ b/ospf6d/ospf6_message.h @@ -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 index 0000000..fbad62a --- /dev/null +++ b/ospf6d/ospf6_neighbor.c @@ -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 + +#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 index 0000000..54b7ffe --- /dev/null +++ b/ospf6d/ospf6_neighbor.h @@ -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 index 0000000..53d6c35 --- /dev/null +++ b/ospf6d/ospf6_network.c @@ -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 + +#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 index 0000000..4fa2839 --- /dev/null +++ b/ospf6d/ospf6_network.h @@ -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 index 0000000..d011601 --- /dev/null +++ b/ospf6d/ospf6_proto.c @@ -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 + +#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 index 0000000..af60eb9 --- /dev/null +++ b/ospf6d/ospf6_proto.h @@ -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 index 0000000..3cfbab5 --- /dev/null +++ b/ospf6d/ospf6_route.c @@ -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 + +#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 index 0000000..027a648 --- /dev/null +++ b/ospf6d/ospf6_route.h @@ -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 index 0000000..266031e --- /dev/null +++ b/ospf6d/ospf6_snmp.c @@ -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 + +#ifdef HAVE_SNMP + +#include +#include + +#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 index 0000000..fa1b0c3 --- /dev/null +++ b/ospf6d/ospf6_snmp.h @@ -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 index 0000000..b6dbd0d --- /dev/null +++ b/ospf6d/ospf6_spf.c @@ -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 + +#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 index 0000000..b3481dc --- /dev/null +++ b/ospf6d/ospf6_spf.h @@ -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 index 0000000..7ec4309 --- /dev/null +++ b/ospf6d/ospf6_top.c @@ -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 + +#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 index 0000000..97fac0d --- /dev/null +++ b/ospf6d/ospf6_top.h @@ -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 index 0000000..2976214 --- /dev/null +++ b/ospf6d/ospf6_zebra.c @@ -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 + +#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 index 0000000..51eb9d7 --- /dev/null +++ b/ospf6d/ospf6_zebra.h @@ -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 index 0000000..387f692 --- /dev/null +++ b/ospf6d/ospf6d.c @@ -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 + +#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 index 0000000..0a6ddb7 --- /dev/null +++ b/ospf6d/ospf6d.conf.sample @@ -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 index 0000000..9e2efb4 --- /dev/null +++ b/ospf6d/ospf6d.h @@ -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 index 0000000..191322b --- /dev/null +++ b/ospfclient/.gitignore @@ -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 index 0000000..b865c55 --- /dev/null +++ b/ospfclient/AUTHORS @@ -0,0 +1 @@ +Ralph Keller diff --git a/ospfclient/COPYING b/ospfclient/COPYING new file mode 100644 index 0000000..b8cf3a1 --- /dev/null +++ b/ospfclient/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. + + + Copyright (C) 19yy + + 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. + + , 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 index 0000000..b42a17a --- /dev/null +++ b/ospfclient/INSTALL @@ -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 index 0000000..1fca431 --- /dev/null +++ b/ospfclient/Makefile.am @@ -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 index 0000000..5b1ec4f --- /dev/null +++ b/ospfclient/NEWS @@ -0,0 +1 @@ +This file contains news. diff --git a/ospfclient/README b/ospfclient/README new file mode 100644 index 0000000..894cd78 --- /dev/null +++ b/ospfclient/README @@ -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 index 0000000..ed7ca94 --- /dev/null +++ b/ospfclient/ospf_apiclient.c @@ -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 + +#include +#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 index 0000000..8098619 --- /dev/null +++ b/ospfclient/ospf_apiclient.h @@ -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 index 0000000..1de7644 --- /dev/null +++ b/ospfclient/ospfclient.c @@ -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 +#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 \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 index 0000000..f7d6f09 --- /dev/null +++ b/ospfd/.gitignore @@ -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 index 0000000..782e332 --- /dev/null +++ b/ospfd/ChangeLog.opaque.txt @@ -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 index 0000000..f586d73 --- /dev/null +++ b/ospfd/Makefile.am @@ -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 index 0000000..dac6182 --- /dev/null +++ b/ospfd/OSPF-ALIGNMENT.txt @@ -0,0 +1,119 @@ +$Id: OSPF-ALIGNMENT.txt,v 1.1 2004/11/17 17:59:52 gdt Exp $ + +Greg Troxel +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 index 0000000..217c1e5 --- /dev/null +++ b/ospfd/OSPF-MIB.txt @@ -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 index 0000000..8a3ab99 --- /dev/null +++ b/ospfd/OSPF-TRAP-MIB.txt @@ -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 index 0000000..95c2d46 --- /dev/null +++ b/ospfd/ospf_abr.c @@ -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 + +#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) : "" ); + + 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 index 0000000..e367e44 --- /dev/null +++ b/ospfd/ospf_abr.h @@ -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 index 0000000..e92f162 --- /dev/null +++ b/ospfd/ospf_api.c @@ -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 + +#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 index 0000000..eb5ad0a --- /dev/null +++ b/ospfd/ospf_api.h @@ -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 index 0000000..aac8ef4 --- /dev/null +++ b/ospfd/ospf_apiserver.c @@ -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 + +#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 + +#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 *) ¶m, seqnum); + if (mask & Power2[OSPF_NETWORK_LSA]) + LSDB_LOOP (NETWORK_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_SUMMARY_LSA]) + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) + LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) + LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, 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 *) ¶m, 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 *) ¶m, 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 *) ¶m, 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 *) ¶m, 0); + break; + case OSPF_OPAQUE_AS_LSA: + LSDB_LOOP (OPAQUE_LINK_LSDB (ospf), rn, lsa) + apiserver_flush_opaque_type_callback(lsa, (void *) ¶m, 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 index 0000000..b60f56b --- /dev/null +++ b/ospfd/ospf_apiserver.h @@ -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 + 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 index 0000000..0a411e4 --- /dev/null +++ b/ospfd/ospf_asbr.c @@ -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 + +#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 index 0000000..0fc3302 --- /dev/null +++ b/ospfd/ospf_asbr.h @@ -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 index 0000000..fe40b10 --- /dev/null +++ b/ospfd/ospf_ase.c @@ -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 + +#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 index 0000000..e6a1b2f --- /dev/null +++ b/ospfd/ospf_ase.h @@ -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 index 0000000..be9fa2a --- /dev/null +++ b/ospfd/ospf_dump.c @@ -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 + +#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 index 0000000..f843df0 --- /dev/null +++ b/ospfd/ospf_dump.h @@ -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 index 0000000..8de4974 --- /dev/null +++ b/ospfd/ospf_flood.c @@ -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 + +#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 index 0000000..1ab11b8 --- /dev/null +++ b/ospfd/ospf_flood.h @@ -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 index 0000000..b2d0fae --- /dev/null +++ b/ospfd/ospf_ia.c @@ -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 + +#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 index 0000000..b65b938 --- /dev/null +++ b/ospfd/ospf_ia.h @@ -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 index 0000000..a46ca6d --- /dev/null +++ b/ospfd/ospf_interface.c @@ -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 + +#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 index 0000000..7070353 --- /dev/null +++ b/ospfd/ospf_interface.h @@ -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 index 0000000..ae6d0cd --- /dev/null +++ b/ospfd/ospf_ism.c @@ -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 + +#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 index 0000000..f0357a4 --- /dev/null +++ b/ospfd/ospf_ism.h @@ -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 index 0000000..f49e263 --- /dev/null +++ b/ospfd/ospf_lsa.c @@ -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 + +#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" + 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 index 0000000..3c87962 --- /dev/null +++ b/ospfd/ospf_lsa.h @@ -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 index 0000000..b92e749 --- /dev/null +++ b/ospfd/ospf_lsdb.c @@ -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 + +#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 index 0000000..51ae45b --- /dev/null +++ b/ospfd/ospf_lsdb.h @@ -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 index 0000000..32b64e2 --- /dev/null +++ b/ospfd/ospf_main.c @@ -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 + +#include +#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 index 0000000..06e63dd --- /dev/null +++ b/ospfd/ospf_neighbor.c @@ -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 + +#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 index 0000000..38f8cb5 --- /dev/null +++ b/ospfd/ospf_neighbor.h @@ -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 + +/* 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 index 0000000..02ddf92 --- /dev/null +++ b/ospfd/ospf_network.c @@ -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 + +#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 index 0000000..8257adb --- /dev/null +++ b/ospfd/ospf_network.h @@ -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 index 0000000..5c01a58 --- /dev/null +++ b/ospfd/ospf_nsm.c @@ -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 + +#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 index 0000000..9b7e14a --- /dev/null +++ b/ospfd/ospf_nsm.h @@ -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 index 0000000..39465c1 --- /dev/null +++ b/ospfd/ospf_opaque.c @@ -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 + +#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 index 0000000..2ac9b41 --- /dev/null +++ b/ospfd/ospf_opaque.h @@ -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. + * + */ +#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 index 0000000..facba89 --- /dev/null +++ b/ospfd/ospf_packet.c @@ -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 + +#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 . + */ + 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 index 0000000..337686a --- /dev/null +++ b/ospfd/ospf_packet.h @@ -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 index 0000000..f093208 --- /dev/null +++ b/ospfd/ospf_ri.c @@ -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 +#include + +#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 index 0000000..c507434 --- /dev/null +++ b/ospfd/ospf_ri.h @@ -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->||<-- 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 index 0000000..eb7829a --- /dev/null +++ b/ospfd/ospf_route.c @@ -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 + +#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 index 0000000..d509e4a --- /dev/null +++ b/ospfd/ospf_route.h @@ -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 index 0000000..31d7ce2 --- /dev/null +++ b/ospfd/ospf_routemap.c @@ -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 + +#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 index 0000000..1f9b851 --- /dev/null +++ b/ospfd/ospf_snmp.c @@ -0,0 +1,2743 @@ +/* OSPFv2 SNMP support + * Copyright (C) 2005 6WIND + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by 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 + +#ifdef HAVE_SNMP +#include +#include + +#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 index 0000000..413d1d7 --- /dev/null +++ b/ospfd/ospf_snmp.h @@ -0,0 +1,38 @@ +/* OSPFv2 SNMP support + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by 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_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 index 0000000..e1c290e --- /dev/null +++ b/ospfd/ospf_spf.c @@ -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 + +#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 index 0000000..e33b3e5 --- /dev/null +++ b/ospfd/ospf_spf.h @@ -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 index 0000000..47771a1 --- /dev/null +++ b/ospfd/ospf_te.c @@ -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 +#include + +#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 index 0000000..8bb77c4 --- /dev/null +++ b/ospfd/ospf_te.h @@ -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->||<-- 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 index 0000000..478d4ff --- /dev/null +++ b/ospfd/ospf_vty.c @@ -0,0 +1,7923 @@ +/* OSPF VTY interface. + * Copyright (C) 2005 6WIND + * 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 + +#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 + 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, " "); + 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 index 0000000..4610638 --- /dev/null +++ b/ospfd/ospf_vty.h @@ -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 index 0000000..60d9852 --- /dev/null +++ b/ospfd/ospf_zebra.c @@ -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 + +#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 index 0000000..32a0271 --- /dev/null +++ b/ospfd/ospf_zebra.h @@ -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 index 0000000..98d6d77 --- /dev/null +++ b/ospfd/ospfd.c @@ -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 + +#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 index 0000000..0e8ac67 --- /dev/null +++ b/ospfd/ospfd.conf.sample @@ -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 index 0000000..0315164 --- /dev/null +++ b/ospfd/ospfd.h @@ -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 +#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 index 0000000..51a2ac8 --- /dev/null +++ b/pimd/.gitignore @@ -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 index 0000000..f6135a4 --- /dev/null +++ b/pimd/AUTHORS @@ -0,0 +1,9 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +# Everton da Silva Marques +$ more ~/.gitconfig +[user] + name = Everton Marques + email = everton.marques@gmail.com + +-x- diff --git a/pimd/CAVEATS b/pimd/CAVEATS new file mode 100644 index 0000000..9f07bda --- /dev/null +++ b/pimd/CAVEATS @@ -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 index 0000000..425ac82 --- /dev/null +++ b/pimd/COMMANDS @@ -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 index 0000000..3912109 --- /dev/null +++ b/pimd/COPYING @@ -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. + + 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 + + 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. + + + Copyright (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., 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. + + , 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 index 0000000..72fb826 --- /dev/null +++ b/pimd/DEBUG @@ -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 index 0000000..e87e567 --- /dev/null +++ b/pimd/LINUX_KERNEL_MROUTE_MFC @@ -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 index 0000000..d045436 --- /dev/null +++ b/pimd/Makefile.am @@ -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 index 0000000..1e3f72c --- /dev/null +++ b/pimd/README @@ -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 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 + + # 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 + 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 index 0000000..7d1f52d --- /dev/null +++ b/pimd/TROUBLESHOOTING @@ -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 index 0000000..2e8c966 --- /dev/null +++ b/pimd/WHY_SSM @@ -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 index 0000000..ad21e08 --- /dev/null +++ b/pimd/pim_assert.c @@ -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 + +#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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); + pim_inet4_dump("", 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_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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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_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_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_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("", src_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", msg_source_addr.u.prefix4, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", 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("", 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("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 index 0000000..bd3fb3e --- /dev/null +++ b/pimd/pim_assert.h @@ -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 + +#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 index 0000000..749eeab --- /dev/null +++ b/pimd/pim_cmd.c @@ -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 + +#include + +#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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", 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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", 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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", 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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", 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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", 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_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("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_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("", 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("", 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->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", 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("", 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("", 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("", 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("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + 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("", 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("", 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("", 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("", 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("", 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("", 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("", 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("", 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("", 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("", 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("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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, "%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, "%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("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 : "", + c_oil->oil.mfcc_parent, + ifp_out ? ifp_out->name : "", + 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("", s_route->group, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 : "", + s_route->iif, + ifp_out ? ifp_out->name : "", + 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("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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.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 : "", + 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("", 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("", 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("", 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 index 0000000..a1cb581 --- /dev/null +++ b/pimd/pim_cmd.h @@ -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 index 0000000..bfc128b --- /dev/null +++ b/pimd/pim_hello.c @@ -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 + +#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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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 index 0000000..b5e272d --- /dev/null +++ b/pimd/pim_hello.h @@ -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 + +#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 index 0000000..ddad6cb --- /dev/null +++ b/pimd/pim_iface.c @@ -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 + +#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_prim_addr, new_prim_str, sizeof(new_prim_str)); + pim_inet4_dump("", 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_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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 index 0000000..8cad3d1 --- /dev/null +++ b/pimd/pim_iface.h @@ -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 + +#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 index 0000000..ad97879 --- /dev/null +++ b/pimd/pim_ifchannel.c @@ -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 + +#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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", 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, up_str, sizeof(up_str)); + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 index 0000000..e6f1c29 --- /dev/null +++ b/pimd/pim_ifchannel.h @@ -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 + +#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 index 0000000..ad3c7b5 --- /dev/null +++ b/pimd/pim_igmp.c @@ -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 + +#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("", 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("", 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("", 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("", 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_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("", 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("", 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_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, ""); + + 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_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("", ip_hdr->ip_src, from_str , sizeof(from_str)); + pim_inet4_dump("", 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("", 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("", 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("", igmp->ifaddr, querier_str, + sizeof(querier_str)); + pim_inet4_dump("", 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, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + 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, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + 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 : "", + 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_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_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_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_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_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 index 0000000..ab39615 --- /dev/null +++ b/pimd/pim_igmp.h @@ -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 + +#include +#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 index 0000000..042818a --- /dev/null +++ b/pimd/pim_igmp_join.c @@ -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 + +#include +#include +#include + +#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 index 0000000..67779ff --- /dev/null +++ b/pimd/pim_igmp_join.h @@ -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 +#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 index 0000000..180de9d --- /dev/null +++ b/pimd/pim_igmpv3.c @@ -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 +#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_str, sizeof(from_str)); + pim_inet4_dump("", 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_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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_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_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_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_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_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_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_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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", 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_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", 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_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", 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 index 0000000..bb7e926 --- /dev/null +++ b/pimd/pim_igmpv3.h @@ -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 +#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 index 0000000..2080751 --- /dev/null +++ b/pimd/pim_int.c @@ -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 + +#include +#include +#include + +#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 index 0000000..d64b103 --- /dev/null +++ b/pimd/pim_int.h @@ -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 + +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 index 0000000..9d8e001 --- /dev/null +++ b/pimd/pim_join.c @@ -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 + +#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_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, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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_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_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_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_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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_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_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", msg_upstream_addr.u.prefix4, + upstream_str, sizeof(upstream_str)); + pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", 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("", 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("", 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 index 0000000..37ec0f4 --- /dev/null +++ b/pimd/pim_join.h @@ -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 + +#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 index 0000000..3f56532 --- /dev/null +++ b/pimd/pim_macro.c @@ -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 + +#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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 index 0000000..472fa9b --- /dev/null +++ b/pimd/pim_macro.h @@ -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 + +#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 index 0000000..ea70a8f --- /dev/null +++ b/pimd/pim_main.c @@ -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 + +#include "log.h" +#include "privs.h" +#include "version.h" +#include +#include "command.h" +#include "thread.h" +#include + +#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 index 0000000..fa460e2 --- /dev/null +++ b/pimd/pim_mroute.c @@ -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 +#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 = ""; + } + ifp = pim_if_find_by_vif_index(msg->im_vif); + pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 : "", + 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 : "", + 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_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 index 0000000..125d190 --- /dev/null +++ b/pimd/pim_mroute.h @@ -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 +#ifdef HAVE_NETINET_IP_MROUTE_H +#include +#endif + +#define PIM_MROUTE_MIN_TTL (1) + +#if defined(HAVE_LINUX_MROUTE_H) +#include +#else +/* + Below: from +*/ + +#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 +*/ + +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 index 0000000..8ead7ce --- /dev/null +++ b/pimd/pim_msg.c @@ -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 + +#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 index 0000000..a884fc8 --- /dev/null +++ b/pimd/pim_msg.h @@ -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 + +/* + 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 index 0000000..8932dc3 --- /dev/null +++ b/pimd/pim_neighbor.c @@ -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 + +#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_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", 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("", 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("", 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("", 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("", 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("", 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("", 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->u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", neigh_addr, this_neigh_str, sizeof(this_neigh_str)); + pim_inet4_dump("", 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 index 0000000..5b2172d --- /dev/null +++ b/pimd/pim_neighbor.h @@ -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 + +#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 index 0000000..1aaece3 --- /dev/null +++ b/pimd/pim_oil.c @@ -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 + +#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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 index 0000000..1753545 --- /dev/null +++ b/pimd/pim_oil.h @@ -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 index 0000000..c52b0d3 --- /dev/null +++ b/pimd/pim_pim.c @@ -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 + +#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("", ip_hdr->ip_src, src_str, sizeof(src_str)); + pim_inet4_dump("", 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, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + 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, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + 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 : "", + 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_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_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("", 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 index 0000000..4b378fb --- /dev/null +++ b/pimd/pim_pim.h @@ -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 + +#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 index 0000000..9cc13be --- /dev/null +++ b/pimd/pim_routemap.c @@ -0,0 +1,32 @@ +/* PIM Route-map Code + * Copyright (C) 2016 Cumulus Networks + * + * 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 + +#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 index 0000000..e7619a5 --- /dev/null +++ b/pimd/pim_rpf.c @@ -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 + +#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_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_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_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_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_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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 : "", + 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 : "", + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); + /* 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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 index 0000000..9a48ea0 --- /dev/null +++ b/pimd/pim_rpf.h @@ -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 + +#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 index 0000000..3549331 --- /dev/null +++ b/pimd/pim_signals.c @@ -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 + +#include + +#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 index 0000000..62523c0 --- /dev/null +++ b/pimd/pim_signals.h @@ -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 index 0000000..ceae4f2 --- /dev/null +++ b/pimd/pim_sock.c @@ -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 +#include "pim_mroute.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + 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, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + 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("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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->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->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 index 0000000..622fb47 --- /dev/null +++ b/pimd/pim_sock.h @@ -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 + +#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 index 0000000..fe88eba --- /dev/null +++ b/pimd/pim_ssmpingd.c @@ -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 + +#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_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("", 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.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("", 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("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", 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 : "", + 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("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", 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 : "", + 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("", 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("", 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("", 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("", 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("", 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("", 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 index 0000000..4bef20b --- /dev/null +++ b/pimd/pim_ssmpingd.h @@ -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 + +#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 index 0000000..078beab --- /dev/null +++ b/pimd/pim_static.c @@ -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 + +#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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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 ("", sroute->group, gbuf, sizeof (gbuf)); + pim_inet4_dump ("", 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 index 0000000..b3be09e --- /dev/null +++ b/pimd/pim_static.h @@ -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 +#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 index 0000000..3a8353c --- /dev/null +++ b/pimd/pim_str.c @@ -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 + +#include +#include +#include + +#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 index 0000000..925f17f --- /dev/null +++ b/pimd/pim_str.h @@ -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 +#include +#include + +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 index 0000000..4e5832c --- /dev/null +++ b/pimd/pim_time.c @@ -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 + +#include +#include +#include + +#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 index 0000000..2984d9a --- /dev/null +++ b/pimd/pim_time.h @@ -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 + +#include +#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 index 0000000..ed4dbba --- /dev/null +++ b/pimd/pim_tlv.c @@ -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 + +#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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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("", 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_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_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("", tmp.u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", 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_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_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 index 0000000..b802cf8 --- /dev/null +++ b/pimd/pim_tlv.h @@ -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 + +#include "config.h" +#include "if.h" +#include "linklist.h" + +#ifdef HAVE_INTTYPES_H +#include +#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 index 0000000..a4d274a --- /dev/null +++ b/pimd/pim_upstream.c @@ -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 + +#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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", 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_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", 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 index 0000000..5b5182d --- /dev/null +++ b/pimd/pim_upstream.h @@ -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 + +#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 index 0000000..fdfed2b --- /dev/null +++ b/pimd/pim_util.c @@ -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 + +#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 index 0000000..a8613e2 --- /dev/null +++ b/pimd/pim_util.h @@ -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 + +#include + +#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 index 0000000..f3a5ee3 --- /dev/null +++ b/pimd/pim_version.c @@ -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 + +#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 index 0000000..ef9f370 --- /dev/null +++ b/pimd/pim_version.h @@ -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 index 0000000..56de83c --- /dev/null +++ b/pimd/pim_vty.c @@ -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 + +#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("", 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("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 index 0000000..904ee55 --- /dev/null +++ b/pimd/pim_vty.h @@ -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 index 0000000..efff100 --- /dev/null +++ b/pimd/pim_zebra.c @@ -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 + +#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("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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 : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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 : "", 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("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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 : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", 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_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_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("", 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_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_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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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_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_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", 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("", up->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", 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 index 0000000..af5baef --- /dev/null +++ b/pimd/pim_zebra.h @@ -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 index 0000000..770fbf7 --- /dev/null +++ b/pimd/pim_zlookup.c @@ -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 +#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_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_str, sizeof(addr_str)); + pim_inet4_dump("", 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_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_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_str, sizeof(addr_str)); + pim_inet4_dump("", 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_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_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_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_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_str, sizeof(addr_str)); + pim_inet4_dump("", 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_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 index 0000000..f2be6d4 --- /dev/null +++ b/pimd/pim_zlookup.h @@ -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 + +#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 index 0000000..97fb223 --- /dev/null +++ b/pimd/pimd.c @@ -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 + +#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 index 0000000..6753085 --- /dev/null +++ b/pimd/pimd.conf.sample @@ -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 index 0000000..9a7e605 --- /dev/null +++ b/pimd/pimd.h @@ -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 + +#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 index 0000000..81026ae --- /dev/null +++ b/pimd/test_igmpv3_join.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..63e9a66 --- /dev/null +++ b/pkgsrc/.gitignore @@ -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 index 0000000..622fbf0 --- /dev/null +++ b/pkgsrc/Makefile.am @@ -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 index 0000000..13ec449 --- /dev/null +++ b/pkgsrc/README.txt @@ -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 index 0000000..d234b54 --- /dev/null +++ b/pkgsrc/bgpd.sh.in @@ -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 index 0000000..3fbdb81 --- /dev/null +++ b/pkgsrc/ospf6d.sh.in @@ -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 index 0000000..daa2252 --- /dev/null +++ b/pkgsrc/ospfd.sh.in @@ -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 index 0000000..3157541 --- /dev/null +++ b/pkgsrc/ripd.sh.in @@ -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 index 0000000..d06ac90 --- /dev/null +++ b/pkgsrc/ripngd.sh.in @@ -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 index 0000000..c2f12a7 --- /dev/null +++ b/pkgsrc/zebra.sh.in @@ -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 index 0000000..dd5bf7c --- /dev/null +++ b/ports/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/Makefile b/ports/Makefile new file mode 100644 index 0000000..86f77bd --- /dev/null +++ b/ports/Makefile @@ -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 diff --git a/ports/README b/ports/README new file mode 100644 index 0000000..a650eaa --- /dev/null +++ b/ports/README @@ -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 index 0000000..dd5bf7c --- /dev/null +++ b/ports/files/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/files/md5 b/ports/files/md5 new file mode 100644 index 0000000..520c348 --- /dev/null +++ b/ports/files/md5 @@ -0,0 +1 @@ +MD5 (zebra-980224.tar.gz) = c6887645741200c43341156c168c7034 diff --git a/ports/pkg/.gitignore b/ports/pkg/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/ports/pkg/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/pkg/COMMENT b/ports/pkg/COMMENT new file mode 100644 index 0000000..53c55e3 --- /dev/null +++ b/ports/pkg/COMMENT @@ -0,0 +1 @@ +Zebra Routing protocol daemon diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR new file mode 100644 index 0000000..aeb1950 --- /dev/null +++ b/ports/pkg/DESCR @@ -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 index 0000000..ccc69eb --- /dev/null +++ b/ports/pkg/PLIST @@ -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 index 0000000..b133c52 --- /dev/null +++ b/qpb/.gitignore @@ -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 index 0000000..0fbda61 --- /dev/null +++ b/qpb/Makefile.am @@ -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 index 0000000..99ccd05 --- /dev/null +++ b/qpb/README.txt @@ -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 index 0000000..e3ebbc6 --- /dev/null +++ b/qpb/linear_allocator.h @@ -0,0 +1,207 @@ +/* + * linear_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 +#include +#include +#include + +/* + * 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 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 + * + * 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 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 + * + * 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 index 0000000..8323d3e --- /dev/null +++ b/qpb/qpb.proto @@ -0,0 +1,90 @@ +/* + * qpb.proto + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 index 0000000..4b4830a --- /dev/null +++ b/qpb/qpb_allocator.c @@ -0,0 +1,67 @@ +/* + * qpb_allocator.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 index 0000000..83ddf56 --- /dev/null +++ b/qpb/qpb_allocator.h @@ -0,0 +1,113 @@ +/* + * qpb_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 + +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 index 0000000..e399bd8 --- /dev/null +++ b/redhat/.gitignore @@ -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 index 0000000..b1d49ac --- /dev/null +++ b/redhat/Makefile.am @@ -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 index 0000000..3e8fa05 --- /dev/null +++ b/redhat/README.rpm_build.md @@ -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 index 0000000..e18511a --- /dev/null +++ b/redhat/bgpd.init @@ -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 index 0000000..ef24841 --- /dev/null +++ b/redhat/bgpd.service @@ -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 index 0000000..9e80530 --- /dev/null +++ b/redhat/isisd.init @@ -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 index 0000000..edb6eea --- /dev/null +++ b/redhat/isisd.service @@ -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 index 0000000..63f138c --- /dev/null +++ b/redhat/nhrpd.service @@ -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 index 0000000..4133b4a --- /dev/null +++ b/redhat/ospf6d.init @@ -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 index 0000000..b53b970 --- /dev/null +++ b/redhat/ospf6d.service @@ -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 index 0000000..d964f38 --- /dev/null +++ b/redhat/ospfd.init @@ -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 index 0000000..5d6c5bb --- /dev/null +++ b/redhat/ospfd.service @@ -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 index 0000000..49f9075 --- /dev/null +++ b/redhat/pimd.init @@ -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 index 0000000..d62fe64 --- /dev/null +++ b/redhat/pimd.service @@ -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 index 0000000..db82cfd --- /dev/null +++ b/redhat/quagga-filter-perl-requires.sh @@ -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 index 0000000..8974b64 --- /dev/null +++ b/redhat/quagga-tmpfs.conf @@ -0,0 +1 @@ +d /var/run/quagga 0755 quagga quagga diff --git a/redhat/quagga.logrotate b/redhat/quagga.logrotate new file mode 100644 index 0000000..afbd40c --- /dev/null +++ b/redhat/quagga.logrotate @@ -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 index 0000000..9a91ad8 --- /dev/null +++ b/redhat/quagga.pam @@ -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 index 0000000..3bf5b9d --- /dev/null +++ b/redhat/quagga.spec.in @@ -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 +# 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 +- Fix lint errors +- Make texi2html conditional, disable by default to avoid needing TeX + by default + +* Mon Feb 27 2017 Paul Jakma +- 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 +- add nhrpd + +* Thu Feb 11 2016 Paul Jakma +- 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 +- 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 +- buildreq updates +- add a default define for with_pimd + +* Mon Sep 12 2005 Paul Jakma +- 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 +- on package upgrade, implement careful, phased restart logic +- use gcc -rdynamic flag when linking for better backtraces + +* Wed Dec 22 2004 Andrew J. Schorr +- daemonv6_list should contain only IPv6 daemons + +* Wed Dec 22 2004 Andrew J. Schorr +- watchquagga added +- on upgrade, all daemons should be condrestart'ed +- on removal, all daemons should be stopped + +* Mon Nov 08 2004 Paul Jakma +- Use makeinfo --html to generate quagga.html + +* Sun Nov 07 2004 Paul Jakma +- Fix with_ipv6 set to 0 build + +* Sat Oct 23 2004 Paul Jakma +- Update to 0.97.2 + +* Sat Oct 23 2004 Andrew J. Schorr +- 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 +- Update to 0.97.0 + +* Wed Sep 15 2004 Paul Jakma +- build snmp support by default +- build irdp support +- build with shared libs +- devel subpackage for archives and headers + +* Thu Jan 08 2004 Paul Jakma +- 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 +- 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 +- Fix -devel package to include all files +- Sync to 0.96.4 + +* Tue Aug 12 2003 Paul Jakma +- Renamed to Quagga +- Sync to Quagga release 0.96 + +* Thu Mar 20 2003 Paul Jakma +- zebra privileges support + +* Tue Mar 18 2003 Paul Jakma +- 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 +- 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 +- 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 +- 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 +- 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 +- update to latest CVS +- timestamped crypt_seqnum patch +- oi->on_write_q fix + +* Mon Sep 30 2002 Paul Jakma +- 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 +- update to 0.93b + +* Wed Sep 11 2002 Paul Jakma +- update to latest CVS +- add "/sbin/ip route flush proto zebra" to zebra RH init on startup + +* Sat Aug 24 2002 Paul Jakma +- update to current CVS +- add OSPF point to multipoint patch +- add OSPF bugfixes +- add BGP hash optimisation patch + +* Fri Jun 14 2002 Paul Jakma +- 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 0.91a-6 +- Fix bug #51336 + +* Wed Aug 1 2001 Trond Eivind Glomsrød 0.91a-5 +- Use generic initscript strings instead of initscript specific + ( "Starting foo: " -> "Starting $prog:" ) + +* Fri Jul 27 2001 Elliot Lee 0.91a-4 +- Bump the release when rebuilding into the dist. + +* Tue Feb 6 2001 Tim Powers +- built for Powertools + +* Sun Feb 4 2001 Pekka Savola +- 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 index 0000000..caa0fff --- /dev/null +++ b/redhat/quagga.sysconfig @@ -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 index 0000000..9b412cb --- /dev/null +++ b/redhat/ripd.init @@ -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 index 0000000..ed7f922 --- /dev/null +++ b/redhat/ripd.service @@ -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 index 0000000..88f346f --- /dev/null +++ b/redhat/ripngd.init @@ -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 index 0000000..2519b31 --- /dev/null +++ b/redhat/ripngd.service @@ -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 index 0000000..dda3506 --- /dev/null +++ b/redhat/watchquagga.init @@ -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 index 0000000..4242b16 --- /dev/null +++ b/redhat/zebra.init @@ -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 index 0000000..f9107f1 --- /dev/null +++ b/redhat/zebra.service @@ -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 index 0000000..d57ea98 --- /dev/null +++ b/release.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +if [ $# -ne 2 ] ; then + echo "usage: $0 " + 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 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 + @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 index 0000000..9bcfb63 --- /dev/null +++ b/ripd/.gitignore @@ -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 index 0000000..571a499 --- /dev/null +++ b/ripd/Makefile.am @@ -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 index 0000000..6c92fb5 --- /dev/null +++ b/ripd/RIPv2-MIB.txt @@ -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 index 0000000..a267874 --- /dev/null +++ b/ripd/rip_debug.c @@ -0,0 +1,284 @@ +/* RIP debug routines + * 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 +#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 index 0000000..990ec90 --- /dev/null +++ b/ripd/rip_debug.h @@ -0,0 +1,53 @@ +/* RIP debug routines + * 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_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 index 0000000..7521fc7 --- /dev/null +++ b/ripd/rip_interface.c @@ -0,0 +1,2111 @@ +/* Interface related function for RIP. + * 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 + +#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 /, 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 /, 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 index 0000000..d9dfbb7 --- /dev/null +++ b/ripd/rip_interface.h @@ -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 index 0000000..4ead9b0 --- /dev/null +++ b/ripd/rip_main.c @@ -0,0 +1,321 @@ +/* RIPd 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 + +#include +#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 index 0000000..0155f90 --- /dev/null +++ b/ripd/rip_offset.c @@ -0,0 +1,413 @@ +/* RIP offset-list + * 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 + +#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 index 0000000..6a3add6 --- /dev/null +++ b/ripd/rip_peer.c @@ -0,0 +1,207 @@ +/* RIP peer support + * 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 + +#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 index 0000000..4e73694 --- /dev/null +++ b/ripd/rip_routemap.c @@ -0,0 +1,1113 @@ +/* RIPv2 routemap. + * Copyright (C) 2005 6WIND + * 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 + +#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 index 0000000..2c7cd2c --- /dev/null +++ b/ripd/rip_snmp.c @@ -0,0 +1,593 @@ +/* RIP SNMP support + * 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 + +#ifdef HAVE_SNMP +#include +#include + +#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 index 0000000..0b51af5 --- /dev/null +++ b/ripd/rip_zebra.c @@ -0,0 +1,762 @@ +/* RIPd and zebra interface. + * 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 + +#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 index 0000000..c073eca --- /dev/null +++ b/ripd/ripd.c @@ -0,0 +1,4143 @@ +/* RIP version 1 and 2. + * Copyright (C) 2005 6WIND + * 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 + +#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 /\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 /\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 index 0000000..2902ff9 --- /dev/null +++ b/ripd/ripd.conf.sample @@ -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 index 0000000..4f82525 --- /dev/null +++ b/ripd/ripd.h @@ -0,0 +1,449 @@ +/* RIP related values and structures. + * 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_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 index 0000000..e871fae --- /dev/null +++ b/ripngd/.gitignore @@ -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 index 0000000..df0f7d3 --- /dev/null +++ b/ripngd/Makefile.am @@ -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 index 0000000..eb8ff5d --- /dev/null +++ b/ripngd/ripng_debug.c @@ -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 +#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 index 0000000..674345c --- /dev/null +++ b/ripngd/ripng_debug.h @@ -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 index 0000000..1d3757a --- /dev/null +++ b/ripngd/ripng_interface.c @@ -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 + +#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 index 0000000..1c184e2 --- /dev/null +++ b/ripngd/ripng_main.c @@ -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 + +#include +#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 index 0000000..b966af0 --- /dev/null +++ b/ripngd/ripng_nexthop.c @@ -0,0 +1,214 @@ +/* RIPngd Zebra + * Copyright (C) 2002 6WIND + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU 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 + +/* For struct udphdr. */ +#include + +#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 index 0000000..19bd32b --- /dev/null +++ b/ripngd/ripng_nexthop.h @@ -0,0 +1,64 @@ +/* RIPng nexthop support + * Copyright (C) 6WIND Vincent Jardin + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU 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 +#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 index 0000000..5bc2568 --- /dev/null +++ b/ripngd/ripng_offset.c @@ -0,0 +1,421 @@ +/* RIPng offset-list + * 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. + */ + + /* RIPng support by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#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 index 0000000..b12e146 --- /dev/null +++ b/ripngd/ripng_peer.c @@ -0,0 +1,216 @@ +/* RIPng peer support + * 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. + */ + +/* RIPng support added by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#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 index 0000000..f26302e --- /dev/null +++ b/ripngd/ripng_route.c @@ -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 + +#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 index 0000000..9ff90aa --- /dev/null +++ b/ripngd/ripng_route.h @@ -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 index 0000000..c596aec --- /dev/null +++ b/ripngd/ripng_routemap.c @@ -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 + +#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 index 0000000..a35bc99 --- /dev/null +++ b/ripngd/ripng_zebra.c @@ -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 + +#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 index 0000000..824b3a4 --- /dev/null +++ b/ripngd/ripngd.c @@ -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 + +#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 index 0000000..ad673e5 --- /dev/null +++ b/ripngd/ripngd.conf.sample @@ -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 index 0000000..a0c6a4e --- /dev/null +++ b/ripngd/ripngd.h @@ -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 +#include + +/* 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 index 0000000..63afe3f --- /dev/null +++ b/solaris/.gitignore @@ -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 index 0000000..dcee240 --- /dev/null +++ b/solaris/Makefile.am @@ -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 index 0000000..811e9d0 --- /dev/null +++ b/solaris/README.txt @@ -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:: + + svcs -l svc:/network/routing/quagga:zebra + svcs -l svc:/network/routing/quagga:ospfd + + + or typically just with the shortcut of 'quagga:' or even + : + + 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 index 0000000..3430e8c --- /dev/null +++ b/solaris/depend.daemons.in @@ -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 index 0000000..8f23482 --- /dev/null +++ b/solaris/depend.dev.in @@ -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 index 0000000..b337929 --- /dev/null +++ b/solaris/depend.doc.in @@ -0,0 +1 @@ +P SUNWdoc Documentation Tools diff --git a/solaris/depend.libs.in b/solaris/depend.libs.in new file mode 100644 index 0000000..4185977 --- /dev/null +++ b/solaris/depend.libs.in @@ -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 index 0000000..6d928d2 --- /dev/null +++ b/solaris/depend.smf.in @@ -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 index 0000000..cab0e3c --- /dev/null +++ b/solaris/pkginfo.daemons.tmpl.in @@ -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 index 0000000..9c5d23e --- /dev/null +++ b/solaris/pkginfo.dev.tmpl.in @@ -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 index 0000000..809ec77 --- /dev/null +++ b/solaris/pkginfo.doc.tmpl.in @@ -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 index 0000000..42adc6e --- /dev/null +++ b/solaris/pkginfo.libs.tmpl.in @@ -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 index 0000000..4aa0393 --- /dev/null +++ b/solaris/pkginfo.smf.tmpl.in @@ -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 index 0000000..2dd27fd --- /dev/null +++ b/solaris/pkginfo.tmpl.in @@ -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 index 0000000..ce65d5e --- /dev/null +++ b/solaris/prototype.daemons.in @@ -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 index 0000000..2ad937b --- /dev/null +++ b/solaris/prototype.dev.in @@ -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 index 0000000..42b076d --- /dev/null +++ b/solaris/prototype.doc.in @@ -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 index 0000000..71f4fd5 --- /dev/null +++ b/solaris/prototype.libs.in @@ -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 index 0000000..3c80f39 --- /dev/null +++ b/solaris/prototype.smf.in @@ -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 index 0000000..ed3350e --- /dev/null +++ b/solaris/quagga.init.in @@ -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 "; + else + echo "Usage: $0 "; + fi + echo "The --pid_file argument is implied"; + echo "This help message: $0 "; +} + +# 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 index 0000000..60427b0 --- /dev/null +++ b/solaris/quagga.xml.in @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..3002b27 --- /dev/null +++ b/tests/.gitignore @@ -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 index 0000000..b13e903 --- /dev/null +++ b/tests/Makefile.am @@ -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 index 0000000..5a0899e --- /dev/null +++ b/tests/aspath_test.c @@ -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 + +#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,)", + { 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", + "", + {}, + 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 index 0000000..a381351 --- /dev/null +++ b/tests/bgp_capability_test.c @@ -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 + +#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 index 0000000..3b1bf14 --- /dev/null +++ b/tests/bgp_mp_attr_test.c @@ -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 + +#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 index 0000000..174d299 --- /dev/null +++ b/tests/bgp_mpath_test.c @@ -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 + +#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 index 0000000..5900186 --- /dev/null +++ b/tests/bgpd.tests/Makefile.am @@ -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 index 0000000..dfecec7 --- /dev/null +++ b/tests/bgpd.tests/aspathtest.exp @@ -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 index 0000000..074952f --- /dev/null +++ b/tests/bgpd.tests/ecommtest.exp @@ -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 index 0000000..2572623 --- /dev/null +++ b/tests/bgpd.tests/testbgpcap.exp @@ -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 index 0000000..96a51e3 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpath.exp @@ -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 index 0000000..e6d7305 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpattr.exp @@ -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 index 0000000..7135856 --- /dev/null +++ b/tests/common-cli.c @@ -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 + +#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 index 0000000..8f67515 --- /dev/null +++ b/tests/common-cli.h @@ -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 index 0000000..2f6bcea --- /dev/null +++ b/tests/config/unix.exp @@ -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 index 0000000..23ee405 --- /dev/null +++ b/tests/ecommunity_test.c @@ -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 + +#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 index 0000000..e69de29 diff --git a/tests/heavy-thread.c b/tests/heavy-thread.c new file mode 100644 index 0000000..c2e71c1 --- /dev/null +++ b/tests/heavy-thread.c @@ -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 +#include + +#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 index 0000000..2f133cc --- /dev/null +++ b/tests/heavy-wq.c @@ -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 + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" +#include "workqueue.h" +#include + +#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 index 0000000..9af46c8 --- /dev/null +++ b/tests/heavy.c @@ -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 + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include + +#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 index 0000000..e69de29 diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp new file mode 100644 index 0000000..e69de29 diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am new file mode 100644 index 0000000..4b74e2d --- /dev/null +++ b/tests/libzebra.tests/Makefile.am @@ -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 index 0000000..5838d4f --- /dev/null +++ b/tests/libzebra.tests/tabletest.exp @@ -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 index 0000000..83531c7 --- /dev/null +++ b/tests/libzebra.tests/test-timer-correctness.exp @@ -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 index 0000000..778bd0c --- /dev/null +++ b/tests/libzebra.tests/testcli.exp @@ -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 index 0000000..d4bfc82 --- /dev/null +++ b/tests/libzebra.tests/testcommands.exp @@ -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 index 0000000..be35a0a --- /dev/null +++ b/tests/libzebra.tests/testnexthopiter.exp @@ -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 index 0000000..ca602e3 --- /dev/null +++ b/tests/libzebra.tests/teststream.exp @@ -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 index 0000000..5396c7d --- /dev/null +++ b/tests/main.c @@ -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 + +#include +#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 index 0000000..bdcfb07 --- /dev/null +++ b/tests/prng.c @@ -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 + +#include +#include +#include + +#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 index 0000000..cf0bacc --- /dev/null +++ b/tests/prng.h @@ -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 index 0000000..fc9cc3d --- /dev/null +++ b/tests/table_test.c @@ -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 + +#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 ("\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 index 0000000..e95d6fb --- /dev/null +++ b/tests/test-buffer.c @@ -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 +#include +#include + +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 \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 index 0000000..b6741f3 --- /dev/null +++ b/tests/test-checksum.c @@ -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 +#include +#include + +#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 index 0000000..6fab6d5 --- /dev/null +++ b/tests/test-cli.c @@ -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 + +#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 index 0000000..18b3b50 --- /dev/null +++ b/tests/test-commands.c @@ -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 + +#include +#include +#include + +#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 ] [-n ] [-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 index 0000000..807249e --- /dev/null +++ b/tests/test-memory.c @@ -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 +#include + +/* 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 index 0000000..2503793 --- /dev/null +++ b/tests/test-nexthop-iter.c @@ -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 +#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 index 0000000..beae81f --- /dev/null +++ b/tests/test-privs.c @@ -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 + +#include +#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 index 0000000..1b851fc --- /dev/null +++ b/tests/test-segv.c @@ -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 +#include +#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 index 0000000..f24da96 --- /dev/null +++ b/tests/test-sig.c @@ -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 +#include +#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 index 0000000..7ef6374 --- /dev/null +++ b/tests/test-stream.c @@ -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 +#include +#include + +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 index 0000000..e523929 --- /dev/null +++ b/tests/test-timer-correctness.c @@ -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 + +#include +#include + +#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 index 0000000..ee45ede --- /dev/null +++ b/tests/test-timer-performance.c @@ -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 + +#include +#include + +#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 index 0000000..f4212b9 --- /dev/null +++ b/tests/testcli.in @@ -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 index 0000000..1515ea2 --- /dev/null +++ b/tests/testcli.refout @@ -0,0 +1,290 @@ +test# echo this is a test message +this is a test message +test# echo foo bla + MESSAGE The message to echo + +test# echo foo bla baz +foo bla baz +test# echo +% Command incomplete. +test# +test# arg ipv4 1.2.3.4 +cmd0 with 1 args. +[00]: 1.2.3.4 +test# arg ipv4 1.2. + A.B.C.D 02 +test# arg ipv4 1.2.3.4 +cmd0 with 1 args. +[00]: 1.2.3.4 +test# arg ipv4 1.2.3 +cmd0 with 1 args. +[00]: 1.2.3 +test# arg ipv4 1.2.3.4.5 +% [NONE] Unknown command: arg ipv4 1.2.3.4.5 +test# arg ipv4 1.a.3.4 +% [NONE] Unknown command: arg ipv4 1.a.3.4 +test# arg ipv4 blah +% [NONE] Unknown command: arg ipv4 blah +test# +test# arg ipv4m 1.2.3.0/24 +cmd1 with 1 args. +[00]: 1.2.3.0/24 +test# arg ipv4m 1.2. + A.B.C.D/M 02 +test# arg ipv4m 1.2.3.0/24 +cmd1 with 1 args. +[00]: 1.2.3.0/24 +test# arg ipv4m 1.2.3/9 +% [NONE] Unknown command: arg ipv4m 1.2.3/9 +test# arg ipv4m 1.2.3.4.5/6 +% [NONE] Unknown command: arg ipv4m 1.2.3.4.5/6 +test# arg ipv4m 1.a.3.4 +% [NONE] Unknown command: arg ipv4m 1.a.3.4 +test# arg ipv4m blah +% [NONE] Unknown command: arg ipv4m blah +test# arg ipv4m 1.2.3.0/999 +% [NONE] Unknown command: arg ipv4m 1.2.3.0/999 +test# arg ipv4m 1.2.3.0/a9 +% [NONE] Unknown command: arg ipv4m 1.2.3.0/a9 +test# arg ipv4m 1.2.3.0/9a +% [NONE] Unknown command: arg ipv4m 1.2.3.0/9a +test# +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b3 +% There is no matched command. +test# arg ipv6 de4d:b33f::caf + X:X::X:X 02 +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b3 +test# arg ipv6 de4d:b33f::caf + X:X::X:X 02 +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b33f:z::cafe +% [NONE] Unknown command: arg ipv6 de4d:b33f:z::cafe +test# arg ipv6 de4d:b33f:cafe: +% [NONE] Unknown command: arg ipv6 de4d:b33f:cafe: +test# arg ipv6 :: +cmd2 with 1 args. +[00]: :: +test# arg ipv6 ::/ +% [NONE] Unknown command: arg ipv6 ::/ +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 +% [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 +test# arg ipv6 12::34::56 +% [NONE] Unknown command: arg ipv6 12::34::56 +test# arg ipv6m dead:beef:cafe::/64 +cmd3 with 1 args. +[00]: dead:beef:cafe::/64 +test# arg ipv6m dead:be + X:X::X:X/M 02 +test# arg ipv6m dead:beef:cafe: + X:X::X:X/M 02 +test# arg ipv6m dead:beef:cafe::/64 +cmd3 with 1 args. +[00]: dead:beef:cafe::/64 +test# +test# arg range 4 +% [NONE] Unknown command: arg range 4 +test# arg range 5 +cmd4 with 1 args. +[00]: 5 +test# arg range 9 + <5-15> 02 +test# arg range 9 +cmd4 with 1 args. +[00]: 9 +test# arg range 15 +cmd4 with 1 args. +[00]: 15 +test# arg range 16 +% [NONE] Unknown command: arg range 16 +test# arg range -1 +% [NONE] Unknown command: arg range -1 +test# arg range 99999999999999999999999999999999999999999 +% [NONE] Unknown command: arg range 99999999999999999999999999999999999999999 +test# +test# arg + ipv4 01 + ipv4m 01 + ipv6 01 + ipv6m 01 + range 01 +test# arg +% Command incomplete. +test# +test# pa +test# papat +% Command incomplete. +test# pat +a b c d e f +test# pat +% Command incomplete. +test# +test# pat a +% Command incomplete. +test# pat a a +cmd5 with 1 args. +[00]: a +test# pat a + a 02 + b 03 +test# pat a b +cmd5 with 1 args. +[00]: b +test# pat a c +% There is no matched command. +test# pat a c +% [NONE] Unknown command: pat a c +test# pat a a x +% [NONE] Unknown command: pat a a x +test# +test# pat b +% Command incomplete. +test# pat b + a 02 +test# pat b a +cmd6 with 1 args. +[00]: a +test# pat b x +% [NONE] Unknown command: pat b x +test# pat b x y +% [NONE] Unknown command: pat b x y +test# +test# pat c a +% Command incomplete. +test# pat c a 1.2.3.4 +cmd7 with 2 args. +[00]: a +[01]: 1.2.3.4 +test# pat c b 2.3.4 +cmd7 with 2 args. +[00]: b +[01]: 2.3.4 +test# pat c c + A.B.C.D 05 +test# pat c c x +% [NONE] Unknown command: pat c c x +test# +test# pat d +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: (null) +test# pat d +bar baz foo +test# pat d +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: (null) +test# pat d foo 1.2.3.4 +cmd8 with 3 args. +[00]: 1.2.3.4 +[01]: (null) +[02]: (null) +test# pat d foo +% Command incomplete. +test# pat d noooo +% [NONE] Unknown command: pat d noooo +test# pat d bar 1::2 +cmd8 with 3 args. +[00]: (null) +[01]: 1::2 +[02]: (null) +test# pat d bar 1::2 foo 3.4.5.6 +cmd8 with 3 args. +[00]: 3.4.5.6 +[01]: 1::2 +[02]: (null) +test# pat d ba + bar 04 + baz 06 +test# pat d baz +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: baz +test# pat d foo 3.4.5.6 baz +cmd8 with 3 args. +[00]: 3.4.5.6 +[01]: (null) +[02]: baz +test# +test# pat e +% Command incomplete. +test# pat e f +% Command incomplete. +test# pat e f g +% Command incomplete. +test# pat e 1.2.3.4 +% Command incomplete. +test# +test# pat f +cmd10 with 0 args. +test# pat f foo +cmd10 with 1 args. +[00]: foo +test# pat f key +cmd10 with 1 args. +[00]: key +test# +test# alt a +test# alt a a + WORD 02 +test# alt a ab +cmd11 with 1 args. +[00]: ab +test# alt a 1 +test# alt a 1.2 + A.B.C.D 02 + WORD 02 +test# alt a 1.2.3.4 +cmd12 with 1 args. +[00]: 1.2.3.4 +test# alt a 1 +test# alt a 1:2 + WORD 02 +test# alt a 1:2 +test# alt a 1:2:: + WORD 02 + X:X::X:X 02 +test# alt a 1:2::3 +cmd13 with 1 args. +[00]: 1:2::3 +test# +test# conf t +test(config)# do pat d baz +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: baz +test(config)# exit +test# +test# show run + +Current configuration: +! +hostname test +! +line vty +! +end +test# conf t +test(config)# hostname foohost +foohost(config)# do show run + +Current configuration: +! +hostname foohost +! +line vty +! +end +foohost(config)# +end. diff --git a/tests/testcommands.in b/tests/testcommands.in new file mode 100644 index 0000000..7fe6279 --- /dev/null +++ b/tests/testcommands.in @@ -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 index 0000000..11483b8 --- /dev/null +++ b/tests/testcommands.refout @@ -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 index 0000000..a528e55 --- /dev/null +++ b/tests/tests.h @@ -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 index 0000000..dd5bf7c --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/tools/mrlg.txt b/tools/mrlg.txt new file mode 100644 index 0000000..0ebf7ee --- /dev/null +++ b/tools/mrlg.txt @@ -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 index 0000000..c5668e1 --- /dev/null +++ b/tools/multiple-bgpd.sh @@ -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 index 0000000..01ff09f --- /dev/null +++ b/tools/zebra.el @@ -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 index 0000000..d5db16d --- /dev/null +++ b/update-autotools @@ -0,0 +1,28 @@ +#! /bin/sh +# +# When local system does not have the latest autoconf/automake +# -- Kunihiro Ishiguro +# + +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 index 0000000..5856eac --- /dev/null +++ b/vtysh/.gitignore @@ -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 index 0000000..983103f --- /dev/null +++ b/vtysh/Makefile.am @@ -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 index 0000000..6e24b77 --- /dev/null +++ b/vtysh/extract.pl.in @@ -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 < +#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 = ; + 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 < + +#include +#include +#include +#include +#include + +#include +#include + +#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 index 0000000..4e0a2be --- /dev/null +++ b/vtysh/vtysh.conf.sample @@ -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 index 0000000..02ff7fd --- /dev/null +++ b/vtysh/vtysh.h @@ -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 index 0000000..2834ef4 --- /dev/null +++ b/vtysh/vtysh_config.c @@ -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 + +#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 index 0000000..e82771b --- /dev/null +++ b/vtysh/vtysh_main.c @@ -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 + +#include +#include +#include +#include + +#include +#include + +#include +#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 index 0000000..584b61f --- /dev/null +++ b/vtysh/vtysh_user.c @@ -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 +#include + +#include + +#ifdef USE_PAM +#include +#ifdef HAVE_PAM_MISC_H +#include +#endif +#ifdef HAVE_OPENPAM_H +#include +#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 index 0000000..a6c8b99 --- /dev/null +++ b/vtysh/vtysh_user.h @@ -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 index 0000000..b6226d5 --- /dev/null +++ b/watchquagga/.gitignore @@ -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 index 0000000..1f05f26 --- /dev/null +++ b/watchquagga/Makefile.am @@ -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 index 0000000..9bd7a5f --- /dev/null +++ b/watchquagga/watchquagga.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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...] ...\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 index 0000000..df703b6 --- /dev/null +++ b/zebra/.gitignore @@ -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 index 0000000..96bcec5 --- /dev/null +++ b/zebra/GNOME-PRODUCT-ZEBRA-MIB @@ -0,0 +1,78 @@ +GNOME-PRODUCT-ZEBRA-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, + OBJECT-IDENTITY + FROM SNMPv2-SMI + gnomeProducts + FROM GNOME-SMI; + +zebra MODULE-IDENTITY + LAST-UPDATED "200004250000Z" + ORGANIZATION "GNOME project" + CONTACT-INFO + "GNU Network Object Model Environment project + + see http://www.gnome.org for contact persons of a particular + area or subproject of GNOME. + + Administrative contact for MIB module: + + Jochen Friedrich + Wingertstr. 70/1 + 68809 Neulussheim + Germany + + email: snmp@gnome.org" + DESCRIPTION + "The product registrations for the various zebra subdeamons. + These registrations are guaranteed to be unique and are used + for SMUX registration by default (if not overridden manually)." + ::= { gnomeProducts 2 } + +zserv OBJECT-IDENTITY + STATUS current + DESCRIPTION + "zserv is part of the zebra project which again is a GNU + endorsed internet routing program. + zserv is the main zebra process which implements routing + entries with the kernel and handles routing updates between + other routing protocols." + ::= { zebra 1 } + +bgpd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "bgpd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 2 } + +ripd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ripd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 3 } + +ripngd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ripngd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 4 } + +ospfd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ospfd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 5 } + +ospf6d OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ospf6d is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 6 } + +END diff --git a/zebra/GNOME-SMI b/zebra/GNOME-SMI new file mode 100644 index 0000000..164732b --- /dev/null +++ b/zebra/GNOME-SMI @@ -0,0 +1,53 @@ +GNOME-SMI DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, + OBJECT-IDENTITY, + enterprises + FROM SNMPv2-SMI; + +gnome MODULE-IDENTITY + LAST-UPDATED "9809010000Z" + ORGANIZATION "GNOME project" + CONTACT-INFO + "GNU Network Object Model Environment project + + see http://www.gnome.org for contact persons of a particular + area or subproject of GNOME. + + Administrative contact for MIB module: + + Jochen Friedrich + Wingertstr. 70/1 + 68809 Neulussheim + Germany + + email: snmp@gnome.org" + DESCRIPTION + "The Structure of GNOME." + ::= { enterprises 3317 } -- assigned by IANA + +gnomeProducts OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeProducts is the root OBJECT IDENTIFIER from + which sysObjectID values are assigned." + ::= { gnome 1 } + +gnomeMgmt OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeMgmt defines the subtree for production GNOME related + MIB registrations." + ::= { gnome 2 } + +gnomeTest OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeTest defines the subtree for testing GNOME related + MIB registrations." + ::= { gnome 3 } + +-- more to come if necessary. + +END diff --git a/zebra/Makefile.am b/zebra/Makefile.am new file mode 100644 index 0000000..b23f9f1 --- /dev/null +++ b/zebra/Makefile.am @@ -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 index 0000000..75c8867 --- /dev/null +++ b/zebra/client_main.c @@ -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 + +#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 index 0000000..1980007 --- /dev/null +++ b/zebra/connected.c @@ -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 + +#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 index 0000000..9595ddb --- /dev/null +++ b/zebra/connected.h @@ -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 index 0000000..7d023ce --- /dev/null +++ b/zebra/debug.c @@ -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 +#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 index 0000000..0fb4dd9 --- /dev/null +++ b/zebra/debug.h @@ -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 index 0000000..99328a1 --- /dev/null +++ b/zebra/if_ioctl.c @@ -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 + +#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 + +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 index 0000000..b399812 --- /dev/null +++ b/zebra/if_ioctl_solaris.c @@ -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 + +#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: + * : + */ + 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 index 0000000..245b7b2 --- /dev/null +++ b/zebra/if_netlink.c @@ -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 + +#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 index 0000000..bb48f61 --- /dev/null +++ b/zebra/if_sysctl.c @@ -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 + +#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 index 0000000..f8b946f --- /dev/null +++ b/zebra/interface.c @@ -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 + +#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 index 0000000..223caf8 --- /dev/null +++ b/zebra/interface.h @@ -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 index 0000000..e1ee429 --- /dev/null +++ b/zebra/ioctl.c @@ -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 + +#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 +#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 index 0000000..fee9b72 --- /dev/null +++ b/zebra/ioctl.h @@ -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 index 0000000..5a8be99 --- /dev/null +++ b/zebra/ioctl_null.c @@ -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 + +#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 index 0000000..b5bf1cc --- /dev/null +++ b/zebra/ioctl_solaris.c @@ -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 + +#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 index 0000000..188986b --- /dev/null +++ b/zebra/ioctl_solaris.h @@ -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 index 0000000..8a935c1 --- /dev/null +++ b/zebra/ipforward.h @@ -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 index 0000000..2876ede --- /dev/null +++ b/zebra/ipforward_proc.c @@ -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 + +#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 index 0000000..4aa1b79 --- /dev/null +++ b/zebra/ipforward_solaris.c @@ -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 +#include "log.h" +#include "prefix.h" + +#include "privs.h" +#include "zebra/ipforward.h" + +/* +** Solaris should define IP_DEV_NAME in , but we'll save +** configure.in changes for another day. We can use the same device +** for both IPv4 and IPv6. +*/ +/* #include */ +#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 index 0000000..57ed185 --- /dev/null +++ b/zebra/ipforward_sysctl.c @@ -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 +#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 index 0000000..9ce55e5 --- /dev/null +++ b/zebra/irdp.h @@ -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 index 0000000..43c63a8 --- /dev/null +++ b/zebra/irdp_interface.c @@ -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 + +#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 +#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 index 0000000..cf78a54 --- /dev/null +++ b/zebra/irdp_main.c @@ -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 + +#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 + +#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 index 0000000..0d31050 --- /dev/null +++ b/zebra/irdp_packet.c @@ -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 + + +#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 +#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 index 0000000..23b2153 --- /dev/null +++ b/zebra/kernel_netlink.c @@ -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 index 0000000..1a16a75 --- /dev/null +++ b/zebra/kernel_null.c @@ -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 +#include + +#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 index 0000000..64c6cbb --- /dev/null +++ b/zebra/kernel_socket.c @@ -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 +#include + +#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 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 index 0000000..e9558ad --- /dev/null +++ b/zebra/kernel_socket.h @@ -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 index 0000000..35cb159 --- /dev/null +++ b/zebra/main.c @@ -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 + +#include +#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 index 0000000..18977d2 --- /dev/null +++ b/zebra/misc_null.c @@ -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 + +#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 index 0000000..a7a6b25 --- /dev/null +++ b/zebra/redistribute.c @@ -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 + +#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 index 0000000..ce84009 --- /dev/null +++ b/zebra/redistribute.h @@ -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 index 0000000..5584d12 --- /dev/null +++ b/zebra/redistribute_null.c @@ -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 +#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 index 0000000..0191f57 --- /dev/null +++ b/zebra/rib.h @@ -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 index 0000000..a4eb73a --- /dev/null +++ b/zebra/router-id.c @@ -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 + +#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 index 0000000..46d300e --- /dev/null +++ b/zebra/router-id.h @@ -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 + +#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 index 0000000..8c1c476 --- /dev/null +++ b/zebra/rt.h @@ -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 index 0000000..fc6e373 --- /dev/null +++ b/zebra/rt_netlink.c @@ -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 +#include + +/* 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, */ +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, */ +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 index 0000000..9fc7001 --- /dev/null +++ b/zebra/rt_netlink.h @@ -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 index 0000000..9dd4582 --- /dev/null +++ b/zebra/rt_socket.c @@ -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 + +#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 index 0000000..2f62714 --- /dev/null +++ b/zebra/rtadv.c @@ -0,0 +1,1795 @@ +/* Router advertisement + * Copyright (C) 2005 6WIND + * 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 + +#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 +#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 index 0000000..160814b --- /dev/null +++ b/zebra/rtadv.h @@ -0,0 +1,107 @@ +/* Router advertisement + * Copyright (C) 2005 6WIND + * 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 index 0000000..e1ec670 --- /dev/null +++ b/zebra/rtread_getmsg.c @@ -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 + +#include "prefix.h" +#include "log.h" +#include "if.h" +#include "vrf.h" +#include "vty.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" + +#include +#include + +/* Solaris defines these in both and , sigh */ +#ifdef SUNOS_5 +#include +#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 +#include +#include + +/* 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 . "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 index 0000000..1f65864 --- /dev/null +++ b/zebra/rtread_netlink.c @@ -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 + +#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 index 0000000..385e150 --- /dev/null +++ b/zebra/rtread_sysctl.c @@ -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 + +#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 index 0000000..09f53ad --- /dev/null +++ b/zebra/test_main.c @@ -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 + +#include +#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 index 0000000..0df7dc2 --- /dev/null +++ b/zebra/testrib.conf @@ -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 index 0000000..a5d0732 --- /dev/null +++ b/zebra/zebra.conf.sample @@ -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 index 0000000..22fc6ca --- /dev/null +++ b/zebra/zebra_fpm.c @@ -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 + +#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 index 0000000..ecd23c7 --- /dev/null +++ b/zebra/zebra_fpm.h @@ -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 index 0000000..bd171c8 --- /dev/null +++ b/zebra/zebra_fpm_dt.c @@ -0,0 +1,275 @@ +/* + * zebra_fpm_dt.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU 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 +#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 index 0000000..175d351 --- /dev/null +++ b/zebra/zebra_fpm_netlink.c @@ -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 + +#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 ""; + + 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 ""; + } +} + +/* + * 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 ""; + + 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 index 0000000..1c4fd4c --- /dev/null +++ b/zebra/zebra_fpm_private.h @@ -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 index 0000000..beef310 --- /dev/null +++ b/zebra/zebra_fpm_protobuf.c @@ -0,0 +1,311 @@ +/* + * zebra_fpm_protobuf.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 + +#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 index 0000000..9ca0290 --- /dev/null +++ b/zebra/zebra_rib.c @@ -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 + +#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 index 0000000..859b6d7 --- /dev/null +++ b/zebra/zebra_rnh.c @@ -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 + +#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 index 0000000..574c95f --- /dev/null +++ b/zebra/zebra_rnh.h @@ -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 index 0000000..664667d --- /dev/null +++ b/zebra/zebra_rnh_null.c @@ -0,0 +1,10 @@ +#include +#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 index 0000000..5a6a96b --- /dev/null +++ b/zebra/zebra_routemap.c @@ -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 + +#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 index 0000000..3d005aa --- /dev/null +++ b/zebra/zebra_snmp.c @@ -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 + +#ifdef HAVE_SNMP +#include +#include + +#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 index 0000000..0c08990 --- /dev/null +++ b/zebra/zebra_vty.c @@ -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 + +#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 /, 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 /, 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 /, 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 /, 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 /, 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 /, 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", + 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 + +#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 [|