summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--.git-blame-ignore-revs1
-rw-r--r--babeld/babel_interface.c9
-rw-r--r--babeld/message.c3
-rw-r--r--babeld/route.c12
-rw-r--r--bfdd/bfd.c161
-rw-r--r--bfdd/bfd.h25
-rw-r--r--bfdd/bfd_packet.c39
-rw-r--r--bfdd/bfdctl.h4
-rw-r--r--bfdd/bfdd_cli.c123
-rw-r--r--bfdd/bfdd_nb.c37
-rw-r--r--bfdd/bfdd_nb.h13
-rw-r--r--bfdd/bfdd_nb_config.c156
-rw-r--r--bfdd/bfdd_vty.c26
-rw-r--r--bfdd/ptm_adapter.c17
-rw-r--r--bgpd/bgp_aspath.c3
-rw-r--r--bgpd/bgp_attr.c65
-rw-r--r--bgpd/bgp_attr.h34
-rw-r--r--bgpd/bgp_attr_evpn.c52
-rw-r--r--bgpd/bgp_attr_evpn.h24
-rw-r--r--bgpd/bgp_bfd.c17
-rw-r--r--bgpd/bgp_bmp.c9
-rw-r--r--bgpd/bgp_damp.c6
-rw-r--r--bgpd/bgp_debug.c79
-rw-r--r--bgpd/bgp_debug.h10
-rw-r--r--bgpd/bgp_ecommunity.c51
-rw-r--r--bgpd/bgp_ecommunity.h8
-rw-r--r--bgpd/bgp_evpn.c1826
-rw-r--r--bgpd/bgp_evpn.h6
-rw-r--r--bgpd/bgp_evpn_mh.c2905
-rw-r--r--bgpd/bgp_evpn_mh.h299
-rw-r--r--bgpd/bgp_evpn_private.h147
-rw-r--r--bgpd/bgp_evpn_vty.c324
-rw-r--r--bgpd/bgp_flowspec_vty.c7
-rw-r--r--bgpd/bgp_fsm.c15
-rw-r--r--bgpd/bgp_label.c3
-rw-r--r--bgpd/bgp_main.c43
-rw-r--r--bgpd/bgp_memory.c5
-rw-r--r--bgpd/bgp_memory.h3
-rw-r--r--bgpd/bgp_mpath.c3
-rw-r--r--bgpd/bgp_mplsvpn.c3
-rw-r--r--bgpd/bgp_network.c6
-rw-r--r--bgpd/bgp_nht.c17
-rw-r--r--bgpd/bgp_open.c39
-rw-r--r--bgpd/bgp_packet.c31
-rw-r--r--bgpd/bgp_pbr.c59
-rw-r--r--bgpd/bgp_rd.c6
-rw-r--r--bgpd/bgp_rd.h1
-rw-r--r--bgpd/bgp_route.c780
-rw-r--r--bgpd/bgp_route.h11
-rw-r--r--bgpd/bgp_routemap.c3
-rw-r--r--bgpd/bgp_rpki.c1270
-rw-r--r--bgpd/bgp_snmp.c46
-rw-r--r--bgpd/bgp_table.h2
-rw-r--r--bgpd/bgp_updgrp.c24
-rw-r--r--bgpd/bgp_updgrp_adv.c18
-rw-r--r--bgpd/bgp_updgrp_packet.c55
-rw-r--r--bgpd/bgp_vpn.c2
-rw-r--r--bgpd/bgp_vty.c185
-rw-r--r--bgpd/bgp_zebra.c95
-rw-r--r--bgpd/bgpd.c43
-rw-r--r--bgpd/bgpd.h17
-rw-r--r--bgpd/rfapi/rfapi.c3
-rw-r--r--bgpd/rfapi/rfapi_rib.c4
-rw-r--r--bgpd/subdir.am3
-rw-r--r--bgpd/valgrind.supp17
-rw-r--r--doc/developer/building-frr-for-openwrt.rst32
-rw-r--r--doc/developer/grpc.rst249
-rw-r--r--doc/developer/index.rst1
-rw-r--r--doc/developer/subdir.am1
-rw-r--r--doc/developer/zebra.rst72
-rw-r--r--doc/figures/fig_dmvpn_topologies.pngbin0 -> 41860 bytes
-rw-r--r--doc/manpages/frr-watchfrr.rst16
-rw-r--r--doc/user/bfd.rst38
-rw-r--r--doc/user/bgp.rst187
-rw-r--r--doc/user/grpc.rst66
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/nhrpd.rst203
-rw-r--r--doc/user/ospf6d.rst7
-rw-r--r--doc/user/overview.rst4
-rw-r--r--doc/user/pbr.rst16
-rw-r--r--doc/user/pim.rst7
-rw-r--r--doc/user/rpki.rst82
-rw-r--r--doc/user/sharp.rst19
-rw-r--r--doc/user/static.rst12
-rw-r--r--doc/user/subdir.am1
-rw-r--r--doc/user/zebra.rst30
-rw-r--r--eigrpd/eigrp_cli.c60
-rw-r--r--eigrpd/eigrp_dump.c3
-rw-r--r--eigrpd/eigrp_network.c10
-rw-r--r--eigrpd/eigrp_packet.c12
-rw-r--r--grpc/frr-northbound.proto36
-rw-r--r--include/linux/if_bridge.h11
-rw-r--r--include/linux/neighbour.h2
-rw-r--r--include/linux/net_namespace.h1
-rw-r--r--include/linux/nexthop.h1
-rw-r--r--isisd/fabricd.c14
-rw-r--r--isisd/isis_adjacency.c43
-rw-r--r--isisd/isis_adjacency.h4
-rw-r--r--isisd/isis_bfd.c89
-rw-r--r--isisd/isis_bfd.h6
-rw-r--r--isisd/isis_bpf.c3
-rw-r--r--isisd/isis_circuit.c33
-rw-r--r--isisd/isis_circuit.h45
-rw-r--r--isisd/isis_cli.c269
-rw-r--r--isisd/isis_csm.c30
-rw-r--r--isisd/isis_dlpi.c3
-rw-r--r--isisd/isis_dr.c8
-rw-r--r--isisd/isis_dynhn.c18
-rw-r--r--isisd/isis_dynhn.h4
-rw-r--r--isisd/isis_lsp.c121
-rw-r--r--isisd/isis_lsp.h12
-rw-r--r--isisd/isis_main.c9
-rw-r--r--isisd/isis_misc.c9
-rw-r--r--isisd/isis_misc.h2
-rw-r--r--isisd/isis_mt.c2
-rw-r--r--isisd/isis_nb.c15
-rw-r--r--isisd/isis_nb.h9
-rw-r--r--isisd/isis_nb_config.c102
-rw-r--r--isisd/isis_pdu.c116
-rw-r--r--isisd/isis_pfpacket.c10
-rw-r--r--isisd/isis_redist.c24
-rw-r--r--isisd/isis_redist.h9
-rw-r--r--isisd/isis_spf.c133
-rw-r--r--isisd/isis_sr.c103
-rw-r--r--isisd/isis_te.c116
-rw-r--r--isisd/isis_tlvs.c125
-rw-r--r--isisd/isis_vty_fabricd.c64
-rw-r--r--isisd/isis_zebra.c18
-rw-r--r--isisd/isisd.c913
-rw-r--r--isisd/isisd.h43
-rw-r--r--ldpd/hello.c15
-rw-r--r--ldpd/init.c15
-rw-r--r--ldpd/interface.c3
-rw-r--r--ldpd/l2vpn.c12
-rw-r--r--ldpd/lde.c20
-rw-r--r--ldpd/lde.h5
-rw-r--r--ldpd/lde_lib.c49
-rw-r--r--ldpd/ldp_vty_conf.c9
-rw-r--r--ldpd/ldp_vty_exec.c9
-rw-r--r--ldpd/ldpd.c15
-rw-r--r--ldpd/ldpe.c18
-rw-r--r--ldpd/neighbor.c6
-rw-r--r--ldpd/notification.c3
-rw-r--r--ldpd/packet.c9
-rw-r--r--lib/bfd.c39
-rw-r--r--lib/bfd.h4
-rw-r--r--lib/bitfield.h43
-rw-r--r--lib/buffer.c3
-rw-r--r--lib/command.c10
-rw-r--r--lib/command.h36
-rw-r--r--lib/command_graph.h1
-rw-r--r--lib/defun_lex.l2
-rw-r--r--lib/ferr.c4
-rw-r--r--lib/filter.h5
-rw-r--r--lib/filter_cli.c267
-rw-r--r--lib/filter_nb.c451
-rw-r--r--lib/id_alloc.c11
-rw-r--r--lib/if.c25
-rw-r--r--lib/if.h7
-rw-r--r--lib/ipaddr.h64
-rw-r--r--lib/libfrr.c10
-rw-r--r--lib/linklist.c75
-rw-r--r--lib/linklist.h33
-rw-r--r--lib/log.c19
-rw-r--r--lib/log.h6
-rw-r--r--lib/memory.c3
-rw-r--r--lib/mpls.h1
-rw-r--r--lib/netns_linux.c38
-rw-r--r--lib/nexthop.c133
-rw-r--r--lib/nexthop.h29
-rw-r--r--lib/nexthop_group.c83
-rw-r--r--lib/nexthop_group.h6
-rw-r--r--lib/northbound.c51
-rw-r--r--lib/northbound.h26
-rw-r--r--lib/northbound_cli.c147
-rw-r--r--lib/northbound_cli.h8
-rw-r--r--lib/northbound_confd.c7
-rw-r--r--lib/northbound_grpc.cpp24
-rw-r--r--lib/northbound_sysrepo.c7
-rw-r--r--lib/ns.h16
-rw-r--r--lib/pbr.h7
-rw-r--r--lib/prefix.c21
-rw-r--r--lib/prefix.h62
-rw-r--r--lib/privs.c3
-rw-r--r--lib/privs.h1
-rwxr-xr-xlib/route_types.pl2
-rw-r--r--lib/route_types.txt1
-rw-r--r--lib/routemap.c89
-rw-r--r--lib/routemap.h16
-rw-r--r--lib/routemap_cli.c163
-rw-r--r--lib/routemap_northbound.c85
-rw-r--r--lib/routing_nb.c40
-rw-r--r--lib/routing_nb.h24
-rw-r--r--lib/routing_nb_config.c74
-rw-r--r--lib/sockopt.c6
-rw-r--r--lib/sockunion.c50
-rw-r--r--lib/sockunion.h4
-rw-r--r--lib/srcdest_table.c10
-rw-r--r--lib/srcdest_table.h2
-rw-r--r--lib/srte.h56
-rw-r--r--lib/stream.c72
-rw-r--r--lib/stream.h8
-rw-r--r--lib/subdir.am8
-rw-r--r--lib/thread.c113
-rw-r--r--lib/thread.h4
-rw-r--r--lib/vrf.c22
-rw-r--r--lib/vrf.h2
-rw-r--r--lib/vty.c9
-rw-r--r--lib/vty.h8
-rw-r--r--lib/yang.c172
-rw-r--r--lib/yang.h55
-rw-r--r--lib/yang_wrappers.c83
-rw-r--r--lib/yang_wrappers.h5
-rw-r--r--lib/zclient.c225
-rw-r--r--lib/zclient.h67
-rw-r--r--lib/zlog.c6
-rw-r--r--m4/ax_python.m43
-rw-r--r--nhrpd/nhrp_route.c7
-rw-r--r--ospf6d/ospf6_abr.c27
-rw-r--r--ospf6d/ospf6_area.c16
-rw-r--r--ospf6d/ospf6_area.h17
-rw-r--r--ospf6d/ospf6_asbr.c55
-rw-r--r--ospf6d/ospf6_bfd.c2
-rw-r--r--ospf6d/ospf6_interface.c6
-rw-r--r--ospf6d/ospf6_interface.h8
-rw-r--r--ospf6d/ospf6_intra.c18
-rw-r--r--ospf6d/ospf6_intra.h10
-rw-r--r--ospf6d/ospf6_lsa.h4
-rw-r--r--ospf6d/ospf6_message.h12
-rw-r--r--ospf6d/ospf6_neighbor.h10
-rw-r--r--ospf6d/ospf6_route.h6
-rw-r--r--ospf6d/ospf6_spf.c3
-rw-r--r--ospf6d/ospf6_top.c29
-rw-r--r--ospf6d/ospf6_top.h6
-rw-r--r--ospfd/ospf_abr.c148
-rw-r--r--ospfd/ospf_apiserver.c6
-rw-r--r--ospfd/ospf_ase.c21
-rw-r--r--ospfd/ospf_bfd.c2
-rw-r--r--ospfd/ospf_dump.c40
-rw-r--r--ospfd/ospf_dump.h4
-rw-r--r--ospfd/ospf_ext.c34
-rw-r--r--ospfd/ospf_flood.c9
-rw-r--r--ospfd/ospf_ia.c24
-rw-r--r--ospfd/ospf_interface.c6
-rw-r--r--ospfd/ospf_lsa.c83
-rw-r--r--ospfd/ospf_network.c17
-rw-r--r--ospfd/ospf_nsm.c6
-rw-r--r--ospfd/ospf_opaque.c12
-rw-r--r--ospfd/ospf_packet.c80
-rw-r--r--ospfd/ospf_route.c24
-rw-r--r--ospfd/ospf_spf.c9
-rw-r--r--ospfd/ospf_sr.c53
-rw-r--r--ospfd/ospf_vty.c44
-rw-r--r--ospfd/ospf_zebra.c262
-rw-r--r--ospfd/ospf_zebra.h9
-rw-r--r--ospfd/ospfd.c1
-rw-r--r--ospfd/ospfd.h2
-rw-r--r--pbrd/pbr_map.c61
-rw-r--r--pbrd/pbr_map.h6
-rw-r--r--pbrd/pbr_nht.c51
-rw-r--r--pbrd/pbr_vty.c107
-rw-r--r--pbrd/pbr_zebra.c1
-rw-r--r--pimd/pim_bfd.c2
-rw-r--r--pimd/pim_bsm.c3
-rw-r--r--pimd/pim_cmd.c14
-rw-r--r--pimd/pim_iface.c2
-rw-r--r--pimd/pim_ifchannel.c9
-rw-r--r--pimd/pim_igmp.c4
-rw-r--r--pimd/pim_igmp_mtrace.c53
-rw-r--r--pimd/pim_mlag.c8
-rw-r--r--pimd/pim_mlag.h1
-rw-r--r--pimd/pim_rp.c3
-rw-r--r--pimd/pim_rpf.c4
-rw-r--r--pimd/pimd.c2
-rw-r--r--pimd/subdir.am6
-rw-r--r--python/makefile.py9
-rw-r--r--ripd/rip_cli.c68
-rw-r--r--ripd/rip_interface.c3
-rw-r--r--ripd/ripd.c24
-rw-r--r--ripngd/ripng_cli.c34
-rw-r--r--ripngd/ripngd.c3
-rw-r--r--sharpd/sharp_main.c3
-rw-r--r--sharpd/sharp_vty.c87
-rw-r--r--sharpd/sharp_zebra.c33
-rw-r--r--sharpd/sharp_zebra.h8
-rw-r--r--staticd/static_main.c11
-rw-r--r--staticd/static_memory.c2
-rw-r--r--staticd/static_memory.h3
-rw-r--r--staticd/static_nb.c202
-rw-r--r--staticd/static_nb.h176
-rw-r--r--staticd/static_nb_config.c1337
-rw-r--r--staticd/static_nht.c122
-rw-r--r--staticd/static_routes.c637
-rw-r--r--staticd/static_routes.h115
-rw-r--r--staticd/static_vrf.c68
-rw-r--r--staticd/static_vrf.h10
-rw-r--r--staticd/static_vty.c1258
-rw-r--r--staticd/static_vty.h2
-rw-r--r--staticd/static_zebra.c146
-rw-r--r--staticd/static_zebra.h8
-rw-r--r--staticd/subdir.am7
-rw-r--r--tests/bgpd/test_aspath.c125
-rw-r--r--tests/isisd/test_isis_lspdb.c1
-rw-r--r--tests/lib/test_checksum.c3
-rw-r--r--tests/lib/test_ttable.c3
-rw-r--r--tests/topotests/Dockerfile6
-rw-r--r--tests/topotests/all-protocol-startup/r1/show_route_map.ref15
-rw-r--r--tests/topotests/bfd-isis-topo1/rt1/bfdd.conf2
-rw-r--r--tests/topotests/bfd-isis-topo1/rt2/bfdd.conf1
-rw-r--r--tests/topotests/bfd-isis-topo1/rt3/bfdd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfdd.conf9
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/isisd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/isisd.conf1
-rwxr-xr-x[-rw-r--r--]tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py0
-rw-r--r--tests/topotests/bfd-topo3/__init__.py0
-rw-r--r--tests/topotests/bfd-topo3/r1/bfd-peers.json68
-rw-r--r--tests/topotests/bfd-topo3/r1/bfdd.conf17
-rw-r--r--tests/topotests/bfd-topo3/r1/bgpd.conf20
-rw-r--r--tests/topotests/bfd-topo3/r1/zebra.conf10
-rw-r--r--tests/topotests/bfd-topo3/r2/bfd-peers.json46
-rw-r--r--tests/topotests/bfd-topo3/r2/bfdd.conf15
-rw-r--r--tests/topotests/bfd-topo3/r2/bgpd.conf15
-rw-r--r--tests/topotests/bfd-topo3/r2/zebra.conf14
-rw-r--r--tests/topotests/bfd-topo3/r3/bfd-peers.json68
-rw-r--r--tests/topotests/bfd-topo3/r3/bfdd.conf11
-rw-r--r--tests/topotests/bfd-topo3/r3/bgpd.conf19
-rw-r--r--tests/topotests/bfd-topo3/r3/zebra.conf14
-rw-r--r--tests/topotests/bfd-topo3/r4/bfd-peers.json46
-rw-r--r--tests/topotests/bfd-topo3/r4/bfdd.conf16
-rw-r--r--tests/topotests/bfd-topo3/r4/bgpd.conf16
-rw-r--r--tests/topotests/bfd-topo3/r4/zebra.conf10
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.dot73
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.jpgbin0 -> 34705 bytes
-rw-r--r--tests/topotests/bfd-topo3/test_bfd_topo3.py191
-rw-r--r--tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdfbin0 -> 90963 bytes
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd11/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd12/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd21/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/evpn.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/pim.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/hostd22/zebra.conf0
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/evpn.conf17
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/pim.conf18
-rw-r--r--tests/topotests/bgp-evpn-mh/spine1/zebra.conf15
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/evpn.conf17
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/pim.conf18
-rw-r--r--tests/topotests/bgp-evpn-mh/spine2/zebra.conf15
-rwxr-xr-xtests/topotests/bgp-evpn-mh/test_evpn_mh.py651
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm11/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm12/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm21/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/evpn.conf21
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/pim.conf13
-rw-r--r--tests/topotests/bgp-evpn-mh/torm22/zebra.conf23
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json3
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf2
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json3
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf2
-rwxr-xr-xtests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py119
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_prefix_sid/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf1
-rw-r--r--tests/topotests/evpn_type5_test_topo1/__init__.py0
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json887
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json887
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py1047
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py2117
-rw-r--r--tests/topotests/isis-sr-topo1/rt1/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt2/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt3/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt4/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt5/isisd.conf1
-rw-r--r--tests/topotests/isis-sr-topo1/rt6/isisd.conf1
-rwxr-xr-xtests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py12
-rw-r--r--tests/topotests/lib/bgp.py724
-rw-r--r--tests/topotests/lib/common_config.py571
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topotest.py125
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json18
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json27
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json15
-rw-r--r--tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json18
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-map.json26
-rw-r--r--tests/topotests/pbr-topo1/r1/pbrd.conf10
-rw-r--r--tests/topotests/pytest.ini2
-rwxr-xr-xtests/topotests/route-scale/test_route_scale.py156
-rw-r--r--tests/topotests/zebra_netlink/__init__.py0
-rw-r--r--tests/topotests/zebra_netlink/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_netlink/r1/v4_route.json2802
-rw-r--r--tests/topotests/zebra_netlink/r1/zebra.conf2
-rwxr-xr-xtests/topotests/zebra_netlink/test_zebra_netlink.py131
-rw-r--r--tools/cocci.h37
-rw-r--r--tools/etc/frr/daemons5
-rw-r--r--tools/etc/frr/support_bundle_commands.conf2
-rwxr-xr-xtools/frr-reload.py44
-rwxr-xr-xtools/frr.in2
-rw-r--r--tools/frr@.service25
-rw-r--r--tools/frrcommon.sh.in22
-rw-r--r--tools/frrinit.sh.in5
-rw-r--r--tools/gcc-plugins/README.md15
-rw-r--r--tools/gcc-plugins/debian/changelog6
-rw-r--r--tools/gcc-plugins/debian/control4
-rw-r--r--tools/gcc-plugins/debian/source/format2
-rw-r--r--tools/gcc-plugins/format-test.c6
-rw-r--r--tools/gcc-plugins/format-test.py8
-rw-r--r--tools/gcc-plugins/frr-format.c90
-rw-r--r--tools/gcc-plugins/gcc-common.h6
-rw-r--r--tools/gen_northbound_callbacks.c2
-rw-r--r--tools/start-stop-daemon.c15
-rw-r--r--tools/stringmangle.py59
-rw-r--r--tools/subdir.am1
-rw-r--r--vrrpd/vrrp.c24
-rw-r--r--vrrpd/vrrp_main.c1
-rw-r--r--vrrpd/vrrp_northbound.c27
-rw-r--r--vrrpd/vrrp_packet.c15
-rw-r--r--vrrpd/vrrp_vty.c56
-rwxr-xr-xvtysh/extract.pl.in9
-rw-r--r--vtysh/vtysh.c33
-rw-r--r--vtysh/vtysh.h5
-rw-r--r--vtysh/vtysh_config.c78
-rw-r--r--vtysh/vtysh_main.c6
-rw-r--r--watchfrr/watchfrr.c220
-rw-r--r--yang/embedmodel.py21
-rw-r--r--yang/frr-bfdd.yang21
-rw-r--r--yang/frr-bgp-bmp.yang203
-rw-r--r--yang/frr-bgp-common-multiprotocol.yang209
-rw-r--r--yang/frr-bgp-common-structure.yang807
-rw-r--r--yang/frr-bgp-common.yang1108
-rw-r--r--yang/frr-bgp-neighbor.yang137
-rw-r--r--yang/frr-bgp-peer-group.yang89
-rw-r--r--yang/frr-bgp-rpki.yang209
-rw-r--r--yang/frr-bgp-types.yang154
-rw-r--r--yang/frr-bgp.yang1239
-rw-r--r--yang/frr-deviations-bgp-datacenter.yang106
-rw-r--r--yang/frr-filter.yang162
-rw-r--r--yang/frr-isisd.yang17
-rw-r--r--yang/frr-nexthop.yang17
-rw-r--r--yang/frr-ospfd.yang995
-rw-r--r--yang/frr-route-map.yang158
-rw-r--r--yang/frr-routing.yang101
-rw-r--r--yang/frr-staticd.yang92
-rw-r--r--yang/frr-zebra.yang72
-rw-r--r--yang/ietf/ietf-bgp-types.yang525
-rw-r--r--yang/subdir.am9
-rw-r--r--zebra/connected.c9
-rw-r--r--zebra/debug.c79
-rw-r--r--zebra/debug.h15
-rw-r--r--zebra/dplane_fpm_nl.c171
-rw-r--r--zebra/if_netlink.c215
-rw-r--r--zebra/if_netlink.h4
-rw-r--r--zebra/interface.c60
-rw-r--r--zebra/interface.h31
-rw-r--r--zebra/ipforward_solaris.c3
-rw-r--r--zebra/irdp_interface.c9
-rw-r--r--zebra/kernel_netlink.c406
-rw-r--r--zebra/kernel_netlink.h46
-rw-r--r--zebra/kernel_socket.c105
-rw-r--r--zebra/label_manager.c2
-rw-r--r--zebra/main.c17
-rw-r--r--zebra/rib.h21
-rw-r--r--zebra/router-id.c490
-rw-r--r--zebra/router-id.h10
-rw-r--r--zebra/rt.h17
-rw-r--r--zebra/rt_netlink.c568
-rw-r--r--zebra/rt_netlink.h20
-rw-r--r--zebra/rt_socket.c38
-rw-r--r--zebra/rtadv.c9
-rw-r--r--zebra/rtread_getmsg.c3
-rw-r--r--zebra/rule_netlink.c107
-rw-r--r--zebra/rule_netlink.h3
-rw-r--r--zebra/subdir.am13
-rw-r--r--zebra/zapi_msg.c335
-rw-r--r--zebra/zapi_msg.h8
-rw-r--r--zebra/zebra_dplane.c571
-rw-r--r--zebra/zebra_dplane.h68
-rw-r--r--zebra/zebra_errors.h1
-rw-r--r--zebra/zebra_evpn.c1448
-rw-r--r--zebra/zebra_evpn.h208
-rw-r--r--zebra/zebra_evpn_mac.c2231
-rw-r--r--zebra/zebra_evpn_mac.h263
-rw-r--r--zebra/zebra_evpn_mh.c2147
-rw-r--r--zebra/zebra_evpn_mh.h239
-rw-r--r--zebra/zebra_evpn_neigh.c2453
-rw-r--r--zebra/zebra_evpn_neigh.h294
-rw-r--r--zebra/zebra_evpn_vxlan.h71
-rw-r--r--zebra/zebra_fpm.c3
-rw-r--r--zebra/zebra_fpm_netlink.c17
-rw-r--r--zebra/zebra_l2.c85
-rw-r--r--zebra/zebra_l2.h11
-rw-r--r--zebra/zebra_memory.c2
-rw-r--r--zebra/zebra_mpls.c152
-rw-r--r--zebra/zebra_mpls.h13
-rw-r--r--zebra/zebra_mpls_netlink.c34
-rw-r--r--zebra/zebra_mpls_openbsd.c3
-rw-r--r--zebra/zebra_nb.c74
-rw-r--r--zebra/zebra_nb.h16
-rw-r--r--zebra/zebra_nb_config.c252
-rw-r--r--zebra/zebra_nb_state.c60
-rw-r--r--zebra/zebra_netns_id.c43
-rw-r--r--zebra/zebra_netns_id.h2
-rw-r--r--zebra/zebra_netns_notify.c33
-rw-r--r--zebra/zebra_nhg.c93
-rw-r--r--zebra/zebra_ns.c17
-rw-r--r--zebra/zebra_ns.h9
-rw-r--r--zebra/zebra_pbr.c17
-rw-r--r--zebra/zebra_pbr.h9
-rw-r--r--zebra/zebra_ptm.c9
-rw-r--r--zebra/zebra_pw.c113
-rw-r--r--zebra/zebra_rib.c62
-rw-r--r--zebra/zebra_rnh.c56
-rw-r--r--zebra/zebra_rnh.h3
-rw-r--r--zebra/zebra_routemap.c48
-rw-r--r--zebra/zebra_router.c1
-rw-r--r--zebra/zebra_router.h6
-rw-r--r--zebra/zebra_srte.c378
-rw-r--r--zebra/zebra_srte.h74
-rw-r--r--zebra/zebra_vrf.c1
-rw-r--r--zebra/zebra_vrf.h9
-rw-r--r--zebra/zebra_vty.c410
-rw-r--r--zebra/zebra_vxlan.c5948
-rw-r--r--zebra/zebra_vxlan.h6
-rw-r--r--zebra/zebra_vxlan_private.h264
-rw-r--r--zebra/zserv.c10
-rw-r--r--zebra/zserv.h6
562 files changed, 46869 insertions, 15457 deletions
diff --git a/.clang-format b/.clang-format
index 654577d936..47d681e0e2 100644
--- a/.clang-format
+++ b/.clang-format
@@ -66,5 +66,6 @@ ForEachMacros:
- SUBGRP_FOREACH_ADJ_SAFE
- AF_FOREACH
- FOREACH_AFI_SAFI
+ - FOREACH_SAFI
# ospfd
- LSDB_LOOP
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..e8a364050a
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1 @@
+d62a17aedeb0eebdba98238874bb13d62c48dbf9
diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c
index 772aec1234..1702d9277c 100644
--- a/babeld/babel_interface.c
+++ b/babeld/babel_interface.c
@@ -692,8 +692,7 @@ interface_recalculate(struct interface *ifp)
rc = resize_receive_buffer(mtu);
if(rc < 0)
- zlog_warn("couldn't resize "
- "receive buffer for interface %s (%d) (%d bytes).\n",
+ zlog_warn("couldn't resize receive buffer for interface %s (%d) (%d bytes).\n",
ifp->name, ifp->ifindex, mtu);
memset(&mreq, 0, sizeof(mreq));
@@ -896,8 +895,7 @@ static void
show_babel_neighbour_sub (struct vty *vty, struct neighbour *neigh)
{
vty_out (vty,
- "Neighbour %s dev %s reach %04x rxcost %d txcost %d "
- "rtt %s rttcost %d%s.\n",
+ "Neighbour %s dev %s reach %04x rxcost %d txcost %d rtt %s rttcost %d%s.\n",
format_address(neigh->address),
neigh->ifp->name,
neigh->reach,
@@ -988,8 +986,7 @@ show_babel_routes_sub(struct babel_route *route, struct vty *vty,
}
vty_out (vty,
- "%s metric %d refmetric %d id %s seqno %d%s age %d "
- "via %s neigh %s%s%s%s\n",
+ "%s metric %d refmetric %d id %s seqno %d%s age %d via %s neigh %s%s%s%s\n",
format_prefix(route->src->prefix, route->src->plen),
route_metric(route), route->refmetric,
format_eui64(route->src->id),
diff --git a/babeld/message.c b/babeld/message.c
index d88790824c..c9a10cb1c0 100644
--- a/babeld/message.c
+++ b/babeld/message.c
@@ -710,8 +710,7 @@ fill_rtt_message(struct interface *ifp)
DO_HTONL(babel_ifp->sendbuf + babel_ifp->buffered_hello + 10, time);
return 1;
} else {
- flog_err(EC_BABEL_PACKET, "No space left for timestamp sub-TLV "
- "(this shouldn't happen)");
+ flog_err(EC_BABEL_PACKET, "No space left for timestamp sub-TLV (this shouldn't happen)");
return -1;
}
}
diff --git a/babeld/route.c b/babeld/route.c
index ab104aa2b1..0f6f6486f2 100644
--- a/babeld/route.c
+++ b/babeld/route.c
@@ -399,16 +399,14 @@ install_route(struct babel_route *route)
return;
if(!route_feasible(route))
- flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route "
- "(this shouldn't happen).");
+ flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route (this shouldn't happen).");
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
if(routes[i] != route && routes[i]->installed) {
flog_err(EC_BABEL_ROUTE,
- "WARNING: attempting to install duplicate route "
- "(this shouldn't happen).");
+ "WARNING: attempting to install duplicate route (this shouldn't happen).");
return;
}
@@ -465,8 +463,7 @@ switch_routes(struct babel_route *old, struct babel_route *new)
return;
if(!route_feasible(new))
- flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route "
- "(this shouldn't happen).");
+ flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route (this shouldn't happen).");
rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen,
old->nexthop, old->neigh->ifp->ifindex,
@@ -835,8 +832,7 @@ update_route(const unsigned char *router_id,
in a timely manner. If the source remains the same, we ignore
the update. */
if(!feasible && route->installed) {
- debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s "
- "(%s %d %d -> %s %d %d).",
+ debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s (%s %d %d -> %s %d %d).",
format_prefix(src->prefix, src->plen),
format_eui64(route->src->id),
route->seqno, route->refmetric,
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index 7d6f4c1431..c16912060c 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -88,6 +88,8 @@ static void bfd_profile_set_default(struct bfd_profile *bp)
bp->admin_shutdown = true;
bp->detection_multiplier = BFD_DEFDETECTMULT;
bp->echo_mode = false;
+ bp->passive = false;
+ bp->minimum_ttl = BFD_DEF_MHOP_TTL;
bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO;
bp->min_rx = BFD_DEFREQUIREDMINRX;
bp->min_tx = BFD_DEFDESIREDMINTX;
@@ -124,53 +126,12 @@ void bfd_profile_free(struct bfd_profile *bp)
XFREE(MTYPE_BFDD_PROFILE, bp);
}
-/**
- * Removes a profile and tests whether it needs to apply the changes or not.
- *
- * \param bs the BFD session.
- * \param apply whether or not to apply configurations immediately.
- */
-static void _bfd_profile_remove(struct bfd_session *bs, bool apply)
-{
- struct bfd_profile *bp;
-
- /* No profile applied, nothing to do. */
- bp = bs->profile;
- if (bp == NULL)
- return;
-
- /* Remove the profile association. */
- bs->profile = NULL;
-
- /* Set multiplier to the default. */
- bs->detect_mult = bs->peer_profile.detection_multiplier;
-
- /* Set timers back to user configuration. */
- bs->timers.desired_min_tx = bs->peer_profile.min_tx;
- bs->timers.required_min_rx = bs->peer_profile.min_rx;
-
- /* We can only apply echo options on single hop sessions. */
- if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
- /* Set default echo timer. */
- bs->timers.required_min_echo = bs->peer_profile.min_echo_rx;
-
- /* Default is no echo mode. */
- if (apply)
- bfd_set_echo(bs, bs->peer_profile.echo_mode);
- }
-
- if (apply)
- bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
-}
-
void bfd_profile_apply(const char *profname, struct bfd_session *bs)
{
struct bfd_profile *bp;
/* Remove previous profile if any. */
if (bs->profile_name) {
- _bfd_profile_remove(bs, false);
-
/* We are changing profiles. */
if (strcmp(bs->profile_name, profname)) {
XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
@@ -182,12 +143,23 @@ void bfd_profile_apply(const char *profname, struct bfd_session *bs)
/* Look up new profile to apply. */
bp = bfd_profile_lookup(profname);
- if (bp == NULL)
- return;
/* Point to profile if it exists. */
bs->profile = bp;
+ /* Apply configuration. */
+ bfd_session_apply(bs);
+}
+
+void bfd_session_apply(struct bfd_session *bs)
+{
+ struct bfd_profile *bp;
+ uint32_t min_tx = bs->timers.desired_min_tx;
+ uint32_t min_rx = bs->timers.required_min_rx;
+
+ /* Pick the source of configuration. */
+ bp = bs->profile ? bs->profile : &bs->peer_profile;
+
/* Set multiplier if not the default. */
if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT)
bs->detect_mult = bp->detection_multiplier;
@@ -219,21 +191,40 @@ void bfd_profile_apply(const char *profname, struct bfd_session *bs)
bfd_set_echo(bs, bp->echo_mode);
else
bfd_set_echo(bs, bs->peer_profile.echo_mode);
+ } else {
+ /* Configure the TTL packet filter. */
+ if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL)
+ bs->mh_ttl = bp->minimum_ttl;
+ else
+ bs->mh_ttl = bs->peer_profile.minimum_ttl;
}
+ /* Toggle 'passive-mode' if default value. */
+ if (bs->peer_profile.passive == false)
+ bfd_set_passive_mode(bs, bp->passive);
+ else
+ bfd_set_passive_mode(bs, bs->peer_profile.passive);
+
/* Toggle 'no shutdown' if default value. */
if (bs->peer_profile.admin_shutdown)
bfd_set_shutdown(bs, bp->admin_shutdown);
else
bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
+
+ /* If session interval changed negotiate new timers. */
+ if (bs->ses_state == PTM_BFD_UP
+ && (bs->timers.desired_min_tx != min_tx
+ || bs->timers.required_min_rx != min_rx))
+ bfd_set_polling(bs);
}
void bfd_profile_remove(struct bfd_session *bs)
{
/* Remove any previous set profile name. */
XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
+ bs->profile = NULL;
- _bfd_profile_remove(bs, true);
+ bfd_session_apply(bs);
}
void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
@@ -374,8 +365,12 @@ int bfd_session_enable(struct bfd_session *bs)
* protocol.
*/
bs->sock = psock;
- bfd_recvtimer_update(bs);
- ptm_bfd_start_xmt_timer(bs, false);
+
+ /* Only start timers if we are using active mode. */
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) {
+ bfd_recvtimer_update(bs);
+ ptm_bfd_start_xmt_timer(bs, false);
+ }
return 0;
}
@@ -536,6 +531,12 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
ptm_bfd_echo_stop(bfd);
+ /* Stop attempting to transmit or expect control packets if passive. */
+ if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) {
+ bfd_recvtimer_delete(bfd);
+ bfd_xmttimer_delete(bfd);
+ }
+
if (old_state != bfd->ses_state) {
bfd->stats.session_down++;
if (bglobal.debug_peer_event)
@@ -758,6 +759,11 @@ static void _bfd_session_update(struct bfd_session *bs,
else
UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
+ if (bpc->bpc_has_minimum_ttl) {
+ bs->mh_ttl = bpc->bpc_minimum_ttl;
+ bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl;
+ }
+
bs->peer_profile.echo_mode = bpc->bpc_echo;
bfd_set_echo(bs, bpc->bpc_echo);
@@ -766,6 +772,7 @@ static void _bfd_session_update(struct bfd_session *bs,
* the session is disabled.
*/
bs->peer_profile.admin_shutdown = bpc->bpc_shutdown;
+ bfd_set_passive_mode(bs, bpc->bpc_passive);
bfd_set_shutdown(bs, bpc->bpc_shutdown);
/*
@@ -924,8 +931,7 @@ int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
/* This pointer is being referenced, don't let it be deleted. */
if (bs->refcount > 0) {
- zlog_err("session-delete: refcount failure: %" PRIu64
- " references",
+ zlog_err("session-delete: refcount failure: %" PRIu64" references",
bs->refcount);
return -1;
}
@@ -987,6 +993,10 @@ static void bs_down_handler(struct bfd_session *bs, int nstate)
* bring it up.
*/
bs->ses_state = PTM_BFD_INIT;
+
+ /* Answer peer with INIT immediately in passive mode. */
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
+ ptm_bfd_snd(bs, 0);
break;
case PTM_BFD_INIT:
@@ -1313,9 +1323,39 @@ void bfd_set_shutdown(struct bfd_session *bs, bool shutdown)
bs->ses_state = PTM_BFD_DOWN;
control_notify(bs, bs->ses_state);
- /* Enable all timers. */
- bfd_recvtimer_update(bs);
+ /* Enable timers if non passive, otherwise stop them. */
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) {
+ bfd_recvtimer_delete(bs);
+ bfd_xmttimer_delete(bs);
+ } else {
+ bfd_recvtimer_update(bs);
+ bfd_xmttimer_update(bs, bs->xmt_TO);
+ }
+ }
+}
+
+void bfd_set_passive_mode(struct bfd_session *bs, bool passive)
+{
+ if (passive) {
+ SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
+
+ /* Session is already up and running, nothing to do now. */
+ if (bs->ses_state != PTM_BFD_DOWN)
+ return;
+
+ /* Lets disable the timers since we are now passive. */
+ bfd_recvtimer_delete(bs);
+ bfd_xmttimer_delete(bs);
+ } else {
+ UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
+
+ /* Session is already up and running, nothing to do now. */
+ if (bs->ses_state != PTM_BFD_DOWN)
+ return;
+
+ /* Session is down, let it attempt to start the connection. */
bfd_xmttimer_update(bs, bs->xmt_TO);
+ bfd_recvtimer_update(bs);
}
}
@@ -1693,8 +1733,7 @@ struct bfd_session *bfd_key_lookup(struct bfd_key key)
inet_ntop(bs.key.family, &bs.key.local,
addr_buf, sizeof(addr_buf));
zlog_debug(
- " peer %s found, but ifp %s"
- " and loc-addr %s ignored",
+ " peer %s found, but ifp %s and loc-addr %s ignored",
peer_buf, key.ifname, addr_buf);
}
return bsp;
@@ -1716,8 +1755,7 @@ struct bfd_session *bfd_key_lookup(struct bfd_key key)
bsp = ctx.result;
if (bglobal.debug_peer_event)
zlog_debug(
- " peer %s found, but ifp"
- " and/or loc-addr params ignored",
+ " peer %s found, but ifp and/or loc-addr params ignored",
peer_buf);
}
return bsp;
@@ -2014,16 +2052,16 @@ static int bfd_vrf_enable(struct vrf *vrf)
if (!bvrf->bg_ev[1])
thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
&bvrf->bg_ev[1]);
- if (!bvrf->bg_ev[2])
+ if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1)
thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
&bvrf->bg_ev[2]);
- if (!bvrf->bg_ev[3])
+ if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1)
thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
&bvrf->bg_ev[3]);
if (!bvrf->bg_ev[4])
thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
&bvrf->bg_ev[4]);
- if (!bvrf->bg_ev[5])
+ if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1)
thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
&bvrf->bg_ev[5]);
}
@@ -2062,10 +2100,13 @@ static int bfd_vrf_disable(struct vrf *vrf)
socket_close(&bvrf->bg_echo);
socket_close(&bvrf->bg_shop);
socket_close(&bvrf->bg_mhop);
- socket_close(&bvrf->bg_shop6);
- socket_close(&bvrf->bg_mhop6);
+ if (bvrf->bg_shop6 != -1)
+ socket_close(&bvrf->bg_shop6);
+ if (bvrf->bg_mhop6 != -1)
+ socket_close(&bvrf->bg_mhop6);
socket_close(&bvrf->bg_echo);
- socket_close(&bvrf->bg_echov6);
+ if (bvrf->bg_echov6 != -1)
+ socket_close(&bvrf->bg_echov6);
/* free context */
XFREE(MTYPE_BFDD_VRF, bvrf);
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index 492334a670..af3f92d6a8 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -170,6 +170,7 @@ enum bfd_session_flags {
BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */
BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */
BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */
+ BFD_SESS_FLAG_PASSIVE = 1 << 10, /* Passive mode */
};
/* BFD session hash keys */
@@ -207,6 +208,10 @@ struct bfd_profile {
uint32_t min_rx;
/** Administrative state. */
bool admin_shutdown;
+ /** Passive mode. */
+ bool passive;
+ /** Minimum expected TTL value. */
+ uint8_t minimum_ttl;
/** Echo mode (only applies to single hop). */
bool echo_mode;
@@ -328,7 +333,8 @@ TAILQ_HEAD(obslist, bfd_session_observer);
#define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */
#define BFD_DEF_REQ_MIN_ECHO (50 * 1000) /* microseconds. */
#define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */
-#define BFD_DEF_MHOP_TTL 5
+/** Minimum multi hop TTL. */
+#define BFD_DEF_MHOP_TTL 254
#define BFD_PKT_LEN 24 /* Length of control packet */
#define BFD_TTL_VAL 255
#define BFD_RCV_TTL_VAL 1
@@ -607,6 +613,23 @@ void bfd_set_echo(struct bfd_session *bs, bool echo);
*/
void bfd_set_shutdown(struct bfd_session *bs, bool shutdown);
+/**
+ * Set the BFD session passive mode.
+ *
+ * \param bs the BFD session.
+ * \param passive the passive mode.
+ */
+void bfd_set_passive_mode(struct bfd_session *bs, bool passive);
+
+/**
+ * Picks the BFD session configuration from the appropriated source:
+ * if using the default peer configuration prefer profile (if it exists),
+ * otherwise use session.
+ *
+ * \param bs the BFD session.
+ */
+void bfd_session_apply(struct bfd_session *bs);
+
/* BFD hash data structures interface */
void bfd_initialize(void);
void bfd_shutdown(void);
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index 68bdd89bb7..5cc47d5a44 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -147,6 +147,8 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd)
bep.my_discr = htonl(bfd->discrs.my_discr);
if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
+ if (bvrf->bg_echov6 == -1)
+ return;
sd = bvrf->bg_echov6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
@@ -577,7 +579,7 @@ int bfd_recv_cb(struct thread *t)
return 0;
}
- /* Validate packet TTL. */
+ /* Validate single hop packet TTL. */
if ((!is_mhop) && (ttl != BFD_TTL_VAL)) {
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
"invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
@@ -630,10 +632,10 @@ int bfd_recv_cb(struct thread *t)
* Single hop: set local address that received the packet.
*/
if (is_mhop) {
- if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
+ if (ttl < bfd->mh_ttl) {
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
"exceeded max hop count (expected %d, got %d)",
- bfd->mh_ttl, BFD_TTL_VAL);
+ bfd->mh_ttl, ttl);
return 0;
}
} else if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) {
@@ -1145,8 +1147,14 @@ int bp_udp6_shop(const struct vrf *vrf)
sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
vrf->name);
}
- if (sd == -1)
- zlog_fatal("udp6-shop: socket: %s", strerror(errno));
+ if (sd == -1) {
+ if (errno != EAFNOSUPPORT)
+ zlog_fatal("udp6-shop: socket: %s", strerror(errno));
+ else
+ zlog_warn("udp6-shop: V6 is not supported, continuing");
+
+ return -1;
+ }
bp_set_ipv6opts(sd);
bp_bind_ipv6(sd, BFD_DEFDESTPORT);
@@ -1162,8 +1170,14 @@ int bp_udp6_mhop(const struct vrf *vrf)
sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
vrf->name);
}
- if (sd == -1)
- zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
+ if (sd == -1) {
+ if (errno != EAFNOSUPPORT)
+ zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
+ else
+ zlog_warn("udp6-mhop: V6 is not supported, continuing");
+
+ return -1;
+ }
bp_set_ipv6opts(sd);
bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
@@ -1194,8 +1208,15 @@ int bp_echov6_socket(const struct vrf *vrf)
frr_with_privs(&bglobal.bfdd_privs) {
s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
}
- if (s == -1)
- zlog_fatal("echov6-socket: socket: %s", strerror(errno));
+ if (s == -1) {
+ if (errno != EAFNOSUPPORT)
+ zlog_fatal("echov6-socket: socket: %s",
+ strerror(errno));
+ else
+ zlog_warn("echov6-socket: V6 is not supported, continuing");
+
+ return -1;
+ }
bp_set_ipv6opts(s);
bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h
index 95cfcb1105..e1cff9a31c 100644
--- a/bfdd/bfdctl.h
+++ b/bfdd/bfdctl.h
@@ -84,11 +84,15 @@ struct bfd_peer_cfg {
bool bpc_has_echointerval;
uint64_t bpc_echointerval;
+ bool bpc_has_minimum_ttl;
+ uint8_t bpc_minimum_ttl;
+
bool bpc_echo;
bool bpc_createonly;
bool bpc_shutdown;
bool bpc_cbit;
+ bool bpc_passive;
bool bpc_has_profile;
char bpc_profile[64];
diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c
index 166997e674..058ce7d1f2 100644
--- a/bfdd/bfdd_cli.c
+++ b/bfdd/bfdd_cli.c
@@ -55,7 +55,7 @@
/*
* Functions.
*/
-DEFPY_NOSH(
+DEFPY_YANG_NOSH(
bfd_enter, bfd_enter_cmd,
"bfd",
"Configure BFD peers\n")
@@ -70,7 +70,7 @@ DEFPY_NOSH(
return ret;
}
-DEFUN(
+DEFUN_YANG(
bfd_config_reset, bfd_config_reset_cmd,
"no bfd",
NO_STR
@@ -93,7 +93,7 @@ void bfd_cli_show_header_end(struct vty *vty,
vty_out(vty, "!\n");
}
-DEFPY_NOSH(
+DEFPY_YANG_NOSH(
bfd_peer_enter, bfd_peer_enter_cmd,
"peer <A.B.C.D|X:X::X:X> [{multihop$multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME$ifname|vrf NAME}]",
PEER_STR
@@ -150,7 +150,7 @@ DEFPY_NOSH(
return ret;
}
-DEFPY(
+DEFPY_YANG(
bfd_no_peer, bfd_no_peer_cmd,
"no peer <A.B.C.D|X:X::X:X> [{multihop$multihop|local-address <A.B.C.D|X:X::X:X>|interface IFNAME$ifname|vrf NAME}]",
NO_STR
@@ -244,7 +244,7 @@ void bfd_cli_show_peer_end(struct vty *vty,
vty_out(vty, " !\n");
}
-DEFPY(
+DEFPY_YANG(
bfd_peer_shutdown, bfd_peer_shutdown_cmd,
"[no] shutdown",
NO_STR
@@ -265,7 +265,64 @@ void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
}
-DEFPY(
+DEFPY_YANG(
+ bfd_peer_passive, bfd_peer_passive_cmd,
+ "[no] passive-mode",
+ NO_STR
+ "Don't attempt to start sessions\n")
+{
+ nb_cli_enqueue_change(vty, "./passive-mode", NB_OP_MODIFY,
+ no ? "false" : "true");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (show_defaults)
+ vty_out(vty, " no passive-mode\n");
+ else
+ vty_out(vty, " %spassive-mode\n",
+ yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
+}
+
+DEFPY_YANG(
+ bfd_peer_minimum_ttl, bfd_peer_minimum_ttl_cmd,
+ "[no] minimum-ttl (1-254)$ttl",
+ NO_STR
+ "Expect packets with at least this TTL\n"
+ "Minimum TTL expected\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY,
+ NULL);
+ else
+ nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_MODIFY,
+ ttl_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(
+ no_bfd_peer_minimum_ttl, no_bfd_peer_minimum_ttl_cmd,
+ "no minimum-ttl",
+ NO_STR
+ "Expect packets with at least this TTL\n")
+{
+ nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ if (show_defaults)
+ vty_out(vty, " minimum-ttl 254\n");
+ else
+ vty_out(vty, " minimum-ttl %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+DEFPY_YANG(
bfd_peer_mult, bfd_peer_mult_cmd,
"detect-multiplier (2-255)$multiplier",
"Configure peer detection multiplier\n"
@@ -287,7 +344,7 @@ void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(dnode, NULL));
}
-DEFPY(
+DEFPY_YANG(
bfd_peer_rx, bfd_peer_rx_cmd,
"receive-interval (10-60000)$interval",
"Configure peer receive interval\n"
@@ -312,11 +369,11 @@ void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode,
BFD_DEFREQUIREDMINRX);
else {
value = yang_dnode_get_uint32(dnode, NULL);
- vty_out(vty, " receive-interval %" PRIu32 "\n", value / 1000);
+ vty_out(vty, " receive-interval %u\n", value / 1000);
}
}
-DEFPY(
+DEFPY_YANG(
bfd_peer_tx, bfd_peer_tx_cmd,
"transmit-interval (10-60000)$interval",
"Configure peer transmit interval\n"
@@ -341,11 +398,11 @@ void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode,
BFD_DEFDESIREDMINTX);
else {
value = yang_dnode_get_uint32(dnode, NULL);
- vty_out(vty, " transmit-interval %" PRIu32 "\n", value / 1000);
+ vty_out(vty, " transmit-interval %u\n", value / 1000);
}
}
-DEFPY(
+DEFPY_YANG(
bfd_peer_echo, bfd_peer_echo_cmd,
"[no] echo-mode",
NO_STR
@@ -366,7 +423,7 @@ void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
}
-DEFPY(
+DEFPY_YANG(
bfd_peer_echo_interval, bfd_peer_echo_interval_cmd,
"echo-interval (10-60000)$interval",
"Configure peer echo interval\n"
@@ -391,14 +448,14 @@ void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode,
BFD_DEF_REQ_MIN_ECHO);
else {
value = yang_dnode_get_uint32(dnode, NULL);
- vty_out(vty, " echo-interval %" PRIu32 "\n", value / 1000);
+ vty_out(vty, " echo-interval %u\n", value / 1000);
}
}
/*
* Profile commands.
*/
-DEFPY_NOSH(bfd_profile, bfd_profile_cmd,
+DEFPY_YANG_NOSH(bfd_profile, bfd_profile_cmd,
"profile WORD$name",
BFD_PROFILE_STR
BFD_PROFILE_NAME_STR)
@@ -419,7 +476,7 @@ DEFPY_NOSH(bfd_profile, bfd_profile_cmd,
return CMD_SUCCESS;
}
-DEFPY(no_bfd_profile, no_bfd_profile_cmd,
+DEFPY_YANG(no_bfd_profile, no_bfd_profile_cmd,
"no profile BFDPROF$name",
NO_STR
BFD_PROFILE_STR
@@ -442,37 +499,53 @@ void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, "./name"));
}
-ALIAS(bfd_peer_mult, bfd_profile_mult_cmd,
+ALIAS_YANG(bfd_peer_mult, bfd_profile_mult_cmd,
"detect-multiplier (2-255)$multiplier",
"Configure peer detection multiplier\n"
"Configure peer detection multiplier value\n")
-ALIAS(bfd_peer_tx, bfd_profile_tx_cmd,
+ALIAS_YANG(bfd_peer_tx, bfd_profile_tx_cmd,
"transmit-interval (10-60000)$interval",
"Configure peer transmit interval\n"
"Configure peer transmit interval value in milliseconds\n")
-ALIAS(bfd_peer_rx, bfd_profile_rx_cmd,
+ALIAS_YANG(bfd_peer_rx, bfd_profile_rx_cmd,
"receive-interval (10-60000)$interval",
"Configure peer receive interval\n"
"Configure peer receive interval value in milliseconds\n")
-ALIAS(bfd_peer_shutdown, bfd_profile_shutdown_cmd,
+ALIAS_YANG(bfd_peer_shutdown, bfd_profile_shutdown_cmd,
"[no] shutdown",
NO_STR
"Disable BFD peer\n")
-ALIAS(bfd_peer_echo, bfd_profile_echo_cmd,
+ALIAS_YANG(bfd_peer_passive, bfd_profile_passive_cmd,
+ "[no] passive-mode",
+ NO_STR
+ "Don't attempt to start sessions\n")
+
+ALIAS_YANG(bfd_peer_minimum_ttl, bfd_profile_minimum_ttl_cmd,
+ "[no] minimum-ttl (1-254)$ttl",
+ NO_STR
+ "Expect packets with at least this TTL\n"
+ "Minimum TTL expected\n")
+
+ALIAS_YANG(no_bfd_peer_minimum_ttl, no_bfd_profile_minimum_ttl_cmd,
+ "no minimum-ttl",
+ NO_STR
+ "Expect packets with at least this TTL\n")
+
+ALIAS_YANG(bfd_peer_echo, bfd_profile_echo_cmd,
"[no] echo-mode",
NO_STR
"Configure echo mode\n")
-ALIAS(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd,
+ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd,
"echo-interval (10-60000)$interval",
"Configure peer echo interval\n"
"Configure peer echo interval value in milliseconds\n")
-DEFPY(bfd_peer_profile, bfd_peer_profile_cmd,
+DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd,
"[no] profile BFDPROF$pname",
NO_STR
"Use BFD profile settings\n"
@@ -530,6 +603,9 @@ bfdd_cli_init(void)
install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_interval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_profile_cmd);
+ install_element(BFD_PEER_NODE, &bfd_peer_passive_cmd);
+ install_element(BFD_PEER_NODE, &bfd_peer_minimum_ttl_cmd);
+ install_element(BFD_PEER_NODE, &no_bfd_peer_minimum_ttl_cmd);
/* Profile commands. */
cmd_variable_handler_register(bfd_vars);
@@ -546,4 +622,7 @@ bfdd_cli_init(void)
install_element(BFD_PROFILE_NODE, &bfd_profile_shutdown_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_interval_cmd);
+ install_element(BFD_PROFILE_NODE, &bfd_profile_passive_cmd);
+ install_element(BFD_PROFILE_NODE, &bfd_profile_minimum_ttl_cmd);
+ install_element(BFD_PROFILE_NODE, &no_bfd_profile_minimum_ttl_cmd);
}
diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c
index 2ff99ca608..64ba3cf811 100644
--- a/bfdd/bfdd_nb.c
+++ b/bfdd/bfdd_nb.c
@@ -78,6 +78,21 @@ const struct frr_yang_module_info frr_bfdd_info = {
}
},
{
+ .xpath = "/frr-bfdd:bfdd/bfd/profile/passive-mode",
+ .cbs = {
+ .modify = bfdd_bfd_profile_passive_mode_modify,
+ .cli_show = bfd_cli_show_passive,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/profile/minimum-ttl",
+ .cbs = {
+ .modify = bfdd_bfd_profile_minimum_ttl_modify,
+ .destroy = bfdd_bfd_profile_minimum_ttl_destroy,
+ .cli_show = bfd_cli_show_minimum_ttl,
+ }
+ },
+ {
.xpath = "/frr-bfdd:bfdd/bfd/profile/echo-mode",
.cbs = {
.modify = bfdd_bfd_profile_echo_mode_modify,
@@ -147,6 +162,13 @@ const struct frr_yang_module_info frr_bfdd_info = {
}
},
{
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify,
+ .cli_show = bfd_cli_show_passive,
+ }
+ },
+ {
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_echo_mode_modify,
@@ -329,6 +351,21 @@ const struct frr_yang_module_info frr_bfdd_info = {
}
},
{
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/passive-mode",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify,
+ .cli_show = bfd_cli_show_passive,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify,
+ .destroy = bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy,
+ .cli_show = bfd_cli_show_minimum_ttl,
+ }
+ },
+ {
.xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator",
.cbs = {
.get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem,
diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h
index a379c2135e..fbd557b6b1 100644
--- a/bfdd/bfdd_nb.h
+++ b/bfdd/bfdd_nb.h
@@ -37,6 +37,9 @@ int bfdd_bfd_profile_desired_transmission_interval_modify(
int bfdd_bfd_profile_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args);
+int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args);
+int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args);
+int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args);
int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args);
@@ -62,6 +65,8 @@ int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_administrative_down_modify(
struct nb_cb_modify_args *args);
+int bfdd_bfd_sessions_single_hop_passive_mode_modify(
+ struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_echo_mode_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
@@ -131,6 +136,10 @@ int bfdd_bfd_sessions_multi_hop_required_receive_interval_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_multi_hop_administrative_down_modify(
struct nb_cb_modify_args *args);
+int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify(
+ struct nb_cb_modify_args *args);
+int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy(
+ struct nb_cb_destroy_args *args);
struct yang_data *
bfdd_bfd_sessions_multi_hop_stats_local_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
@@ -206,5 +215,9 @@ void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_peer_profile_show(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
#endif /* _FRR_BFDD_NB_H_ */
diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c
index 915a2f6419..0046bc625b 100644
--- a/bfdd/bfdd_nb_config.c
+++ b/bfdd/bfdd_nb_config.c
@@ -85,8 +85,7 @@ static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode,
if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)
&& strlen(ifname) == 0) {
zlog_warn(
- "%s: when using link-local you must specify "
- "an interface.",
+ "%s: when using link-local you must specify an interface.",
__func__);
return NB_ERR_VALIDATION;
}
@@ -361,6 +360,64 @@ int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args)
}
/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/passive-mode
+ */
+int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+ bool passive;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ passive = yang_dnode_get_bool(args->dnode, NULL);
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ if (bp->passive == passive)
+ return NB_OK;
+
+ bp->passive = passive;
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/minimum-ttl
+ */
+int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
+ uint8_t minimum_ttl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ if (bp->minimum_ttl == minimum_ttl)
+ return NB_OK;
+
+ bp->minimum_ttl = minimum_ttl;
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args)
+{
+ struct bfd_profile *bp;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ bp->minimum_ttl = BFD_DEF_MHOP_TTL;
+ bfd_profile_update(bp);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-bfdd:bfdd/bfd/profile/echo-mode
*/
int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args)
@@ -498,8 +555,8 @@ int bfdd_bfd_sessions_single_hop_detection_multiplier_modify(
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
- bs->detect_mult = detection_multiplier;
bs->peer_profile.detection_multiplier = detection_multiplier;
+ bfd_session_apply(bs);
break;
case NB_EV_ABORT:
@@ -534,9 +591,8 @@ int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify(
if (tx_interval == bs->timers.desired_min_tx)
return NB_OK;
- bs->timers.desired_min_tx = tx_interval;
bs->peer_profile.min_tx = tx_interval;
- bfd_set_polling(bs);
+ bfd_session_apply(bs);
break;
case NB_EV_ABORT:
@@ -571,9 +627,8 @@ int bfdd_bfd_sessions_single_hop_required_receive_interval_modify(
if (rx_interval == bs->timers.required_min_rx)
return NB_OK;
- bs->timers.required_min_rx = rx_interval;
bs->peer_profile.min_rx = rx_interval;
- bfd_set_polling(bs);
+ bfd_session_apply(bs);
break;
case NB_EV_ABORT:
@@ -607,7 +662,37 @@ int bfdd_bfd_sessions_single_hop_administrative_down_modify(
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.admin_shutdown = shutdown;
- bfd_set_shutdown(bs, shutdown);
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode
+ */
+int bfdd_bfd_sessions_single_hop_passive_mode_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+ bool passive;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ passive = yang_dnode_get_bool(args->dnode, NULL);
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.passive = passive;
+ bfd_session_apply(bs);
return NB_OK;
}
@@ -635,7 +720,7 @@ int bfdd_bfd_sessions_single_hop_echo_mode_modify(
bs = nb_running_get_entry(args->dnode, NULL, true);
bs->peer_profile.echo_mode = echo;
- bfd_set_echo(bs, echo);
+ bfd_session_apply(bs);
return NB_OK;
}
@@ -665,8 +750,8 @@ int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
if (echo_interval == bs->timers.required_min_echo)
return NB_OK;
- bs->timers.required_min_echo = echo_interval;
bs->peer_profile.min_echo_rx = echo_interval;
+ bfd_session_apply(bs);
break;
case NB_EV_ABORT:
@@ -690,3 +775,54 @@ int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args)
{
return bfd_session_destroy(args->event, args->dnode, true);
}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl
+ */
+int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL);
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
+
+int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ return NB_OK;
+
+ case NB_EV_APPLY:
+ break;
+
+ case NB_EV_ABORT:
+ return NB_OK;
+ }
+
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ bs->peer_profile.minimum_ttl = BFD_DEF_MHOP_TTL;
+ bfd_session_apply(bs);
+
+ return NB_OK;
+}
diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c
index f22e9277a1..a3f1638e5f 100644
--- a/bfdd/bfdd_vty.c
+++ b/bfdd/bfdd_vty.c
@@ -111,6 +111,12 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs)
vty_out(vty, "\t\tID: %u\n", bs->discrs.my_discr);
vty_out(vty, "\t\tRemote ID: %u\n", bs->discrs.remote_discr);
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
+ vty_out(vty, "\t\tPassive mode\n");
+ else
+ vty_out(vty, "\t\tActive mode\n");
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
+ vty_out(vty, "\t\tMinimum TTL: %d\n", bs->mh_ttl);
vty_out(vty, "\t\tStatus: ");
switch (bs->ses_state) {
@@ -146,23 +152,23 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs)
CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic");
vty_out(vty, "\t\tLocal timers:\n");
- vty_out(vty, "\t\t\tDetect-multiplier: %" PRIu32 "\n",
+ vty_out(vty, "\t\t\tDetect-multiplier: %u\n",
bs->detect_mult);
- vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tReceive interval: %ums\n",
bs->timers.required_min_rx / 1000);
- vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tTransmission interval: %ums\n",
bs->timers.desired_min_tx / 1000);
- vty_out(vty, "\t\t\tEcho transmission interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tEcho transmission interval: %ums\n",
bs->timers.required_min_echo / 1000);
vty_out(vty, "\t\tRemote timers:\n");
- vty_out(vty, "\t\t\tDetect-multiplier: %" PRIu32 "\n",
+ vty_out(vty, "\t\t\tDetect-multiplier: %u\n",
bs->remote_detect_mult);
- vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tReceive interval: %ums\n",
bs->remote_timers.required_min_rx / 1000);
- vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tTransmission interval: %ums\n",
bs->remote_timers.desired_min_tx / 1000);
- vty_out(vty, "\t\t\tEcho transmission interval: %" PRIu32 "ms\n",
+ vty_out(vty, "\t\t\tEcho transmission interval: %ums\n",
bs->remote_timers.required_min_echo / 1000);
vty_out(vty, "\n");
@@ -203,6 +209,10 @@ static struct json_object *__display_peer_json(struct bfd_session *bs)
json_object_int_add(jo, "id", bs->discrs.my_discr);
json_object_int_add(jo, "remote-id", bs->discrs.remote_discr);
+ json_object_boolean_add(jo, "passive-mode",
+ CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE));
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
+ json_object_int_add(jo, "minimum-ttl", bs->mh_ttl);
switch (bs->ses_state) {
case PTM_BFD_ADM_DOWN:
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
index 1a23a690e0..48e55bce37 100644
--- a/bfdd/ptm_adapter.c
+++ b/bfdd/ptm_adapter.c
@@ -152,8 +152,7 @@ static void _ptm_bfd_session_del(struct bfd_session *bs, uint8_t diag)
*/
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) {
zlog_err(
- "ptm-del-session: [%s] session refcount is "
- "zero but it was configured by CLI",
+ "ptm-del-session: [%s] session refcount is zero but it was configured by CLI",
bs_to_string(bs));
} else {
control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
@@ -304,7 +303,6 @@ static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
struct bfd_peer_cfg *bpc, struct ptm_client **pc)
{
uint32_t pid;
- uint8_t ttl __attribute__((unused));
size_t ifnamelen;
/*
@@ -376,7 +374,18 @@ static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
if (bpc->bpc_mhop) {
/* Read multihop source address and TTL. */
_ptm_msg_read_address(msg, &bpc->bpc_local);
- STREAM_GETC(msg, ttl);
+ STREAM_GETC(msg, bpc->bpc_minimum_ttl);
+ if (bpc->bpc_minimum_ttl >= BFD_TTL_VAL
+ || bpc->bpc_minimum_ttl == 0) {
+ zlog_warn("%s: received invalid TTL configuration %d",
+ __func__, bpc->bpc_has_minimum_ttl);
+ bpc->bpc_minimum_ttl = BFD_DEF_MHOP_TTL;
+ bpc->bpc_has_minimum_ttl = false;
+ } else {
+ bpc->bpc_minimum_ttl =
+ (BFD_TTL_VAL + 1) - bpc->bpc_minimum_ttl;
+ bpc->bpc_has_minimum_ttl = true;
+ }
} else {
/* If target is IPv6, then we must obtain local address. */
if (bpc->bpc_ipv4 == false)
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index 44962f5af3..5cf3c60fa2 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -1723,8 +1723,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath,
if (hops < seg->length) {
if (BGP_DEBUG(as4, AS4))
zlog_debug(
- "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls"
- " across 2/4 ASN boundary somewhere, broken..");
+ "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls across 2/4 ASN boundary somewhere, broken..");
hops = seg->length;
}
/* fallthru */
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index d8566fed9f..2c7091f2e4 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -718,6 +718,9 @@ bool attrhash_cmp(const void *p1, const void *p2)
&& IPV4_ADDR_SAME(&attr1->originator_id,
&attr2->originator_id)
&& overlay_index_same(attr1, attr2)
+ && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t))
+ && attr1->es_flags == attr2->es_flags
+ && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum
&& attr1->nh_ifindex == attr2->nh_ifindex
&& attr1->nh_lla_ifindex == attr2->nh_lla_ifindex
&& attr1->distance == attr2->distance
@@ -765,8 +768,7 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty)
inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ);
vty_out(vty,
- "\tflags: %" PRIu64
- " med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n",
+ "\tflags: %" PRIu64" med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n",
attr->flag, attr->med, attr->local_pref, attr->origin,
attr->weight, attr->label, sid_str);
}
@@ -1148,6 +1150,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
bgp_size_t length)
{
struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
const uint8_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,
@@ -1155,6 +1158,14 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
*/
uint8_t *notify_datap = (length > 0 ? args->startp : NULL);
+ if (bgp_debug_update(peer, NULL, NULL, 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, sizeof(attr_str));
+
+ zlog_debug("%s: attributes: %s", __func__, attr_str);
+ }
+
/* Only relax error handling for eBGP peers */
if (peer->sort != BGP_PEER_EBGP) {
bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode,
@@ -1244,8 +1255,7 @@ bgp_attr_flags_diagnose(struct bgp_attr_parser_args *args,
}
if (!seen) {
zlog_debug(
- "Strange, %s called for attr %s, but no problem found with flags"
- " (real flags 0x%x, desired 0x%x)",
+ "Strange, %s called for attr %s, but no problem found with flags (real flags 0x%x, desired 0x%x)",
__func__, lookup_msg(attr_str, attr_code, NULL),
real_flags, desired_flags);
}
@@ -1311,16 +1321,14 @@ static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args)
if (CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL)) {
if (!CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)) {
flog_err(EC_BGP_ATTR_FLAG,
- "%s well-known attribute "
- "must NOT have the partial flag set (%x)",
+ "%s well-known attribute must NOT have the partial flag set (%x)",
lookup_msg(attr_str, attr_code, NULL), flags);
return true;
}
if (CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)
&& !CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS)) {
flog_err(EC_BGP_ATTR_FLAG,
- "%s optional + transitive attribute "
- "must NOT have the partial flag set (%x)",
+ "%s optional + transitive attribute must NOT have the partial flag set (%x)",
lookup_msg(attr_str, attr_code, NULL), flags);
return true;
}
@@ -1773,10 +1781,7 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr,
/* 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",
+ "[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 {
@@ -1794,9 +1799,7 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr,
*/
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",
+ "[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);
attr->aggregator_as = as4_aggregator;
/* sweep it under the carpet and simulate a "good"
@@ -2195,6 +2198,7 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
uint8_t sticky = 0;
+ bool proxy = false;
if (length == 0) {
attr->ecommunity = NULL;
@@ -2232,7 +2236,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
attr->router_flag = 1;
/* Check EVPN Neighbor advertisement flags, R-bit */
- bgp_attr_evpn_na_flag(attr, &attr->router_flag);
+ bgp_attr_evpn_na_flag(attr, &attr->router_flag, &proxy);
+ if (proxy)
+ attr->es_flags |= ATTR_ES_PROXY_ADVERT;
/* Extract the Rmac, if any */
if (bgp_attr_rmac(attr, &attr->rmac)) {
@@ -2407,8 +2413,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (STREAM_READABLE(peer->curr) < length
|| length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID label index length is %" PRIu16
- " instead of %u",
+ "Prefix SID label index length is %hu instead of %u",
length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2435,8 +2440,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (STREAM_READABLE(peer->curr) < length
|| length != BGP_PREFIX_SID_IPV6_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID IPv6 length is %" PRIu16
- " instead of %u",
+ "Prefix SID IPv6 length is %hu instead of %u",
length, BGP_PREFIX_SID_IPV6_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2468,7 +2472,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (length < (2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH)) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID Originator SRGB length field claims length of %" PRIu16 " bytes, but the minimum for this TLV type is %u",
+ "Prefix SID Originator SRGB length field claims length of %hu bytes, but the minimum for this TLV type is %u",
length,
2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
return bgp_attr_malformed(
@@ -2482,7 +2486,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
*/
if (STREAM_READABLE(peer->curr) < length) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID Originator SRGB specifies length %" PRIu16 ", but only %zu bytes remain",
+ "Prefix SID Originator SRGB specifies length %hu, but only %zu bytes remain",
length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(
args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2499,7 +2503,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %" PRIu16 "bytes, but it must be a multiple of %u",
+ "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %hubytes, but it must be a multiple of %u",
length, BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
return bgp_attr_malformed(
args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2519,8 +2523,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (STREAM_READABLE(peer->curr) < length
|| length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID VPN SID length is %" PRIu16
- " instead of %u",
+ "Prefix SID VPN SID length is %hu instead of %u",
length, BGP_PREFIX_SID_VPN_SID_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2560,8 +2563,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (STREAM_READABLE(peer->curr) < length
|| length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 L3-Service length is %" PRIu16
- " instead of %u",
+ "Prefix SID SRv6 L3-Service length is %hu instead of %u",
length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2604,8 +2606,7 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
if (STREAM_READABLE(peer->curr) < length) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 length is %" PRIu16
- " - too long, only %zu remaining in this UPDATE",
+ "Prefix SID SRv6 length is %hu - too long, only %zu remaining in this UPDATE",
length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(
args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2658,8 +2659,7 @@ bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
if (STREAM_READABLE(peer->curr) < length) {
flog_err(
EC_BGP_ATTR_LEN,
- "Malformed Prefix SID attribute - insufficient data (need %" PRIu16
- " for attribute body, have %zu remaining in UPDATE)",
+ "Malformed Prefix SID attribute - insufficient data (need %hu for attribute body, have %zu remaining in UPDATE)",
length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2676,8 +2676,7 @@ bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
if (psid_parsed_length > args->length) {
flog_err(
EC_BGP_ATTR_LEN,
- "Malformed Prefix SID attribute - TLV overflow by attribute (need %zu"
- " for TLV length, have %zu overflowed in UPDATE)",
+ "Malformed Prefix SID attribute - TLV overflow by attribute (need %zu for TLV length, have %zu overflowed in UPDATE)",
length + headersz, psid_parsed_length - (length + headersz));
return bgp_attr_malformed(
args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 94531313ae..1b2c75fbef 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -215,6 +215,30 @@ struct attr {
/* NA router flag (R-bit) support in EVPN */
uint8_t router_flag;
+ /* ES info */
+ uint8_t es_flags;
+ /* Path is not "locally-active" on the advertising VTEP. This is
+ * translated into an ARP-ND ECOM.
+ */
+#define ATTR_ES_PROXY_ADVERT (1 << 0)
+ /* Destination ES is present locally. This flag is set on local
+ * paths and sync paths
+ */
+#define ATTR_ES_IS_LOCAL (1 << 1)
+ /* There are one or more non-best paths from ES peers. Note that
+ * this flag is only set on the local MAC-IP paths in the VNI
+ * route table (not set in the global routing table). And only
+ * non-proxy advertisements from an ES peer can result in this
+ * flag being set.
+ */
+#define ATTR_ES_PEER_ACTIVE (1 << 2)
+ /* There are one or more non-best proxy paths from ES peers */
+#define ATTR_ES_PEER_PROXY (1 << 3)
+ /* An ES peer has router bit set - only applicable if
+ * ATTR_ES_PEER_ACTIVE is set
+ */
+#define ATTR_ES_PEER_ROUTER (1 << 4)
+
/* route tag */
route_tag_t tag;
@@ -241,6 +265,13 @@ struct attr {
/* EVPN MAC Mobility sequence number, if any. */
uint32_t mm_seqnum;
+ /* highest MM sequence number rxed in a MAC-IP route from an
+ * ES peer (this includes both proxy and non-proxy MAC-IP
+ * advertisements from ES peers).
+ * This is only applicable to local paths in the VNI routing
+ * table and derived from other imported/non-best paths.
+ */
+ uint32_t mm_sync_seqnum;
/* EVPN local router-mac */
struct ethaddr rmac;
@@ -253,6 +284,9 @@ struct attr {
/* Link bandwidth value, if any. */
uint32_t link_bw;
+
+ /* EVPN ES */
+ esi_t esi;
};
/* rmap_change_flags definition */
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 65072088ae..aa0c59f3a7 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -54,47 +54,27 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
* format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ
* if id is null, check only is done
*/
-bool str2esi(const char *str, struct eth_segment_id *id)
+bool str2esi(const char *str, esi_t *id)
{
- unsigned int a[ESI_LEN];
+ unsigned int a[ESI_BYTES];
int i;
if (!str)
return false;
if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1,
a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9)
- != ESI_LEN) {
+ != ESI_BYTES) {
/* error in incoming str length */
return false;
}
/* valid mac address */
if (!id)
return true;
- for (i = 0; i < ESI_LEN; ++i)
+ for (i = 0; i < ESI_BYTES; ++i)
id->val[i] = a[i] & 0xff;
return true;
}
-char *esi2str(struct eth_segment_id *id)
-{
- char *ptr;
- uint8_t *val;
-
- if (!id)
- return NULL;
-
- val = id->val;
- ptr = XMALLOC(MTYPE_TMP,
- (ESI_LEN * 2 + ESI_LEN - 1 + 1) * sizeof(char));
-
- snprintf(ptr, (ESI_LEN * 2 + ESI_LEN - 1 + 1),
- "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", val[0],
- val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8],
- val[9]);
-
- return ptr;
-}
-
char *ecom_mac2str(char *ecom_mac)
{
char *en;
@@ -215,7 +195,8 @@ uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky)
/*
* return true if attr contains router flag extended community
*/
-void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag)
+void bgp_attr_evpn_na_flag(struct attr *attr,
+ uint8_t *router_flag, bool *proxy)
{
struct ecommunity *ecom;
int i;
@@ -237,10 +218,14 @@ void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag)
if (type == ECOMMUNITY_ENCODE_EVPN &&
sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) {
val = *pnt++;
- if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) {
+
+ if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
*router_flag = 1;
- break;
- }
+
+ if (val & ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)
+ *proxy = true;
+
+ break;
}
}
}
@@ -292,14 +277,3 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi)
return false;
}
-
-extern bool is_zero_esi(const struct eth_segment_id *esi)
-{
- int i;
-
- for (i = 0; i < ESI_LEN; i++)
- if (esi->val[i])
- return false;
-
- return true;
-}
diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h
index c1bfd83765..19c028a826 100644
--- a/bgpd/bgp_attr_evpn.h
+++ b/bgpd/bgp_attr_evpn.h
@@ -21,38 +21,20 @@
#ifndef _QUAGGA_BGP_ATTR_EVPN_H
#define _QUAGGA_BGP_ATTR_EVPN_H
-/* value of first byte of ESI */
-#define ESI_TYPE_ARBITRARY 0 /* */
-#define ESI_TYPE_LACP 1 /* <> */
-#define ESI_TYPE_BRIDGE 2 /* <Root bridge Mac-6B>:<Root Br Priority-2B>:00 */
-#define ESI_TYPE_MAC 3 /* <Syst Mac Add-6B>:<Local Discriminator Value-3B> */
-#define ESI_TYPE_ROUTER 4 /* <RouterId-4B>:<Local Discriminator Value-4B> */
-#define ESI_TYPE_AS 5 /* <AS-4B>:<Local Discriminator Value-4B> */
-
-#define MAX_ESI {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
-#define ESI_LEN 10
-
#define MAX_ET 0xffffffff
struct attr;
-/* EVPN ESI */
-struct eth_segment_id {
- uint8_t val[ESI_LEN];
-};
-
union gw_addr {
struct in_addr ipv4;
struct in6_addr ipv6;
};
struct bgp_route_evpn {
- struct eth_segment_id eth_s_id;
union gw_addr gw_ip;
};
-extern bool str2esi(const char *str, struct eth_segment_id *id);
-extern char *esi2str(struct eth_segment_id *id);
+extern bool str2esi(const char *str, esi_t *id);
extern char *ecom_mac2str(char *ecom_mac);
extern void bgp_add_routermac_ecom(struct attr *attr,
@@ -64,9 +46,9 @@ extern uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr,
uint8_t *sticky);
extern uint8_t bgp_attr_default_gw(struct attr *attr);
-extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag);
+extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag,
+ bool *proxy);
extern bool is_zero_gw_ip(const union gw_addr *gw_ip, afi_t afi);
-extern bool is_zero_esi(const struct eth_segment_id *esi);
#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index 54970af67b..67b8018c8e 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -351,8 +351,7 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status,
if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) &&
CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) &&
!remote_cbit) {
- zlog_info("%s BFD DOWN message ignored in the process"
- " of graceful restart when C bit is cleared",
+ zlog_info("%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
peer->host);
return;
}
@@ -487,7 +486,7 @@ static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx,
int command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
/* This command overrides profile if it was previously applied. */
bi = peer->bfd_info;
@@ -498,8 +497,8 @@ static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx,
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
- min_rx, min_tx, detect_mult, defaults,
- &command);
+ min_rx, min_tx, detect_mult, NULL,
+ defaults, &command);
/*
* This command overrides profile if it was previously
@@ -565,7 +564,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer,
if (!peer->bfd_info)
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
bfd_info->type = type;
@@ -578,7 +577,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer,
bfd_set_param(
(struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
bfd_info->type = type;
@@ -613,7 +612,7 @@ static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile)
struct bfd_info *bfd_info;
bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX,
- BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
@@ -629,7 +628,7 @@ static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile)
command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index f1ad6a1e75..af88547ca9 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -951,8 +951,11 @@ afibreak:
/* initialize syncrdpos to the first
* mid-layer table entry
*/
- if (!bmp->syncrdpos)
+ if (!bmp->syncrdpos) {
bmp->syncrdpos = bgp_table_top(table);
+ if (!bmp->syncrdpos)
+ goto eor;
+ }
/* look for a valid mid-layer table */
do {
@@ -1942,9 +1945,7 @@ DEFPY(no_bmp_listener_main,
DEFPY(bmp_connect,
bmp_connect_cmd,
- "[no] bmp connect HOSTNAME port (1-65535) "
- "{min-retry (100-86400000)"
- "|max-retry (100-86400000)}",
+ "[no] bmp connect HOSTNAME port (1-65535) {min-retry (100-86400000)|max-retry (100-86400000)}",
NO_STR
BMP_STR
"Actively establish connection to monitoring station\n"
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index 97d625493f..565d0b8e19 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -50,6 +50,12 @@ static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
unsigned int i;
int index;
+ /*
+ * reuse_limit can't be zero, this is for Coverity
+ * to bypass division by zero test.
+ */
+ assert(bdc->reuse_limit);
+
i = (int)(((double)penalty / bdc->reuse_limit - 1.0)
* bdc->scale_factor);
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index f3d387a0e1..255a7f238b 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -30,7 +30,6 @@
#include "memory.h"
#include "queue.h"
#include "filter.h"
-#include "hook.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_aspath.h"
@@ -48,9 +47,6 @@
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_flowspec.h"
-DEFINE_HOOK(bgp_hook_config_write_debug, (struct vty *vty, bool running),
- (vty, running))
-
unsigned long conf_bgp_debug_as4;
unsigned long conf_bgp_debug_neighbor_events;
unsigned long conf_bgp_debug_events;
@@ -68,6 +64,7 @@ unsigned long conf_bgp_debug_flowspec;
unsigned long conf_bgp_debug_labelpool;
unsigned long conf_bgp_debug_pbr;
unsigned long conf_bgp_debug_graceful_restart;
+unsigned long conf_bgp_debug_evpn_mh;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
@@ -86,6 +83,7 @@ unsigned long term_bgp_debug_flowspec;
unsigned long term_bgp_debug_labelpool;
unsigned long term_bgp_debug_pbr;
unsigned long term_bgp_debug_graceful_restart;
+unsigned long term_bgp_debug_evpn_mh;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
@@ -2010,6 +2008,57 @@ DEFUN (no_debug_bgp_pbr,
return CMD_SUCCESS;
}
+DEFPY (debug_bgp_evpn_mh,
+ debug_bgp_evpn_mh_cmd,
+ "[no$no] debug bgp evpn mh <es$es|route$rt>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "EVPN\n"
+ "Multihoming\n"
+ "Ethernet Segment debugging\n"
+ "Route debugging\n")
+{
+ if (es) {
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ else
+ DEBUG_ON(evpn_mh, EVPN_MH_ES);
+ } else {
+ if (no) {
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ vty_out(vty,
+ "BGP EVPN-MH ES debugging is off\n");
+ } else {
+ TERM_DEBUG_ON(evpn_mh, EVPN_MH_ES);
+ vty_out(vty,
+ "BGP EVPN-MH ES debugging is on\n");
+ }
+ }
+ }
+ if (rt) {
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(evpn_mh, EVPN_MH_RT);
+ else
+ DEBUG_ON(evpn_mh, EVPN_MH_RT);
+ } else {
+ if (no) {
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
+ vty_out(vty,
+ "BGP EVPN-MH route debugging is off\n");
+ } else {
+ TERM_DEBUG_ON(evpn_mh, EVPN_MH_RT);
+ vty_out(vty,
+ "BGP EVPN-MH route debugging is on\n");
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (debug_bgp_labelpool,
debug_bgp_labelpool_cmd,
"debug bgp labelpool",
@@ -2089,6 +2138,8 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(pbr, PBR);
TERM_DEBUG_OFF(pbr, PBR_ERROR);
TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
vty_out(vty, "All possible debugging has been turned off\n");
@@ -2148,7 +2199,7 @@ DEFUN_NOSH (show_debugging_bgp,
bgp_debug_zebra_prefixes);
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
- vty_out(vty, " BGP graceful-restart debugging is on");
+ vty_out(vty, " BGP graceful-restart debugging is on\n");
if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
vty_out(vty, " BGP allow martian next hop debugging is on\n");
@@ -2172,7 +2223,12 @@ DEFUN_NOSH (show_debugging_bgp,
vty_out(vty, " BGP policy based routing debugging is on\n");
if (BGP_DEBUG(pbr, PBR_ERROR))
vty_out(vty, " BGP policy based routing error debugging is on\n");
- hook_call(bgp_hook_config_write_debug, vty, false);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ vty_out(vty, " BGP EVPN-MH ES debugging is on\n");
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ vty_out(vty, " BGP EVPN-MH route debugging is on\n");
+
vty_out(vty, "\n");
return CMD_SUCCESS;
}
@@ -2289,8 +2345,15 @@ static int bgp_config_write_debug(struct vty *vty)
write++;
}
- if (hook_call(bgp_hook_config_write_debug, vty, true))
+ if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ vty_out(vty, "debug bgp evpn mh es\n");
write++;
+ }
+ if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ vty_out(vty, "debug bgp evpn mh route\n");
+ write++;
+ }
+
return write;
}
@@ -2417,6 +2480,8 @@ void bgp_debug_init(void)
install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index e021f19c45..f16cfee4f2 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -21,15 +21,9 @@
#ifndef _QUAGGA_BGP_DEBUG_H
#define _QUAGGA_BGP_DEBUG_H
-#include "hook.h"
-#include "vty.h"
-
#include "bgp_attr.h"
#include "bgp_updgrp.h"
-DECLARE_HOOK(bgp_hook_config_write_debug, (struct vty *vty, bool running),
- (vty, running))
-
/* sort of packet direction */
#define DUMP_ON 1
#define DUMP_SEND 2
@@ -83,6 +77,7 @@ extern unsigned long conf_bgp_debug_flowspec;
extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long conf_bgp_debug_pbr;
extern unsigned long conf_bgp_debug_graceful_restart;
+extern unsigned long conf_bgp_debug_evpn_mh;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
@@ -99,6 +94,7 @@ extern unsigned long term_bgp_debug_flowspec;
extern unsigned long term_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_pbr;
extern unsigned long term_bgp_debug_graceful_restart;
+extern unsigned long term_bgp_debug_evpn_mh;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
@@ -135,6 +131,8 @@ struct bgp_debug_filter {
#define BGP_DEBUG_LABELPOOL 0x01
#define BGP_DEBUG_PBR 0x01
#define BGP_DEBUG_PBR_ERROR 0x02
+#define BGP_DEBUG_EVPN_MH_ES 0x01
+#define BGP_DEBUG_EVPN_MH_RT 0x02
#define BGP_DEBUG_PACKET_SEND 0x01
#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 7d5cac4d62..f2aac3646c 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -676,13 +676,10 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
are three types of format.
route-map set extcommunity format
- "rt 100:1 100:2"
- "soo 100:3"
+ "rt 100:1 100:2soo 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:3show [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:
@@ -813,6 +810,35 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
strlcpy(encbuf, "ND:Router Flag",
sizeof(encbuf));
+ if (CHECK_FLAG(
+ flags,
+ ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
+ strlcpy(encbuf, "ND:Proxy",
+ sizeof(encbuf));
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
+ struct ethaddr mac;
+
+ pnt++;
+ memcpy(&mac, pnt, ETH_ALEN);
+ snprintf(encbuf,
+ sizeof(encbuf),
+ "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)mac.octet[0],
+ (uint8_t)mac.octet[1],
+ (uint8_t)mac.octet[2],
+ (uint8_t)mac.octet[3],
+ (uint8_t)mac.octet[4],
+ (uint8_t)mac.octet[5]);
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
+ uint8_t flags = *++pnt;
+
+ snprintf(encbuf,
+ sizeof(encbuf), "ESI-label-Rt:%s",
+ (flags &
+ ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
+ "SA":"AA");
} else
unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
@@ -868,21 +894,6 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
} else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
snprintf(encbuf, sizeof(encbuf),
"FS:marking %u", *(pnt + 5));
- } else if (*pnt
- == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
- struct ethaddr mac;
-
- memcpy(&mac, pnt, ETH_ALEN);
-
- snprintf(
- encbuf, sizeof(encbuf),
- "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
- (uint8_t)mac.octet[0],
- (uint8_t)mac.octet[1],
- (uint8_t)mac.octet[2],
- (uint8_t)mac.octet[3],
- (uint8_t)mac.octet[4],
- (uint8_t)mac.octet[5]);
} else
unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 7deae8e746..812bcc46e7 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -73,8 +73,12 @@
#define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01
-#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01
-#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02
+
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02
+#define ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG 0x04
+
+#define ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG (1 << 0) /* single-active */
/* Low-order octet of the Extended Communities type field for OPAQUE types */
#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 8c5d6421f1..54593497cc 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -40,6 +40,7 @@
#include "bgpd/bgp_label.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_debug.h"
@@ -54,61 +55,24 @@
/*
* Definitions and external declarations.
*/
-extern struct zclient *zclient;
-
DEFINE_QOBJ_TYPE(bgpevpn)
-DEFINE_QOBJ_TYPE(evpnes)
+DEFINE_QOBJ_TYPE(bgp_evpn_es)
/*
* Static function declarations
*/
-static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
- struct bgp_dest *dest,
- struct bgp_path_info **pi);
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ struct bgp_node *rn, struct bgp_path_info *local_pi,
+ const char *caller);
+static struct in_addr zero_vtep_ip;
/*
* Private functions.
*/
-/* compare two IPV4 VTEP IPs */
-static int evpn_vtep_ip_cmp(void *p1, void *p2)
-{
- const struct in_addr *ip1 = p1;
- const struct in_addr *ip2 = p2;
-
- return ip1->s_addr - ip2->s_addr;
-}
-
-/*
- * Make hash key for ESI.
- */
-static unsigned int esi_hash_keymake(const void *p)
-{
- const struct evpnes *pes = p;
- const void *pnt = (void *)pes->esi.val;
-
- return jhash(pnt, ESI_BYTES, 0xa5a5a55a);
-}
-
-/*
- * Compare two ESIs.
- */
-static bool esi_cmp(const void *p1, const void *p2)
-{
- const struct evpnes *pes1 = p1;
- const struct evpnes *pes2 = p2;
-
- if (pes1 == NULL && pes2 == NULL)
- return true;
-
- if (pes1 == NULL || pes2 == NULL)
- return false;
-
- return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0);
-}
-
/*
* Make vni hash key.
*/
@@ -133,7 +97,7 @@ static bool vni_hash_cmp(const void *p1, const void *p2)
return (vpn1->vni == vpn2->vni);
}
-static int vni_list_cmp(void *p1, void *p2)
+int vni_list_cmp(void *p1, void *p2)
{
const struct bgpevpn *vpn1 = p1;
const struct bgpevpn *vpn2 = p2;
@@ -579,19 +543,54 @@ static void evpn_convert_nexthop_to_ipv6(struct attr *attr)
attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
}
+struct bgp_node *bgp_global_evpn_node_get(
+ struct bgp_table *table, afi_t afi,
+ safi_t safi, const struct prefix_evpn *evp,
+ struct prefix_rd *prd)
+{
+ struct prefix_evpn global_p;
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ /* prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy of the prefix
+ */
+ evpn_type1_prefix_global_copy(&global_p, evp);
+ evp = &global_p;
+ }
+ return bgp_afi_node_get(table, afi, safi, (struct prefix *)evp, prd);
+}
+
+struct bgp_node *bgp_global_evpn_node_lookup(
+ struct bgp_table *table, afi_t afi,
+ safi_t safi, const struct prefix_evpn *evp,
+ struct prefix_rd *prd)
+{
+ struct prefix_evpn global_p;
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ /* prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy of the prefix
+ */
+ evpn_type1_prefix_global_copy(&global_p, evp);
+ evp = &global_p;
+ }
+ return bgp_afi_node_lookup(table, afi, safi, (struct prefix *)evp, prd);
+}
+
/*
* Add (update) or delete MACIP from zebra.
*/
static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
const struct prefix_evpn *p,
struct in_addr remote_vtep_ip, int add,
- uint8_t flags, uint32_t seq)
+ uint8_t flags, uint32_t seq, esi_t *esi)
{
struct stream *s;
int ipa_len;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
char buf3[INET6_ADDRSTRLEN];
+ static struct in_addr zero_remote_vtep_ip;
/* Check socket. */
if (!zclient || zclient->sock < 0)
@@ -605,6 +604,9 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
__func__);
return 0;
}
+
+ if (!esi)
+ esi = zero_esi;
s = zclient->obuf;
stream_reset(s);
@@ -622,13 +624,20 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
stream_putl(s, ipa_len);
stream_put(s, &p->prefix.macip_addr.ip.ip.addr, ipa_len);
}
- stream_put_in_addr(s, &remote_vtep_ip);
+ /* If the ESI is valid that becomes the nexthop; tape out the
+ * VTEP-IP for that case
+ */
+ if (bgp_evpn_is_esi_valid(esi))
+ stream_put_in_addr(s, &zero_remote_vtep_ip);
+ else
+ stream_put_in_addr(s, &remote_vtep_ip);
/* TX flags - MAC sticky status and/or gateway mac */
/* Also TX the sequence number of the best route. */
if (add) {
stream_putc(s, flags);
stream_putl(s, seq);
+ stream_put(s, esi, sizeof(esi_t));
}
stream_putw_at(s, 0, stream_get_endp(s));
@@ -698,40 +707,6 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
- * Build extended community for EVPN ES (type-4) route
- */
-static void build_evpn_type4_route_extcomm(struct evpnes *es,
- struct attr *attr)
-{
- struct ecommunity ecom_encap;
- struct ecommunity ecom_es_rt;
- struct ecommunity_val eval;
- struct ecommunity_val eval_es_rt;
- bgp_encap_types tnl_type;
- struct ethaddr mac;
-
- /* Encap */
- tnl_type = BGP_ENCAP_TYPE_VXLAN;
- memset(&ecom_encap, 0, sizeof(ecom_encap));
- encode_encap_extcomm(tnl_type, &eval);
- ecom_encap.size = 1;
- ecom_encap.val = (uint8_t *)eval.val;
- attr->ecommunity = ecommunity_dup(&ecom_encap);
-
- /* ES import RT */
- memset(&mac, 0, sizeof(struct ethaddr));
- memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
- es_get_system_mac(&es->esi, &mac);
- encode_es_rt_extcomm(&eval_es_rt, &mac);
- ecom_es_rt.size = 1;
- ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
- attr->ecommunity =
- ecommunity_merge(attr->ecommunity, &ecom_es_rt);
-
- attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
-}
-
-/*
* Build extended communities for EVPN prefix route.
*/
static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
@@ -800,6 +775,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
struct ecommunity_val eval_default_gw;
struct ecommunity_val eval_rmac;
struct ecommunity_val eval_na;
+ bool proxy;
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
@@ -861,9 +837,10 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
ecommunity_merge(attr->ecommunity, &ecom_default_gw);
}
- if (attr->router_flag) {
+ proxy = !!(attr->es_flags & ATTR_ES_PROXY_ADVERT);
+ if (attr->router_flag || proxy) {
memset(&ecom_na, 0, sizeof(ecom_na));
- encode_na_flag_extcomm(&eval_na, attr->router_flag);
+ encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy);
ecom_na.size = 1;
ecom_na.val = (uint8_t *)eval_na.val;
attr->ecommunity = ecommunity_merge(attr->ecommunity,
@@ -934,19 +911,60 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
int ret;
uint8_t flags;
int flood_control;
+ uint32_t seq;
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
flags = 0;
- if (pi->attr->sticky)
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
- if (pi->attr->default_gw)
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
- if (is_evpn_prefix_ipaddr_v6(p) &&
- pi->attr->router_flag)
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ if (pi->sub_type == BGP_ROUTE_IMPORTED) {
+ if (pi->attr->sticky)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (pi->attr->default_gw)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ if (is_evpn_prefix_ipaddr_v6(p) &&
+ pi->attr->router_flag)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ seq = mac_mobility_seqnum(pi->attr);
+ /* if local ES notify zebra that this is a sync path */
+ if (bgp_evpn_attr_is_local_es(pi->attr)) {
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH);
+ if (bgp_evpn_attr_is_proxy(pi->attr))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+ } else {
+ if (!bgp_evpn_attr_is_sync(pi->attr))
+ return 0;
+
+ /* if a local path is being turned around and sent
+ * to zebra it is because it is a sync path on
+ * a local ES
+ */
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH);
+ /* supply the highest peer seq number to zebra
+ * for MM seq syncing
+ */
+ seq = bgp_evpn_attr_get_sync_seq(pi->attr);
+ /* if any of the paths from the peer have the ROUTER
+ * flag set install the local entry as a router entry
+ */
+ if (is_evpn_prefix_ipaddr_v6(p) &&
+ (pi->attr->es_flags &
+ ATTR_ES_PEER_ROUTER))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ if (!(pi->attr->es_flags & ATTR_ES_PEER_ACTIVE))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+
ret = bgp_zebra_send_remote_macip(
- bgp, vpn, p, pi->attr->nexthop, 1, flags,
- mac_mobility_seqnum(pi->attr));
+ bgp, vpn, p, pi->attr->nexthop, 1, flags,
+ seq, bgp_evpn_attr_get_esi(pi->attr));
+ } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ ret = bgp_evpn_remote_es_evi_add(bgp, vpn, p);
} else {
switch (pi->attr->pmsi_tnl_type) {
case PMSI_TNLTYPE_INGR_REPL:
@@ -976,7 +994,9 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip,
- 0, 0, 0);
+ 0, 0, 0, NULL);
+ else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ ret = bgp_evpn_remote_es_evi_del(bgp, vpn, p);
else
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p,
VXLAN_FLOOD_DISABLED, 0);
@@ -991,19 +1011,36 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
*/
static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
struct bgp_dest *dest,
- struct bgp_path_info *old_local)
+ struct bgp_path_info *old_local,
+ struct bgp_path_info *new_select)
{
struct bgp_dest *global_dest;
struct bgp_path_info *pi;
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char prefix_buf[PREFIX_STRLEN];
+ char esi_buf[ESI_STR_LEN];
+ char esi_buf2[ESI_STR_LEN];
+ struct prefix_evpn *evp = (struct prefix_evpn *)&dest->p;
+
+ zlog_debug("local path deleted %s es %s; new-path-es %s",
+ prefix2str(evp,
+ prefix_buf, sizeof(prefix_buf)),
+ esi_to_str(&old_local->attr->esi,
+ esi_buf, sizeof(esi_buf)),
+ new_select ? esi_to_str(&new_select->attr->esi,
+ esi_buf2, sizeof(esi_buf2)) : "");
+ }
+
/* Locate route node in the global EVPN routing table. Note that
* this table is a 2-level tree (RD-level + Prefix-level) similar to
* L3VPN routes.
*/
- global_dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
- bgp_dest_get_prefix(dest), &vpn->prd);
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
+ &vpn->prd);
if (global_dest) {
/* Delete route entry in the global EVPN table. */
delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
@@ -1020,172 +1057,12 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
bgp_path_info_delete(dest, old_local);
}
-static struct in_addr *es_vtep_new(struct in_addr vtep)
-{
- struct in_addr *ip;
-
- ip = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(struct in_addr));
-
- ip->s_addr = vtep.s_addr;
- return ip;
-}
-
-static void es_vtep_free(struct in_addr *ip)
-{
- XFREE(MTYPE_BGP_EVPN_ES_VTEP, ip);
-}
-
-/* check if VTEP is already part of the list */
-static int is_vtep_present_in_list(struct list *list,
- struct in_addr vtep)
-{
- struct listnode *node = NULL;
- struct in_addr *tmp;
-
- for (ALL_LIST_ELEMENTS_RO(list, node, tmp)) {
- if (tmp->s_addr == vtep.s_addr)
- return 1;
- }
- return 0;
-}
-
-/*
- * Best path for ES route was changed,
- * update the list of VTEPs for this ES
- */
-static int evpn_es_install_vtep(struct bgp *bgp, struct evpnes *es,
- const struct prefix_evpn *p,
- struct in_addr rvtep)
-{
- struct in_addr *vtep_ip;
-
- if (is_vtep_present_in_list(es->vtep_list, rvtep))
- return 0;
-
-
- vtep_ip = es_vtep_new(rvtep);
- if (vtep_ip)
- listnode_add_sort(es->vtep_list, vtep_ip);
- return 0;
-}
-
-/*
- * Best path for ES route was changed,
- * update the list of VTEPs for this ES
- */
-static int evpn_es_uninstall_vtep(struct bgp *bgp,
- struct evpnes *es,
- struct prefix_evpn *p,
- struct in_addr rvtep)
-{
- struct listnode *node, *nnode, *node_to_del = NULL;
- struct in_addr *tmp;
-
- for (ALL_LIST_ELEMENTS(es->vtep_list, node, nnode, tmp)) {
- if (tmp->s_addr == rvtep.s_addr) {
- es_vtep_free(tmp);
- node_to_del = node;
- }
- }
-
- if (node_to_del)
- list_delete_node(es->vtep_list, node_to_del);
-
- return 0;
-}
-
-/*
- * Calculate the best path for a ES(type-4) route.
- */
-static int evpn_es_route_select_install(struct bgp *bgp, struct evpnes *es,
- struct bgp_dest *dest)
-{
- int ret = 0;
- afi_t afi = AFI_L2VPN;
- safi_t safi = SAFI_EVPN;
- struct bgp_path_info *old_select; /* old best */
- struct bgp_path_info *new_select; /* new best */
- struct bgp_path_info_pair old_and_new;
-
- /* Compute the best path. */
- bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new,
- afi, safi);
- old_select = old_and_new.old;
- new_select = old_and_new.new;
-
- /*
- * If the best path hasn't changed - see if something needs to be
- * updated
- */
- if (old_select && old_select == new_select
- && old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_IMPORTED
- && !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR)
- && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
- && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
- if (bgp_zebra_has_route_changed(old_select)) {
- ret = evpn_es_install_vtep(
- bgp, es,
- (const struct prefix_evpn *)bgp_dest_get_prefix(
- dest),
- old_select->attr->nexthop);
- }
- UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
- UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG);
- bgp_zebra_clear_route_change_flags(dest);
- return ret;
- }
-
- /* If the user did a "clear" this flag will be set */
- UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
-
- /*
- * bestpath has changed; update relevant fields and install or uninstall
- * into the zebra RIB.
- */
- if (old_select || new_select)
- bgp_bump_version(dest);
-
- if (old_select)
- bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
- if (new_select) {
- bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED);
- bgp_path_info_unset_flag(dest, new_select,
- BGP_PATH_ATTR_CHANGED);
- UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG);
- UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG);
- }
-
- if (new_select && new_select->type == ZEBRA_ROUTE_BGP
- && new_select->sub_type == BGP_ROUTE_IMPORTED) {
- ret = evpn_es_install_vtep(
- bgp, es,
- (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
- new_select->attr->nexthop);
- } else {
- if (old_select && old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_IMPORTED)
- ret = evpn_es_uninstall_vtep(
- bgp, es,
- (struct prefix_evpn *)bgp_dest_get_prefix(dest),
- old_select->attr->nexthop);
- }
-
- /* Clear any route change flags. */
- bgp_zebra_clear_route_change_flags(dest);
-
- /* Reap old select bgp_path_info, if it has been removed */
- if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED))
- bgp_path_info_reap(dest, old_select);
-
- return ret;
-}
-
/*
* Calculate the best path for an EVPN route. Install/update best path in zebra,
* if appropriate.
+ * Note: vpn is NULL for local EAD-ES routes.
*/
-static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
+int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
struct bgp_dest *dest)
{
struct bgp_path_info *old_select, *new_select;
@@ -1201,12 +1078,15 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
new_select = old_and_new.new;
/* If the best path hasn't changed - see if there is still something to
- * update
- * to zebra RIB.
+ * update to zebra RIB.
+ * Remote routes and SYNC route (i.e. local routes with
+ * SYNCED_FROM_PEER flag) need to updated to zebra on any attr
+ * change.
*/
if (old_select && old_select == new_select
&& old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_IMPORTED
+ && (old_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(old_select->attr))
&& !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
&& !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
@@ -1241,8 +1121,12 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG);
}
+ /* a local entry with the SYNC flag also results in a MAC-IP update
+ * to zebra
+ */
if (new_select && new_select->type == ZEBRA_ROUTE_BGP
- && new_select->sub_type == BGP_ROUTE_IMPORTED) {
+ && (new_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(new_select->attr))) {
ret = evpn_zebra_install(
bgp, vpn,
(struct prefix_evpn *)bgp_dest_get_prefix(dest),
@@ -1255,10 +1139,13 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
* need to do an implicit delete and withdraw that route from
* peers.
*/
- if (old_select && old_select->peer == bgp->peer_self
- && old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_STATIC)
- evpn_delete_old_local_route(bgp, vpn, dest, old_select);
+ if (new_select->sub_type == BGP_ROUTE_IMPORTED &&
+ old_select && old_select->peer == bgp->peer_self
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_STATIC
+ && vpn)
+ evpn_delete_old_local_route(bgp, vpn, dest,
+ old_select, new_select);
} else {
if (old_select && old_select->type == ZEBRA_ROUTE_BGP
&& old_select->sub_type == BGP_ROUTE_IMPORTED)
@@ -1279,222 +1166,21 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
return ret;
}
-/*
- * Return true if the local ri for this rn is of type gateway mac
- */
-static int evpn_route_is_def_gw(struct bgp *bgp, struct bgp_dest *dest)
-{
- struct bgp_path_info *tmp_pi = NULL;
- struct bgp_path_info *local_pi = NULL;
-
- local_pi = NULL;
- for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
- tmp_pi = tmp_pi->next) {
- if (tmp_pi->peer == bgp->peer_self
- && tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_STATIC)
- local_pi = tmp_pi;
- }
-
- if (!local_pi)
- return 0;
-
- return local_pi->attr->default_gw;
-}
-
-
-/*
- * Return true if the local ri for this rn has sticky set
- */
-static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_dest *dest)
+static struct bgp_path_info *bgp_evpn_route_get_local_path(
+ struct bgp *bgp, struct bgp_dest *dest)
{
struct bgp_path_info *tmp_pi;
- struct bgp_path_info *local_pi;
-
- local_pi = NULL;
- for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
- tmp_pi = tmp_pi->next) {
- if (tmp_pi->peer == bgp->peer_self
- && tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_STATIC)
- local_pi = tmp_pi;
- }
-
- if (!local_pi)
- return 0;
-
- return local_pi->attr->sticky;
-}
-
-/*
- * create or update EVPN type4 route entry.
- * This could be in the ES table or the global table.
- * TODO: handle remote ES (type4) routes as well
- */
-static int update_evpn_type4_route_entry(struct bgp *bgp, struct evpnes *es,
- afi_t afi, safi_t safi,
- struct bgp_dest *dest,
- struct attr *attr, int add,
- struct bgp_path_info **ri,
- int *route_changed)
-{
- char buf[ESI_STR_LEN];
- char buf1[INET6_ADDRSTRLEN];
- struct bgp_path_info *tmp_pi = NULL;
- struct bgp_path_info *local_pi = NULL; /* local route entry if any */
- struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */
- struct attr *attr_new = NULL;
- const struct prefix_evpn *evp = NULL;
-
- *ri = NULL;
- *route_changed = 1;
- evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ struct bgp_path_info *local_pi = NULL;
- /* locate the local and remote entries if any */
for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
- tmp_pi = tmp_pi->next) {
- if (tmp_pi->peer == bgp->peer_self
- && tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ tmp_pi = tmp_pi->next) {
+ if (bgp_evpn_is_path_local(bgp, tmp_pi)) {
local_pi = tmp_pi;
- if (tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_IMPORTED
- && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID))
- remote_pi = tmp_pi;
- }
-
- /* we don't expect to see a remote_ri at this point.
- * An ES route has esi + vtep_ip as the key,
- * We shouldn't see the same route from any other vtep.
- */
- if (remote_pi) {
- flog_err(
- EC_BGP_ES_INVALID,
- "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote",
- bgp->vrf_id,
- esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)),
- ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
- return -1;
- }
-
- if (!local_pi && !add)
- return 0;
-
- /* create or update the entry */
- if (!local_pi) {
-
- /* Add or update attribute to hash */
- attr_new = bgp_attr_intern(attr);
-
- /* Create new route with its attribute. */
- tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
- bgp->peer_self, attr_new, dest);
- SET_FLAG(tmp_pi->flags, BGP_PATH_VALID);
-
- /* add the newly created path to the route-node */
- bgp_path_info_add(dest, tmp_pi);
- } else {
- tmp_pi = local_pi;
- if (attrhash_cmp(tmp_pi->attr, attr)
- && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
- *route_changed = 0;
- else {
- /* The attribute has changed.
- * Add (or update) attribute to hash. */
- attr_new = bgp_attr_intern(attr);
- bgp_path_info_set_flag(dest, tmp_pi,
- BGP_PATH_ATTR_CHANGED);
-
- /* Restore route, if needed. */
- if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
- bgp_path_info_restore(dest, tmp_pi);
-
- /* Unintern existing, set to new. */
- bgp_attr_unintern(&tmp_pi->attr);
- tmp_pi->attr = attr_new;
- tmp_pi->uptime = bgp_clock();
+ break;
}
}
- /* Return back the route entry. */
- *ri = tmp_pi;
- return 0;
-}
-
-/* update evpn es (type-4) route */
-static int update_evpn_type4_route(struct bgp *bgp,
- struct evpnes *es,
- struct prefix_evpn *p)
-{
- int ret = 0;
- int route_changed = 0;
- char buf[ESI_STR_LEN];
- char buf1[INET6_ADDRSTRLEN];
- afi_t afi = AFI_L2VPN;
- safi_t safi = SAFI_EVPN;
- struct attr attr;
- struct attr *attr_new = NULL;
- struct bgp_dest *dest = NULL;
- struct bgp_path_info *pi = NULL;
-
- memset(&attr, 0, sizeof(struct attr));
-
- /* Build path-attribute for this route. */
- bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
- attr.nexthop = es->originator_ip.ipaddr_v4;
- attr.mp_nexthop_global_in = es->originator_ip.ipaddr_v4;
- attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
-
- /* Set up extended community. */
- build_evpn_type4_route_extcomm(es, &attr);
-
- /* First, create (or fetch) route node within the ESI. */
- /* NOTE: There is no RD here. */
- dest = bgp_node_get(es->route_table, (struct prefix *)p);
-
- /* Create or update route entry. */
- ret = update_evpn_type4_route_entry(bgp, es, afi, safi, dest, &attr, 1,
- &pi, &route_changed);
- if (ret != 0) {
- flog_err(EC_BGP_ES_INVALID,
- "%u ERROR: Failed to updated ES route ESI: %s VTEP %s",
- bgp->vrf_id,
- esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
- ipaddr2str(&es->originator_ip, buf1, sizeof(buf1)));
- }
-
- assert(pi);
- attr_new = pi->attr;
-
- /* Perform route selection;
- * this is just to set the flags correctly
- * as local route in the ES always wins.
- */
- evpn_es_route_select_install(bgp, es, dest);
- bgp_dest_unlock_node(dest);
-
- /* If this is a new route or some attribute has changed, export the
- * route to the global table. The route will be advertised to peers
- * from there. Note that this table is a 2-level tree (RD-level +
- * Prefix-level) similar to L3VPN routes.
- */
- if (route_changed) {
- struct bgp_path_info *global_pi;
-
- dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)p, &es->prd);
- update_evpn_type4_route_entry(bgp, es, afi, safi, dest,
- attr_new, 1, &global_pi,
- &route_changed);
-
- /* Schedule for processing and unlock node. */
- bgp_process(bgp, dest, afi, safi);
- bgp_dest_unlock_node(dest);
- }
-
- /* Unintern temporary. */
- aspath_unintern(&attr.aspath);
- return 0;
+ return local_pi;
}
static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
@@ -1640,8 +1326,9 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
build_evpn_type5_route_extcomm(bgp_vrf, &attr);
/* get the route node in global table */
- dest = bgp_afi_node_get(bgp_evpn->rib[afi][safi], afi, safi,
- (struct prefix *)evp, &bgp_vrf->vrf_prd);
+ dest = bgp_global_evpn_node_get(bgp_evpn->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)evp,
+ &bgp_vrf->vrf_prd);
assert(dest);
/* create or update the route entry within the route node */
@@ -1660,15 +1347,137 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
return 0;
}
+static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi,
+ struct bgp_node *rn, uint32_t loc_seq, uint32_t *max_sync_seq,
+ bool *active_on_peer, bool *peer_router,
+ bool *proxy_from_peer)
+{
+ struct bgp_path_info *tmp_pi;
+ struct bgp_path_info *second_best_path = NULL;
+ uint32_t tmp_mm_seq = 0;
+ esi_t *tmp_esi;
+ int paths_eq;
+
+ /* find the best non-local path. a local path can only be present
+ * as best path
+ */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(rn); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->sub_type != BGP_ROUTE_IMPORTED ||
+ !CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID))
+ continue;
+
+ if (bgp_evpn_path_info_cmp(bgp, tmp_pi,
+ second_best_path, &paths_eq))
+ second_best_path = tmp_pi;
+ }
+
+ if (!second_best_path)
+ return;
+
+ tmp_esi = bgp_evpn_attr_get_esi(second_best_path->attr);
+ /* if this has the same ES desination as the local path
+ * it is a sync path
+ */
+ if (!memcmp(esi, tmp_esi, sizeof(esi_t))) {
+ tmp_mm_seq = mac_mobility_seqnum(second_best_path->attr);
+ if (tmp_mm_seq < loc_seq)
+ return;
+
+ /* we have a non-proxy path from the ES peer. */
+ if (second_best_path->attr->es_flags &
+ ATTR_ES_PROXY_ADVERT) {
+ *proxy_from_peer = true;
+ } else {
+ *active_on_peer = true;
+ }
+
+ if (second_best_path->attr->router_flag)
+ *peer_router = true;
+
+ /* we use both proxy and non-proxy imports to
+ * determine the max sync sequence
+ */
+ if (tmp_mm_seq > *max_sync_seq)
+ *max_sync_seq = tmp_mm_seq;
+ }
+}
+
+/* Bubble up sync-info from all paths (non-best) to the local-path.
+ * This is need for MM sequence number syncing and proxy advertisement.
+ * Note: The local path can only exist as a best path in the
+ * VPN route table. It will take precedence over all sync paths.
+ */
+static void update_evpn_route_entry_sync_info(struct bgp *bgp,
+ struct bgp_node *rn, struct attr *attr, uint32_t loc_seq,
+ bool setup_sync)
+{
+ esi_t *esi;
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return;
+
+ esi = bgp_evpn_attr_get_esi(attr);
+ if (bgp_evpn_is_esi_valid(esi)) {
+ if (setup_sync) {
+ uint32_t max_sync_seq = 0;
+ bool active_on_peer = false;
+ bool peer_router = false;
+ bool proxy_from_peer = false;
+
+ bgp_evpn_get_sync_info(bgp, esi, rn, loc_seq,
+ &max_sync_seq, &active_on_peer,
+ &peer_router, &proxy_from_peer);
+ attr->mm_sync_seqnum = max_sync_seq;
+ if (active_on_peer)
+ attr->es_flags |= ATTR_ES_PEER_ACTIVE;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_ACTIVE;
+ if (proxy_from_peer)
+ attr->es_flags |= ATTR_ES_PEER_PROXY;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_PROXY;
+ if (peer_router)
+ attr->es_flags |= ATTR_ES_PEER_ROUTER;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_ROUTER;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char prefix_buf[PREFIX_STRLEN];
+ char esi_buf[ESI_STR_LEN];
+
+ zlog_debug("setup sync info for %s es %s max_seq %d %s%s%s",
+ prefix2str(evp, prefix_buf,
+ sizeof(prefix_buf)),
+ esi_to_str(esi, esi_buf,
+ sizeof(esi_buf)),
+ max_sync_seq,
+ (attr->es_flags & ATTR_ES_PEER_ACTIVE) ?
+ "peer-active " : "",
+ (attr->es_flags & ATTR_ES_PEER_PROXY) ?
+ "peer-proxy " : "",
+ (attr->es_flags & ATTR_ES_PEER_ROUTER) ?
+ "peer-router " : "");
+ }
+ }
+ } else {
+ attr->mm_sync_seqnum = 0;
+ attr->es_flags &= ~ATTR_ES_PEER_ACTIVE;
+ attr->es_flags &= ~ATTR_ES_PEER_PROXY;
+ }
+}
+
/*
* Create or update EVPN route entry. This could be in the VNI route table
* or the global route table.
*/
static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
- afi_t afi, safi_t safi,
- struct bgp_dest *dest, struct attr *attr,
- int add, struct bgp_path_info **pi,
- uint8_t flags, uint32_t seq)
+ afi_t afi, safi_t safi, struct bgp_dest *dest,
+ struct attr *attr, int add,
+ struct bgp_path_info **pi, uint8_t flags,
+ uint32_t seq, bool setup_sync,
+ bool *old_is_sync)
{
struct bgp_path_info *tmp_pi;
struct bgp_path_info *local_pi;
@@ -1684,14 +1493,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
memset(&label, 0, sizeof(label));
/* See if this is an update of an existing route, or a new add. */
- local_pi = NULL;
- for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
- tmp_pi = tmp_pi->next) {
- if (tmp_pi->peer == bgp->peer_self
- && tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_STATIC)
- local_pi = tmp_pi;
- }
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
/* If route doesn't exist already, create a new one, if told to.
* Otherwise act based on whether the attributes of the route have
@@ -1700,6 +1502,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
if (!local_pi && !add)
return 0;
+ if (old_is_sync && local_pi)
+ *old_is_sync = bgp_evpn_attr_is_sync(local_pi->attr);
+
+ /* if a local path is being added with a non-zero esi look
+ * for SYNC paths from ES peers and bubble up the sync-info
+ */
+ update_evpn_route_entry_sync_info(bgp, dest, attr, seq, setup_sync);
+
/* For non-GW MACs, update MAC mobility seq number, if needed. */
if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW))
add_mac_mobility_to_attr(seq, attr);
@@ -1811,11 +1621,11 @@ static void evpn_zebra_reinstall_best_route(struct bgp *bgp,
}
if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP
- && curr_select->sub_type == BGP_ROUTE_IMPORTED)
- evpn_zebra_install(
- bgp, vpn,
- (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
- curr_select);
+ && (curr_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(curr_select->attr)))
+ evpn_zebra_install(bgp, vpn,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
+ curr_select);
}
/*
@@ -1842,7 +1652,7 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp,
zlog_debug("evicting local evpn prefix %pRN as remote won",
dest);
- evpn_delete_old_local_route(bgp, vpn, dest, local_pi);
+ evpn_delete_old_local_route(bgp, vpn, dest, local_pi, NULL);
bgp_path_info_reap(dest, local_pi);
/* tell zebra to re-add the best remote path */
@@ -1855,7 +1665,7 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp,
*/
static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p, uint8_t flags,
- uint32_t seq)
+ uint32_t seq, esi_t *esi)
{
struct bgp_dest *dest;
struct attr attr;
@@ -1865,6 +1675,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
int route_change;
+ bool old_is_sync = false;
memset(&attr, 0, sizeof(struct attr));
@@ -1877,6 +1688,13 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
attr.router_flag = CHECK_FLAG(flags,
ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? 1 : 0;
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT))
+ attr.es_flags |= ATTR_ES_PROXY_ADVERT;
+
+ if (esi && bgp_evpn_is_esi_valid(esi)) {
+ memcpy(&attr.esi, esi, sizeof(esi_t));
+ attr.es_flags |= ATTR_ES_IS_LOCAL;
+ }
/* PMSI is only needed for type-3 routes */
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
@@ -1884,6 +1702,21 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
attr.pmsi_tnl_type = PMSI_TNLTYPE_INGR_REPL;
}
+ if (bgp_debug_zebra(NULL)) {
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[PREFIX_STRLEN];
+ char buf3[ESI_STR_LEN];
+
+ zlog_debug("VRF %s vni %u type-2 route evp %s RMAC %s nexthop %s esi %s",
+ vpn->bgp_vrf ?
+ vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ",
+ vpn->vni,
+ prefix2str(p, buf1, sizeof(buf1)),
+ prefix_mac2str(&attr.rmac, buf,
+ sizeof(buf)),
+ inet_ntoa(attr.mp_nexthop_global_in),
+ esi_to_str(esi, buf3, sizeof(buf3)));
+ }
/* router mac is only needed for type-2 routes here. */
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
uint8_t af_flags = 0;
@@ -1892,20 +1725,6 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
SET_FLAG(af_flags, BGP_EVPN_MACIP_TYPE_SVI_IP);
bgp_evpn_get_rmac_nexthop(vpn, p, &attr, af_flags);
-
- if (bgp_debug_zebra(NULL)) {
- char buf[ETHER_ADDR_STRLEN];
- char buf1[PREFIX_STRLEN];
-
- zlog_debug("VRF %s vni %u type-2 route evp %s RMAC %s nexthop %s",
- vpn->bgp_vrf ?
- vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ",
- vpn->vni,
- prefix2str(p, buf1, sizeof(buf1)),
- prefix_mac2str(&attr.rmac, buf,
- sizeof(buf)),
- inet_ntoa(attr.mp_nexthop_global_in));
- }
}
vni2label(vpn->vni, &(attr.label));
@@ -1930,7 +1749,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
/* Create or update route entry. */
route_change = update_evpn_route_entry(bgp, vpn, afi, safi, dest, &attr,
- 1, &pi, flags, seq);
+ 1, &pi, flags, seq,
+ true /* setup_sync */, &old_is_sync);
assert(pi);
attr_new = pi->attr;
@@ -1951,9 +1771,25 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* to re-add the best remote dest. BGP doesn't retain non-best local
* routes.
*/
- if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
route_change = 0;
- evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi);
+ } else {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ route_change = 0;
+ evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi);
+ } else {
+ bool new_is_sync;
+
+ /* If the local path already existed and is still the
+ * best path we need to also check if it transitioned
+ * from being a sync path to a non-sync path. If it
+ * it did we need to notify zebra that the sync-path
+ * has been removed.
+ */
+ new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
+ if (!new_is_sync && old_is_sync)
+ evpn_zebra_uninstall(bgp, vpn, p, zero_vtep_ip);
+ }
}
bgp_path_info_unlock(pi);
@@ -1967,10 +1803,12 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
if (route_change) {
struct bgp_path_info *global_pi;
- dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)p, &vpn->prd);
+ dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p,
+ &vpn->prd);
update_evpn_route_entry(bgp, vpn, afi, safi, dest, attr_new, 1,
- &global_pi, flags, seq);
+ &global_pi, flags, seq,
+ false /* setup_sync */, NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
bgp_process(bgp, dest, afi, safi);
@@ -1987,7 +1825,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* Delete EVPN route entry.
* The entry can be in ESI/VNI table or the global table.
*/
-static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
+void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
struct bgp_dest *dest,
struct bgp_path_info **pi)
{
@@ -2010,56 +1848,6 @@ static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
bgp_path_info_delete(dest, tmp_pi);
}
-
-
-/* Delete EVPN ES (type-4) route */
-static int delete_evpn_type4_route(struct bgp *bgp,
- struct evpnes *es,
- struct prefix_evpn *p)
-{
- afi_t afi = AFI_L2VPN;
- safi_t safi = SAFI_EVPN;
- struct bgp_path_info *pi;
- struct bgp_dest *dest = NULL; /* dest in esi table */
- struct bgp_dest *global_dest = NULL; /* dest in global table */
-
- /* First, locate the route node within the ESI.
- * If it doesn't exist, ther is nothing to do.
- * Note: there is no RD here.
- */
- dest = bgp_node_lookup(es->route_table, (struct prefix *)p);
- if (!dest)
- return 0;
-
- /* Next, locate route node in the global EVPN routing table.
- * Note that this table is a 2-level tree (RD-level + Prefix-level)
- */
- global_dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)p, &es->prd);
- if (global_dest) {
-
- /* Delete route entry in the global EVPN table. */
- delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
-
- /* Schedule for processing - withdraws to peers happen from
- * this table.
- */
- if (pi)
- bgp_process(bgp, global_dest, afi, safi);
- bgp_dest_unlock_node(global_dest);
- }
-
- /*
- * Delete route entry in the ESI route table.
- * This can just be removed.
- */
- delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
- if (pi)
- bgp_path_info_reap(dest, pi);
- bgp_dest_unlock_node(dest);
- return 0;
-}
-
/* Delete EVPN type5 route */
static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
{
@@ -2074,8 +1862,8 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
return 0;
/* locate the global route entry for this type-5 prefix */
- dest = bgp_afi_node_lookup(bgp_evpn->rib[afi][safi], afi, safi,
- (struct prefix *)evp, &bgp_vrf->vrf_prd);
+ dest = bgp_global_evpn_node_lookup(bgp_evpn->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)evp, &bgp_vrf->vrf_prd);
if (!dest)
return 0;
@@ -2111,8 +1899,8 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* this table is a 2-level tree (RD-level + Prefix-level) similar to
* L3VPN routes.
*/
- global_dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)p, &vpn->prd);
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p, &vpn->prd);
if (global_dest) {
/* Delete route entry in the global EVPN table. */
delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
@@ -2137,139 +1925,177 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
+static void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
+ struct bgpevpn *vpn, struct bgp_node *rn,
+ struct bgp_path_info *local_pi, const char *caller)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *pi;
+ struct attr attr;
+ struct attr *attr_new;
+ uint32_t seq;
+ int add_l3_ecomm = 0;
+ struct bgp_node *global_rn;
+ struct bgp_path_info *global_pi;
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+ int route_change;
+ bool old_is_sync = false;
+
+ if (CHECK_FLAG(local_pi->flags, BGP_PATH_REMOVED))
+ return;
+
+ /*
+ * Build attribute per local route as the MAC mobility and
+ * some other values could differ for different routes. The
+ * attributes will be shared in the hash table.
+ */
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = vpn->originator_ip;
+ attr.mp_nexthop_global_in = vpn->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.sticky = (local_pi->attr->sticky) ? 1 : 0;
+ attr.router_flag = (local_pi->attr->router_flag) ? 1 : 0;
+ attr.es_flags = local_pi->attr->es_flags;
+ if (local_pi->attr->default_gw) {
+ attr.default_gw = 1;
+ if (is_evpn_prefix_ipaddr_v6(evp))
+ attr.router_flag = 1;
+ }
+ memcpy(&attr.esi, &local_pi->attr->esi, sizeof(esi_t));
+ bgp_evpn_get_rmac_nexthop(vpn, evp, &attr,
+ local_pi->extra->af_flags);
+ vni2label(vpn->vni, &(attr.label));
+ /* Add L3 VNI RTs and RMAC for non IPv6 link-local if
+ * using L3 VNI for type-2 routes also.
+ */
+ if ((is_evpn_prefix_ipaddr_v4(evp) ||
+ !IN6_IS_ADDR_LINKLOCAL(
+ &evp->prefix.macip_addr.ip.ipaddr_v6)) &&
+ CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
+ bgpevpn_get_l3vni(vpn))
+ add_l3_ecomm = 1;
+
+ /* Set up extended community. */
+ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
+ seq = mac_mobility_seqnum(local_pi->attr);
+
+ if (bgp_debug_zebra(NULL)) {
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[PREFIX_STRLEN];
+ char buf3[ESI_STR_LEN];
+
+ zlog_debug("VRF %s vni %u evp %s RMAC %s nexthop %s esi %s esf 0x%x from %s",
+ vpn->bgp_vrf ?
+ vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ",
+ vpn->vni,
+ prefix2str(evp, buf1, sizeof(buf1)),
+ prefix_mac2str(&attr.rmac, buf, sizeof(buf)),
+ inet_ntoa(attr.mp_nexthop_global_in),
+ esi_to_str(&attr.esi, buf3, sizeof(buf3)),
+ attr.es_flags, caller);
+ }
+
+ /* Update the route entry. */
+ route_change = update_evpn_route_entry(bgp, vpn, afi, safi,
+ rn, &attr, 0, &pi, 0, seq,
+ true /* setup_sync */, &old_is_sync);
+
+ assert(pi);
+ attr_new = pi->attr;
+ /* lock ri to prevent freeing in evpn_route_select_install */
+ bgp_path_info_lock(pi);
+
+ /* Perform route selection. Normally, the local route in the
+ * VNI is expected to win and be the best route. However,
+ * under peculiar situations (e.g., tunnel (next hop) IP change
+ * that causes best selection to be based on next hop), a
+ * remote route could win. If the local route is the best,
+ * ensure it is updated in the global EVPN route table and
+ * advertised to peers; otherwise, ensure it is evicted and
+ * (re)install the remote route into zebra.
+ */
+ evpn_route_select_install(bgp, vpn, rn);
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ route_change = 0;
+ } else {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ route_change = 0;
+ evpn_cleanup_local_non_best_route(bgp, vpn, rn, pi);
+ } else {
+ bool new_is_sync;
+
+ /* If the local path already existed and is still the
+ * best path we need to also check if it transitioned
+ * from being a sync path to a non-sync path. If it
+ * it did we need to notify zebra that the sync-path
+ * has been removed.
+ */
+ new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
+ if (!new_is_sync && old_is_sync)
+ evpn_zebra_uninstall(bgp, vpn,
+ evp, zero_vtep_ip);
+ }
+ }
+
+
+ /* unlock pi */
+ bgp_path_info_unlock(pi);
+
+ if (route_change) {
+ /* Update route in global routing table. */
+ global_rn = bgp_global_evpn_node_get(bgp->rib[afi][safi],
+ afi, safi, evp, &vpn->prd);
+ assert(global_rn);
+ update_evpn_route_entry(bgp, vpn, afi, safi, global_rn,
+ attr_new, 0, &global_pi, 0,
+ mac_mobility_seqnum(attr_new),
+ false /* setup_sync */, NULL /* old_is_sync */);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, global_rn, afi, safi);
+ bgp_dest_unlock_node(global_rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+}
+
/*
* Update all type-2 (MACIP) local routes for this VNI - these should also
* be scheduled for advertise to peers.
*/
static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
{
- afi_t afi;
- safi_t safi;
struct bgp_dest *dest;
- struct bgp_path_info *pi, *tmp_pi;
- struct attr attr;
- struct attr *attr_new;
- uint32_t seq;
- int add_l3_ecomm = 0;
-
- afi = AFI_L2VPN;
- safi = SAFI_EVPN;
+ struct bgp_path_info *tmp_pi;
/* Walk this VNI's route table and update local type-2 routes. For any
* routes updated, update corresponding entry in the global table too.
*/
for (dest = bgp_table_top(vpn->route_table); dest;
- dest = bgp_route_next(dest)) {
+ dest = bgp_route_next(dest)) {
const struct prefix_evpn *evp =
(const struct prefix_evpn *)bgp_dest_get_prefix(dest);
- struct bgp_dest *rd_dest;
- struct bgp_path_info *global_pi;
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
/* Identify local route. */
for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
- tmp_pi = tmp_pi->next) {
+ tmp_pi = tmp_pi->next) {
if (tmp_pi->peer == bgp->peer_self
- && tmp_pi->type == ZEBRA_ROUTE_BGP
- && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
break;
}
if (!tmp_pi)
continue;
- /*
- * Build attribute per local route as the MAC mobility and
- * some other values could differ for different routes. The
- * attributes will be shared in the hash table.
- */
- bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
- attr.nexthop = vpn->originator_ip;
- attr.mp_nexthop_global_in = vpn->originator_ip;
- attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
- bgp_evpn_get_rmac_nexthop(vpn, evp, &attr,
- tmp_pi->extra->af_flags);
-
- if (evpn_route_is_sticky(bgp, dest))
- attr.sticky = 1;
- else if (evpn_route_is_def_gw(bgp, dest)) {
- attr.default_gw = 1;
- if (is_evpn_prefix_ipaddr_v6(evp))
- attr.router_flag = 1;
- }
-
- if (bgp_debug_zebra(NULL)) {
- char buf[ETHER_ADDR_STRLEN];
- char buf1[PREFIX_STRLEN];
-
- zlog_debug("VRF %s vni %u evp %s RMAC %s nexthop %s",
- vpn->bgp_vrf ?
- vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ",
- vpn->vni,
- prefix2str(evp, buf1, sizeof(buf1)),
- prefix_mac2str(&attr.rmac, buf, sizeof(buf)),
- inet_ntoa(attr.mp_nexthop_global_in));
- }
-
- /* Add L3 VNI RTs and RMAC for non IPv6 link-local if
- * using L3 VNI for type-2 routes also.
- */
- if ((is_evpn_prefix_ipaddr_v4(evp) ||
- !IN6_IS_ADDR_LINKLOCAL(
- &evp->prefix.macip_addr.ip.ipaddr_v6)) &&
- CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
- bgpevpn_get_l3vni(vpn))
- add_l3_ecomm = 1;
-
- /* Set up extended community. */
- build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
-
- seq = mac_mobility_seqnum(tmp_pi->attr);
-
- /* Update the route entry. */
- update_evpn_route_entry(bgp, vpn, afi, safi, dest, &attr, 0,
- &pi, 0, seq);
-
- /* lock ri to prevent freeing in evpn_route_select_install */
- bgp_path_info_lock(pi);
-
- /* Perform route selection. Normally, the local route in the
- * VNI is expected to win and be the best route. However,
- * under peculiar situations (e.g., tunnel (next hop) IP change
- * that causes best selection to be based on next hop), a
- * remote route could win. If the local route is the best,
- * ensure it is updated in the global EVPN route table and
- * advertised to peers; otherwise, ensure it is evicted and
- * (re)install the remote route into zebra.
- */
- evpn_route_select_install(bgp, vpn, dest);
- if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
- evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi);
- /* unlock pi */
- bgp_path_info_unlock(pi);
- } else {
- attr_new = pi->attr;
- /* unlock pi */
- bgp_path_info_unlock(pi);
-
- /* Update route in global routing table. */
- rd_dest = bgp_afi_node_get(bgp->rib[afi][safi], afi,
- safi, (struct prefix *)evp,
- &vpn->prd);
- assert(rd_dest);
- update_evpn_route_entry(bgp, vpn, afi, safi, rd_dest,
- attr_new, 0, &global_pi, 0,
- mac_mobility_seqnum(attr_new));
-
- /* Schedule for processing and unlock node. */
- bgp_process(bgp, rd_dest, afi, safi);
- bgp_dest_unlock_node(rd_dest);
- }
-
- /* Unintern temporary. */
- aspath_unintern(&attr.aspath);
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, tmp_pi,
+ __func__);
}
return 0;
@@ -2356,27 +2182,6 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
- * Delete all routes in per ES route-table
- */
-static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es)
-{
- struct bgp_dest *dest;
- struct bgp_path_info *pi, *nextpi;
-
- /* Walk this ES's route table and delete all routes. */
- for (dest = bgp_table_top(es->route_table); dest;
- dest = bgp_route_next(dest)) {
- for (pi = bgp_dest_get_bgp_path_info(dest);
- (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
- bgp_path_info_delete(dest, pi);
- bgp_path_info_reap(dest, pi);
- }
- }
-
- return 0;
-}
-
-/*
* Delete all routes in the per-VNI route table.
*/
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
@@ -2434,7 +2239,7 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
if (bgp_evpn_vni_flood_mode_get(bgp, vpn)
== VXLAN_FLOOD_HEAD_END_REPL) {
build_evpn_type3_prefix(&p, vpn->originator_ip);
- ret = update_evpn_route(bgp, vpn, &p, 0, 0);
+ ret = update_evpn_route(bgp, vpn, &p, 0, 0, NULL);
if (ret)
return ret;
}
@@ -2442,29 +2247,6 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
return update_all_type2_routes(bgp, vpn);
}
-/* Delete (and withdraw) local routes for specified ES from global and ES table.
- * Also remove all other routes from the per ES table.
- * Invoked when ES is deleted.
- */
-static int delete_routes_for_es(struct bgp *bgp, struct evpnes *es)
-{
- int ret;
- char buf[ESI_STR_LEN];
- struct prefix_evpn p;
-
- /* Delete and withdraw locally learnt ES route */
- build_evpn_type4_prefix(&p, &es->esi, es->originator_ip.ipaddr_v4);
- ret = delete_evpn_type4_route(bgp, es, &p);
- if (ret) {
- flog_err(EC_BGP_EVPN_ROUTE_DELETE,
- "%u failed to delete type-4 route for ESI %s",
- bgp->vrf_id, esi_to_str(&es->esi, buf, sizeof(buf)));
- }
-
- /* Delete all routes from per ES table */
- return delete_all_es_routes(bgp, es);
-}
-
/*
* Delete (and withdraw) local routes for specified VNI from the global
* table and per-VNI table. After this, remove all other routes from
@@ -2574,68 +2356,6 @@ bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi,
return pi;
}
-/* Install EVPN route entry in ES */
-static int install_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es,
- const struct prefix_evpn *p,
- struct bgp_path_info *parent_pi)
-{
- int ret = 0;
- struct bgp_dest *dest = NULL;
- struct bgp_path_info *pi = NULL;
- struct attr *attr_new = NULL;
-
- /* Create (or fetch) route within the VNI.
- * NOTE: There is no RD here.
- */
- dest = bgp_node_get(es->route_table, (struct prefix *)p);
-
- /* Check if route entry is already present. */
- for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
- if (pi->extra
- && (struct bgp_path_info *)pi->extra->parent == parent_pi)
- break;
-
- if (!pi) {
- /* Add (or update) attribute to hash. */
- attr_new = bgp_attr_intern(parent_pi->attr);
-
- /* Create new route with its attribute. */
- pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0,
- parent_pi->peer, attr_new, dest);
- SET_FLAG(pi->flags, BGP_PATH_VALID);
- bgp_path_info_extra_get(pi);
- pi->extra->parent = bgp_path_info_lock(parent_pi);
- bgp_dest_lock_node((struct bgp_dest *)parent_pi->net);
- bgp_path_info_add(dest, pi);
- } else {
- if (attrhash_cmp(pi->attr, parent_pi->attr)
- && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
- bgp_dest_unlock_node(dest);
- return 0;
- }
- /* The attribute has changed. */
- /* Add (or update) attribute to hash. */
- attr_new = bgp_attr_intern(parent_pi->attr);
-
- /* Restore route, if needed. */
- if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
- bgp_path_info_restore(dest, pi);
-
- /* Mark if nexthop has changed. */
- if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
- SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
-
- /* Unintern existing, set to new. */
- bgp_attr_unintern(&pi->attr);
- pi->attr = attr_new;
- pi->uptime = bgp_clock();
- }
-
- /* Perform route selection and update zebra, if required. */
- ret = evpn_es_route_select_install(bgp, es, dest);
- return ret;
-}
-
/*
* Install route entry into the VRF routing table and invoke route selection.
*/
@@ -2761,8 +2481,17 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
{
struct bgp_dest *dest;
struct bgp_path_info *pi;
+ struct bgp_path_info *local_pi;
struct attr *attr_new;
int ret;
+ struct prefix_evpn ad_evp;
+
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ p = evpn_type1_prefix_vni_copy(&ad_evp, p,
+ parent_pi->attr->nexthop);
/* Create (or fetch) route within the VNI. */
/* NOTE: There is no RD here. */
@@ -2805,46 +2534,16 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
- bgp_dest_unlock_node(dest);
-
- return ret;
-}
-
-/* Uninstall EVPN route entry from ES route table */
-static int uninstall_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es,
- const struct prefix_evpn *p,
- struct bgp_path_info *parent_pi)
-{
- int ret;
- struct bgp_dest *dest;
- struct bgp_path_info *pi;
-
- if (!es->route_table)
- return 0;
-
- /* Locate route within the ESI.
- * NOTE: There is no RD here.
+ /* if the best path is a local path with a non-zero ES
+ * sync info against the local path may need to be updated
+ * when a remote path is added/updated (including changes
+ * from sync-path to remote-path)
*/
- dest = bgp_node_lookup(es->route_table, (struct prefix *)p);
- if (!dest)
- return 0;
-
- /* Find matching route entry. */
- for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
- if (pi->extra
- && (struct bgp_path_info *)pi->extra->parent == parent_pi)
- break;
-
- if (!pi)
- return 0;
-
- /* Mark entry for deletion */
- bgp_path_info_delete(dest, pi);
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
+ if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr))
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
+ __func__);
- /* Perform route selection and update zebra, if required. */
- ret = evpn_es_route_select_install(bgp, es, dest);
-
- /* Unlock route node. */
bgp_dest_unlock_node(dest);
return ret;
@@ -2936,7 +2635,16 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
{
struct bgp_dest *dest;
struct bgp_path_info *pi;
+ struct bgp_path_info *local_pi;
int ret;
+ struct prefix_evpn ad_evp;
+
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ p = evpn_type1_prefix_vni_copy(&ad_evp, p,
+ parent_pi->attr->nexthop);
/* Locate route within the VNI. */
/* NOTE: There is no RD here. */
@@ -2959,6 +2667,15 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
+ /* if the best path is a local path with a non-zero ES
+ * sync info against the local path may need to be updated
+ * when a remote path is deleted
+ */
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
+ if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr))
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
+ __func__);
+
/* Unlock route node. */
bgp_dest_unlock_node(dest);
@@ -2966,22 +2683,6 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
/*
- * Given a prefix, see if it belongs to ES.
- */
-static int is_prefix_matching_for_es(const struct prefix_evpn *p,
- struct evpnes *es)
-{
- /* if not an ES route return false */
- if (p->prefix.route_type != BGP_EVPN_ES_ROUTE)
- return 0;
-
- if (memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)) == 0)
- return 1;
-
- return 0;
-}
-
-/*
* Given a route entry and a VRF, see if this route entry should be
* imported into the VRF i.e., RTs match.
*/
@@ -3115,78 +2816,6 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
-static int install_uninstall_routes_for_es(struct bgp *bgp,
- struct evpnes *es,
- int install)
-{
- int ret;
- afi_t afi;
- safi_t safi;
- char buf[PREFIX_STRLEN];
- char buf1[ESI_STR_LEN];
- struct bgp_dest *rd_dest, *dest;
- struct bgp_table *table;
- struct bgp_path_info *pi;
-
- afi = AFI_L2VPN;
- safi = SAFI_EVPN;
-
- /*
- * Walk entire global routing table and evaluate routes which could be
- * imported into this VRF. Note that we need to loop through all global
- * routes to determine which route matches the import rt on vrf
- */
- for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
- rd_dest = bgp_route_next(rd_dest)) {
- table = bgp_dest_get_bgp_table_info(rd_dest);
- if (!table)
- continue;
-
- for (dest = bgp_table_top(table); dest;
- dest = bgp_route_next(dest)) {
- const struct prefix_evpn *evp =
- (const struct prefix_evpn *)bgp_dest_get_prefix(
- dest);
-
- for (pi = bgp_dest_get_bgp_path_info(dest); pi;
- pi = pi->next) {
- /*
- * Consider "valid" remote routes applicable for
- * this ES.
- */
- if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
- && pi->type == ZEBRA_ROUTE_BGP
- && pi->sub_type == BGP_ROUTE_NORMAL))
- continue;
-
- if (!is_prefix_matching_for_es(evp, es))
- continue;
-
- if (install)
- ret = install_evpn_route_entry_in_es(
- bgp, es, evp, pi);
- else
- ret = uninstall_evpn_route_entry_in_es(
- bgp, es, evp, pi);
-
- if (ret) {
- flog_err(
- EC_BGP_EVPN_FAIL,
- "Failed to %s EVPN %s route in ESI %s",
- install ? "install"
- : "uninstall",
- prefix2str(evp, buf,
- sizeof(buf)),
- esi_to_str(&es->esi, buf1,
- sizeof(buf1)));
- return ret;
- }
- }
- }
- }
- return 0;
-}
-
/* This API will scan evpn routes for checking attribute's rmac
* macthes with bgp instance router mac. It avoid installing
* route into bgp vrf table and remote rmac in bridge table.
@@ -3207,7 +2836,7 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf,
char buf1[PREFIX_STRLEN];
char attr_str[BUFSIZ] = {0};
- bgp_dump_attr(pi->attr, attr_str, BUFSIZ);
+ bgp_dump_attr(pi->attr, attr_str, sizeof(attr_str));
zlog_debug("%s: bgp %u prefix %s with attr %s - DENIED due to self mac",
__func__, bgp_vrf->vrf_id,
@@ -3390,15 +3019,6 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
return 0;
}
-/* Install any existing remote ES routes applicable for this ES into its routing
- * table. This is invoked when ES comes up.
- */
-static int install_routes_for_es(struct bgp *bgp, struct evpnes *es)
-{
- return install_uninstall_routes_for_es(bgp, es, 1);
-}
-
-
/* Install any existing remote routes applicable for this VRF into VRF RIB. This
* is invoked upon l3vni-add or l3vni import rt change
*/
@@ -3425,6 +3045,11 @@ static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
if (ret)
return ret;
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
+ 1);
+ if (ret)
+ return ret;
+
return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE,
1);
}
@@ -3453,33 +3078,14 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
if (ret)
return ret;
- return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
- 0);
-}
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
+ 1);
+ if (ret)
+ return ret;
-/* Install or unistall route in ES */
-static int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es,
- afi_t afi, safi_t safi,
- struct prefix_evpn *evp,
- struct bgp_path_info *pi, int install)
-{
- int ret = 0;
- char buf[ESI_STR_LEN];
- if (install)
- ret = install_evpn_route_entry_in_es(bgp, es, evp, pi);
- else
- ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, pi);
-
- if (ret) {
- flog_err(
- EC_BGP_EVPN_FAIL,
- "%u: Failed to %s EVPN %s route in ESI %s", bgp->vrf_id,
- install ? "install" : "uninstall", "ES",
- esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)));
- return ret;
- }
- return 0;
+ return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
+ 0);
}
/*
@@ -3576,6 +3182,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct attr *attr = pi->attr;
struct ecommunity *ecom;
int i;
+ struct prefix_evpn ad_evp;
assert(attr);
@@ -3583,6 +3190,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
|| evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
|| evp->prefix.route_type == BGP_EVPN_ES_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_AD_ROUTE
|| evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
return 0;
@@ -3590,6 +3198,12 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
return 0;
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ evp = evpn_type1_prefix_vni_copy(&ad_evp, evp, attr->nexthop);
+
ecom = attr->ecommunity;
if (!ecom || !ecom->size)
return -1;
@@ -3603,7 +3217,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
struct ecommunity_val eval_tmp;
struct irt_node *irt; /* import rt for l2vni */
struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
- struct evpnes *es;
+ struct bgp_evpn_es *es;
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
@@ -3621,6 +3235,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
*/
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
evp->prefix.route_type == BGP_EVPN_IMET_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_AD_ROUTE ||
evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
irt = lookup_import_rt(bgp, eval);
@@ -3668,9 +3283,9 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
/* we will match based on the entire esi to avoid
* imoort of an es route for esi2 into esi1
*/
- es = bgp_evpn_lookup_es(bgp, &evp->prefix.es_addr.esi);
- if (es && is_es_local(es))
- install_uninstall_route_in_es(
+ es = bgp_evpn_es_find(&evp->prefix.es_addr.esi);
+ if (es && bgp_evpn_is_es_local(es))
+ bgp_evpn_es_route_install_uninstall(
bgp, es, afi, safi, evp, pi, import);
}
}
@@ -3804,10 +3419,11 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
return 0;
attr = pi->attr;
- global_dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)&p, &vpn->prd);
+ global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi],
+ afi, safi, &p, &vpn->prd);
update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr,
- 1, &pi, 0, mac_mobility_seqnum(attr));
+ 1, &pi, 0, mac_mobility_seqnum(attr),
+ false /* setup_sync */, NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_dest, afi, safi);
@@ -3838,12 +3454,13 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
* attribute.
*/
attr = pi->attr;
- global_dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)evp, &vpn->prd);
+ global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ evp, &vpn->prd);
assert(global_dest);
- update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr,
- 1, &global_pi, 0,
- mac_mobility_seqnum(attr));
+ update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr, 1,
+ &global_pi, 0,
+ mac_mobility_seqnum(attr),
+ false /* setup_sync */, NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_dest, afi, safi);
@@ -3875,8 +3492,8 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
/* Remove type-3 route for this VNI from global table. */
build_evpn_type3_prefix(&p, vpn->originator_ip);
- global_dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)&p, &vpn->prd);
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)&p, &vpn->prd);
if (global_dest) {
/* Delete route entry in the global EVPN table. */
delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
@@ -3943,7 +3560,7 @@ static void create_advertise_type3(struct hash_bucket *bucket, void *data)
return;
build_evpn_type3_prefix(&p, vpn->originator_ip);
- if (update_evpn_route(bgp, vpn, &p, 0, 0))
+ if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL))
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
"Type3 route creation failure for VNI %u", vpn->vni);
}
@@ -4011,8 +3628,14 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
/* Copy Ethernet Seg Identifier */
- memcpy(&evpn.eth_s_id.val, pfx, ESI_LEN);
- pfx += ESI_LEN;
+ if (attr) {
+ memcpy(&attr->esi, pfx, sizeof(esi_t));
+ if (bgp_evpn_is_esi_local(&attr->esi))
+ attr->es_flags |= ATTR_ES_IS_LOCAL;
+ else
+ attr->es_flags &= ~ATTR_ES_IS_LOCAL;
+ }
+ pfx += sizeof(esi_t);
/* Copy Ethernet Tag */
memcpy(&eth_tag, pfx, 4);
@@ -4165,68 +3788,6 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
}
/*
- * Process received EVPN type-4 route (advertise or withdraw).
- */
-static int process_type4_route(struct peer *peer, afi_t afi, safi_t safi,
- struct attr *attr, uint8_t *pfx, int psize,
- uint32_t addpath_id)
-{
- int ret;
- esi_t esi;
- uint8_t ipaddr_len;
- struct in_addr vtep_ip;
- struct prefix_rd prd;
- struct prefix_evpn p;
-
- /* Type-4 route should be either 23 or 35 bytes
- * RD (8), ESI (10), ip-len (1), ip (4 or 16)
- */
- if (psize != 23 && psize != 35) {
- flog_err(EC_BGP_EVPN_ROUTE_INVALID,
- "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d",
- peer->bgp->vrf_id, peer->host, psize);
- return -1;
- }
-
- /* Make prefix_rd */
- prd.family = AF_UNSPEC;
- prd.prefixlen = 64;
- memcpy(&prd.val, pfx, 8);
- pfx += 8;
-
- /* get the ESI */
- memcpy(&esi, pfx, ESI_BYTES);
- pfx += ESI_BYTES;
-
-
- /* Get the IP. */
- ipaddr_len = *pfx++;
- if (ipaddr_len == IPV4_MAX_BITLEN) {
- memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN);
- } else {
- flog_err(
- EC_BGP_EVPN_ROUTE_INVALID,
- "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d",
- peer->bgp->vrf_id, peer->host, ipaddr_len);
- return -1;
- }
-
- build_evpn_type4_prefix(&p, &esi, vtep_ip);
- /* Process the route. */
- if (attr) {
- ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
- afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, NULL, 0, 0, NULL);
- } else {
- ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
- afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, NULL, 0, NULL);
- }
- return ret;
-}
-
-
-/*
* Process received EVPN type-5 route (advertise or withdraw).
*/
static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
@@ -4271,8 +3832,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
memset(&evpn, 0, sizeof(evpn));
/* Fetch ESI */
- memcpy(&evpn.eth_s_id.val, pfx, 10);
- pfx += 10;
+ if (attr)
+ memcpy(&attr->esi, pfx, sizeof(esi_t));
+ pfx += ESI_BYTES;
/* Fetch Ethernet Tag. */
memcpy(&eth_tag, pfx, 4);
@@ -4322,7 +3884,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
if (attr) {
is_valid_update = true;
- if (is_zero_mac(&attr->rmac) && is_zero_esi(&evpn.eth_s_id) &&
+ if (is_zero_mac(&attr->rmac) &&
is_zero_gw_ip(&evpn.gw_ip, gw_afi))
is_valid_update = false;
@@ -4368,9 +3930,9 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p,
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
stream_put(s, prd->val, 8);
if (attr)
- stream_put(s, &(attr->evpn_overlay.eth_s_id), 10);
+ stream_put(s, &attr->esi, sizeof(esi_t));
else
- stream_put(s, &temp, 10);
+ stream_put(s, 0, sizeof(esi_t));
stream_putl(s, p_evpn_p->prefix_addr.eth_tag);
stream_putc(s, p_evpn_p->prefix_addr.ip_prefix_length);
if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
@@ -5073,6 +4635,15 @@ char *bgp_evpn_route2str(const struct prefix_evpn *p, char *buf, int len)
is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN,
inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4));
+ } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ snprintf(buf, len, "[%d]:[%u]:[%s]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.ead_addr.eth_tag,
+ esi_to_str(&p->prefix.ead_addr.esi,
+ buf3, sizeof(buf3)),
+ is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN
+ : IPV6_MAX_BITLEN,
+ inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",
@@ -5112,7 +4683,7 @@ void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
stream_putc(s, len);
stream_put(s, prd->val, 8); /* RD */
if (attr)
- stream_put(s, &attr->evpn_overlay.eth_s_id, ESI_LEN);
+ stream_put(s, &attr->esi, ESI_BYTES);
else
stream_put(s, 0, 10);
stream_putl(s, evp->prefix.macip_addr.eth_tag); /* Ethernet Tag ID */
@@ -5147,6 +4718,16 @@ void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4);
break;
+ case BGP_EVPN_AD_ROUTE:
+ /* RD, ESI, EthTag, 1 VNI */
+ len = RD_BYTES + ESI_BYTES + EVPN_ETH_TAG_BYTES + BGP_LABEL_BYTES;
+ stream_putc(s, len);
+ stream_put(s, prd->val, RD_BYTES); /* RD */
+ stream_put(s, evp->prefix.ead_addr.esi.val, ESI_BYTES); /* ESI */
+ stream_putl(s, evp->prefix.ead_addr.eth_tag); /* Ethernet Tag */
+ stream_put(s, label, BGP_LABEL_BYTES);
+ break;
+
case BGP_EVPN_IP_PREFIX_ROUTE:
/* TODO: AddPath support. */
evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr);
@@ -5234,7 +4815,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
break;
case BGP_EVPN_ES_ROUTE:
- if (process_type4_route(peer, afi, safi,
+ if (bgp_evpn_type4_route_process(peer, afi, safi,
withdraw ? NULL : attr, pnt,
psize, addpath_id)) {
flog_err(
@@ -5245,6 +4826,18 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
}
break;
+ case BGP_EVPN_AD_ROUTE:
+ if (bgp_evpn_type1_route_process(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "%u:%s - Error in processing EVPN type-1 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE;
+ }
+ break;
+
case BGP_EVPN_IP_PREFIX_ROUTE:
if (process_type5_route(peer, afi, safi,
withdraw ? NULL : attr, pnt,
@@ -5423,7 +5016,7 @@ void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
*/
void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn)
{
- char buf[100];
+ char buf[BGP_EVPN_PREFIX_RD_LEN];
vpn->prd.family = AF_UNSPEC;
vpn->prd.prefixlen = 64;
@@ -5507,6 +5100,8 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
/* add to l2vni list on corresponding vrf */
bgpevpn_link_to_l3vni(vpn);
+ bgp_evpn_vni_es_init(vpn);
+
QOBJ_REG(vpn, bgpevpn);
return vpn;
}
@@ -5519,6 +5114,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ bgp_evpn_vni_es_cleanup(vpn);
bgpevpn_unlink_from_l3vni(vpn);
bgp_table_unlock(vpn->route_table);
bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
@@ -5531,79 +5127,6 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
}
/*
- * Lookup local ES.
- */
-struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi)
-{
- struct evpnes *es;
- struct evpnes tmp;
-
- memset(&tmp, 0, sizeof(struct evpnes));
- memcpy(&tmp.esi, esi, sizeof(esi_t));
- es = hash_lookup(bgp->esihash, &tmp);
- return es;
-}
-
-/*
- * Create a new local es - invoked upon zebra notification.
- */
-struct evpnes *bgp_evpn_es_new(struct bgp *bgp,
- esi_t *esi,
- struct ipaddr *originator_ip)
-{
- char buf[100];
- struct evpnes *es;
-
- if (!bgp)
- return NULL;
-
- es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct evpnes));
-
- /* set the ESI and originator_ip */
- memcpy(&es->esi, esi, sizeof(esi_t));
- memcpy(&es->originator_ip, originator_ip, sizeof(struct ipaddr));
-
- /* Initialise the VTEP list */
- es->vtep_list = list_new();
- es->vtep_list->cmp = evpn_vtep_ip_cmp;
-
- /* auto derive RD for this es */
- bf_assign_index(bm->rd_idspace, es->rd_id);
- es->prd.family = AF_UNSPEC;
- es->prd.prefixlen = 64;
- snprintf(buf, sizeof(buf), "%s:%hu", inet_ntoa(bgp->router_id),
- es->rd_id);
- (void)str2prefix_rd(buf, &es->prd);
-
- /* Initialize the ES route table */
- es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
-
- /* Add to hash */
- if (!hash_get(bgp->esihash, es, hash_alloc_intern)) {
- XFREE(MTYPE_BGP_EVPN_ES, es);
- return NULL;
- }
-
- QOBJ_REG(es, evpnes);
- return es;
-}
-
-/*
- * Free a given ES -
- * This just frees appropriate memory, caller should have taken other
- * needed actions.
- */
-void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es)
-{
- list_delete(&es->vtep_list);
- bgp_table_unlock(es->route_table);
- bf_release_index(bm->rd_idspace, es->rd_id);
- hash_release(bgp->esihash, es);
- QOBJ_UNREG(es);
- XFREE(MTYPE_BGP_EVPN_ES, es);
-}
-
-/*
* Import evpn route from global table to VNI/VRF/ESI.
*/
int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
@@ -5667,7 +5190,8 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp)
char attr_str[BUFSIZ] = {0};
bgp_dump_attr(pi->attr,
- attr_str, BUFSIZ);
+ attr_str,
+ sizeof(attr_str));
zlog_debug(
"%u: prefix %pRN with attr %s - DENIED due to martian or self nexthop",
@@ -5724,7 +5248,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
* Handle add of a local MACIP.
*/
int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
- struct ipaddr *ip, uint8_t flags, uint32_t seq)
+ struct ipaddr *ip, uint8_t flags, uint32_t seq, esi_t *esi)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
@@ -5740,7 +5264,7 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
/* Create EVPN type-2 route and schedule for processing. */
build_evpn_type2_prefix(&p, mac, ip);
- if (update_evpn_route(bgp, vpn, &p, flags, seq)) {
+ if (update_evpn_route(bgp, vpn, &p, flags, seq, esi)) {
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
@@ -6112,7 +5636,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
if (bgp_evpn_vni_flood_mode_get(bgp, vpn)
== VXLAN_FLOOD_HEAD_END_REPL) {
build_evpn_type3_prefix(&p, vpn->originator_ip);
- if (update_evpn_route(bgp, vpn, &p, 0, 0)) {
+ if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL)) {
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
"%u: Type3 route creation failure for VNI %u",
bgp->vrf_id, vni);
@@ -6137,88 +5661,6 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
}
/*
- * bgp_evpn_local_es_del
- */
-int bgp_evpn_local_es_del(struct bgp *bgp,
- esi_t *esi,
- struct ipaddr *originator_ip)
-{
- char buf[ESI_STR_LEN];
- struct evpnes *es = NULL;
-
- if (!bgp->esihash) {
- flog_err(EC_BGP_ES_CREATE, "%u: ESI hash not yet created",
- bgp->vrf_id);
- return -1;
- }
-
- /* Lookup ESI hash - should exist. */
- es = bgp_evpn_lookup_es(bgp, esi);
- if (!es) {
- flog_warn(EC_BGP_EVPN_ESI,
- "%u: ESI hash entry for ESI %s at Local ES DEL",
- bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
- return -1;
- }
-
- /* Delete all local EVPN ES routes from ESI table
- * and schedule for processing (to withdraw from peers))
- */
- delete_routes_for_es(bgp, es);
-
- /* free the hash entry */
- bgp_evpn_es_free(bgp, es);
-
- return 0;
-}
-
-/*
- * bgp_evpn_local_es_add
- */
-int bgp_evpn_local_es_add(struct bgp *bgp,
- esi_t *esi,
- struct ipaddr *originator_ip)
-{
- char buf[ESI_STR_LEN];
- struct evpnes *es = NULL;
- struct prefix_evpn p;
-
- if (!bgp->esihash) {
- flog_err(EC_BGP_ES_CREATE, "%u: ESI hash not yet created",
- bgp->vrf_id);
- return -1;
- }
-
- /* create the new es */
- es = bgp_evpn_lookup_es(bgp, esi);
- if (!es) {
- es = bgp_evpn_es_new(bgp, esi, originator_ip);
- if (!es) {
- flog_err(
- EC_BGP_ES_CREATE,
- "%u: Failed to allocate ES entry for ESI %s - at Local ES Add",
- bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
- return -1;
- }
- }
- UNSET_FLAG(es->flags, EVPNES_REMOTE);
- SET_FLAG(es->flags, EVPNES_LOCAL);
-
- build_evpn_type4_prefix(&p, esi, originator_ip->ipaddr_v4);
- if (update_evpn_type4_route(bgp, es, &p)) {
- flog_err(EC_BGP_EVPN_ROUTE_CREATE,
- "%u: Type4 route creation failure for ESI %s",
- bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
- return -1;
- }
-
- /* import all remote ES routes in th ES table */
- install_routes_for_es(bgp, es);
-
- return 0;
-}
-
-/*
* Handle change in setting for BUM handling. The supported values
* are head-end replication and dropping all BUM packets. Any change
* should be registered with zebra. Also, if doing head-end replication,
@@ -6267,9 +5709,6 @@ void bgp_evpn_cleanup(struct bgp *bgp)
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
- if (bgp->esihash)
- hash_free(bgp->esihash);
- bgp->esihash = NULL;
list_delete(&bgp->vrf_import_rtl);
list_delete(&bgp->vrf_export_rtl);
@@ -6286,9 +5725,6 @@ void bgp_evpn_init(struct bgp *bgp)
{
bgp->vnihash =
hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
- bgp->esihash =
- hash_create(esi_hash_keymake, esi_cmp,
- "BGP EVPN Local ESI Hash");
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index 267c87ee54..8535f1fa31 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -176,7 +176,7 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni,
int state);
extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
struct ethaddr *mac, struct ipaddr *ip,
- uint8_t flags, uint32_t seq);
+ uint8_t flags, uint32_t seq, esi_t *esi);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
struct ethaddr *vrr_rmac,
@@ -188,10 +188,6 @@ extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
struct in_addr mcast_grp);
-extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
- struct ipaddr *originator_ip);
-extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi,
- struct ipaddr *originator_ip);
extern void bgp_evpn_flood_control_change(struct bgp *bgp);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
new file mode 100644
index 0000000000..eb65c43bb9
--- /dev/null
+++ b/bgpd/bgp_evpn_mh.c
@@ -0,0 +1,2905 @@
+/* EVPN Multihoming procedures
+ *
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRR.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "filter.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+#include "zclient.h"
+
+#include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_label.h"
+
+static void bgp_evpn_local_es_down(struct bgp *bgp,
+ struct bgp_evpn_es *es);
+static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp,
+ struct bgp_evpn_es *es);
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr);
+static void bgp_evpn_es_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr);
+static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es);
+static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es);
+static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi);
+
+esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
+
+/******************************************************************************
+ * per-ES (Ethernet Segment) routing table
+ *
+ * Following routes are added to the ES's routing table -
+ * 1. Local and remote ESR (Type-4)
+ * 2. Local EAD-per-ES (Type-1).
+ *
+ * Key for these routes is {ESI, VTEP-IP} so the path selection is practically
+ * a no-op i.e. all paths lead to same VTEP-IP (i.e. result in the same VTEP
+ * being added to same ES).
+ *
+ * Note the following routes go into the VNI routing table (instead of the
+ * ES routing table) -
+ * 1. Remote EAD-per-ES
+ * 2. Local and remote EAD-per-EVI
+ */
+
+/* Calculate the best path for a multi-homing (Type-1 or Type-4) route
+ * installed in the ES's routing table.
+ */
+static int bgp_evpn_es_route_select_install(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct bgp_node *rn)
+{
+ int ret = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *old_select; /* old best */
+ struct bgp_path_info *new_select; /* new best */
+ struct bgp_path_info_pair old_and_new;
+
+ /* Compute the best path. */
+ bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
+ old_select = old_and_new.old;
+ new_select = old_and_new.new;
+
+ /*
+ * If the best path hasn't changed - see if something needs to be
+ * updated
+ */
+ if (old_select && old_select == new_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED
+ && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
+ && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
+ && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
+ if (bgp_zebra_has_route_changed(old_select)) {
+ bgp_evpn_es_vtep_add(bgp, es,
+ old_select->attr->nexthop,
+ true /*esr*/);
+ }
+ UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
+ bgp_zebra_clear_route_change_flags(rn);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR);
+
+ /* bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(rn);
+
+ if (old_select)
+ bgp_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED);
+ if (new_select) {
+ bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED);
+ bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED);
+ UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG);
+ }
+
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && new_select->sub_type == BGP_ROUTE_IMPORTED) {
+ bgp_evpn_es_vtep_add(bgp, es,
+ new_select->attr->nexthop, true /*esr */);
+ } else {
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED)
+ bgp_evpn_es_vtep_del(
+ bgp, es, old_select->attr->nexthop,
+ true /*esr*/);
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(rn);
+
+ /* Reap old select bgp_path_info, if it has been removed */
+ if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED))
+ bgp_path_info_reap(rn, old_select);
+
+ return ret;
+}
+
+/* Install Type-1/Type-4 route entry in the per-ES routing table */
+static int bgp_evpn_es_route_install(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p,
+ struct bgp_path_info *parent_pi)
+{
+ int ret = 0;
+ struct bgp_node *rn = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct attr *attr_new = NULL;
+
+ /* Create (or fetch) route within the VNI.
+ * NOTE: There is no RD here.
+ */
+ rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Check if route entry is already present. */
+ for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent ==
+ parent_pi)
+ break;
+
+ if (!pi) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_pi->attr);
+
+ /* Create new route with its attribute. */
+ pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0,
+ parent_pi->peer, attr_new, rn);
+ SET_FLAG(pi->flags, BGP_PATH_VALID);
+ bgp_path_info_extra_get(pi);
+ pi->extra->parent = bgp_path_info_lock(parent_pi);
+ bgp_dest_lock_node((struct bgp_node *)parent_pi->net);
+ bgp_path_info_add(rn, pi);
+ } else {
+ if (attrhash_cmp(pi->attr, parent_pi->attr)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ bgp_dest_unlock_node(rn);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_pi->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(rn, pi);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = bgp_clock();
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ ret = bgp_evpn_es_route_select_install(bgp, es, rn);
+
+ bgp_dest_unlock_node(rn);
+
+ return ret;
+}
+
+/* Uninstall Type-1/Type-4 route entry from the ES routing table */
+static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct prefix_evpn *p, struct bgp_path_info *parent_pi)
+{
+ int ret;
+ struct bgp_node *rn;
+ struct bgp_path_info *pi;
+
+ if (!es->route_table)
+ return 0;
+
+ /* Locate route within the ESI.
+ * NOTE: There is no RD here.
+ */
+ rn = bgp_node_lookup(es->route_table, (struct prefix *)p);
+ if (!rn)
+ return 0;
+
+ /* Find matching route entry. */
+ for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent ==
+ parent_pi)
+ break;
+
+ if (!pi)
+ return 0;
+
+ /* Mark entry for deletion */
+ bgp_path_info_delete(rn, pi);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = bgp_evpn_es_route_select_install(bgp, es, rn);
+
+ /* Unlock route node. */
+ bgp_dest_unlock_node(rn);
+
+ return ret;
+}
+
+/* Install or unistall a Tyoe-4 route in the per-ES routing table */
+int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
+ afi_t afi, safi_t safi, struct prefix_evpn *evp,
+ struct bgp_path_info *pi, int install)
+{
+ int ret = 0;
+
+ if (install)
+ ret = bgp_evpn_es_route_install(bgp, es, evp, pi);
+ else
+ ret = bgp_evpn_es_route_uninstall(bgp, es, evp, pi);
+
+ if (ret) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in ESI %s",
+ bgp->vrf_id,
+ install ? "install" : "uninstall",
+ "ES", es->esi_str);
+ return ret;
+ }
+ return 0;
+}
+
+/* Delete (and withdraw) local routes for specified ES from global and ES table.
+ * Also remove all remote routes from the per ES table. Invoked when ES
+ * is deleted.
+ */
+static void bgp_evpn_es_route_del_all(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ struct bgp_node *rn;
+ struct bgp_path_info *pi, *nextpi;
+
+ /* de-activate the ES */
+ bgp_evpn_local_es_down(bgp, es);
+ bgp_evpn_local_type1_evi_route_del(bgp, es);
+
+ /* Walk this ES's routing table and delete all routes. */
+ for (rn = bgp_table_top(es->route_table); rn;
+ rn = bgp_route_next(rn)) {
+ for (pi = bgp_dest_get_bgp_path_info(rn);
+ (pi != NULL) && (nextpi = pi->next, 1);
+ pi = nextpi) {
+ bgp_path_info_delete(rn, pi);
+ bgp_path_info_reap(rn, pi);
+ }
+ }
+}
+
+/*****************************************************************************
+ * Base APIs for creating MH routes (Type-1 or Type-4) on local ethernet
+ * segment updates.
+ */
+
+/* create or update local EVPN type1/type4 route entry.
+ *
+ * This could be in -
+ * the ES table if ESR/EAD-ES (or)
+ * the VNI table if EAD-EVI (or)
+ * the global table if ESR/EAD-ES/EAD-EVI
+ *
+ * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and
+ * ESR).
+ */
+static int bgp_evpn_mh_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct bgpevpn *vpn, afi_t afi,
+ safi_t safi, struct bgp_node *rn, struct attr *attr,
+ int add, struct bgp_path_info **ri, int *route_changed)
+{
+ struct bgp_path_info *tmp_pi = NULL;
+ struct bgp_path_info *local_pi = NULL; /* local route entry if any */
+ struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */
+ struct attr *attr_new = NULL;
+ struct prefix_evpn *evp;
+
+ *ri = NULL;
+ evp = (struct prefix_evpn *)&rn->p;
+ *route_changed = 1;
+
+ /* locate the local and remote entries if any */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(rn); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->peer == bgp->peer_self
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ local_pi = tmp_pi;
+ if (tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_IMPORTED
+ && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID))
+ remote_pi = tmp_pi;
+ }
+
+ /* we don't expect to see a remote_ri at this point as
+ * an ES route has {esi, vtep_ip} as the key in the ES-rt-table
+ * in the VNI-rt-table.
+ */
+ if (remote_pi) {
+ flog_err(
+ EC_BGP_ES_INVALID,
+ "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote",
+ bgp->vrf_id, es->esi_str,
+ inet_ntoa(es->originator_ip));
+ return -1;
+ }
+
+ if (!local_pi && !add)
+ return 0;
+
+ /* create or update the entry */
+ if (!local_pi) {
+
+ /* Add or update attribute to hash */
+ attr_new = bgp_attr_intern(attr);
+
+ /* Create new route with its attribute. */
+ tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp->peer_self, attr_new, rn);
+ SET_FLAG(tmp_pi->flags, BGP_PATH_VALID);
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ bgp_path_info_extra_get(tmp_pi);
+ tmp_pi->extra->num_labels = 1;
+ if (vpn)
+ vni2label(vpn->vni, &tmp_pi->extra->label[0]);
+ else
+ tmp_pi->extra->label[0] = 0;
+ }
+
+ /* add the newly created path to the route-node */
+ bgp_path_info_add(rn, tmp_pi);
+ } else {
+ tmp_pi = local_pi;
+ if (attrhash_cmp(tmp_pi->attr, attr)
+ && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ *route_changed = 0;
+ else {
+ /* The attribute has changed.
+ * Add (or update) attribute to hash.
+ */
+ attr_new = bgp_attr_intern(attr);
+ bgp_path_info_set_flag(rn, tmp_pi,
+ BGP_PATH_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(rn, tmp_pi);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_pi->attr);
+ tmp_pi->attr = attr_new;
+ tmp_pi->uptime = bgp_clock();
+ }
+ }
+
+ if (*route_changed) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("local ES %s vni %u route-type %s nexthop %s updated",
+ es->esi_str,
+ vpn ? vpn->vni : 0,
+ evp->prefix.route_type ==
+ BGP_EVPN_ES_ROUTE ? "esr" :
+ (vpn ? "ead-evi" : "ead-es"),
+ inet_ntoa(attr->mp_nexthop_global_in));
+ }
+
+ /* Return back the route entry. */
+ *ri = tmp_pi;
+ return 0;
+}
+
+/* Delete local EVPN ESR (type-4) and EAD (type-1) route
+ *
+ * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and
+ * ESR).
+ */
+static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct bgpevpn *vpn, struct prefix_evpn *p)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *pi;
+ struct bgp_node *rn = NULL; /* rn in esi table */
+ struct bgp_node *global_rn = NULL; /* rn in global table */
+ struct bgp_table *rt_table;
+ struct prefix_rd *prd;
+
+ if (vpn) {
+ rt_table = vpn->route_table;
+ prd = &vpn->prd;
+ } else {
+ rt_table = es->route_table;
+ prd = &es->prd;
+ }
+
+ /* First, locate the route node within the ESI or VNI.
+ * If it doesn't exist, ther is nothing to do.
+ * Note: there is no RD here.
+ */
+ rn = bgp_node_lookup(rt_table, (struct prefix *)p);
+ if (!rn)
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("local ES %s vni %u route-type %s nexthop %s delete",
+ es->esi_str,
+ vpn ? vpn->vni : 0,
+ p->prefix.route_type == BGP_EVPN_ES_ROUTE ?
+ "esr" : (vpn ? "ead-evi" : "ead-es"),
+ inet_ntoa(es->originator_ip));
+
+ /* Next, locate route node in the global EVPN routing table.
+ * Note that this table is a 2-level tree (RD-level + Prefix-level)
+ */
+ global_rn = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p, prd);
+ if (global_rn) {
+
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (pi)
+ bgp_process(bgp, global_rn, afi, safi);
+ bgp_dest_unlock_node(global_rn);
+ }
+
+ /*
+ * Delete route entry in the ESI or VNI routing table.
+ * This can just be removed.
+ */
+ delete_evpn_route_entry(bgp, afi, safi, rn, &pi);
+ if (pi)
+ bgp_path_info_reap(rn, pi);
+ bgp_dest_unlock_node(rn);
+ return 0;
+}
+
+/*****************************************************************************
+ * Ethernet Segment (Type-4) Routes
+ * ESRs are used for BUM handling. XXX - BUM support is planned for phase-2 i.e.
+ * this code is just a place holder for now
+ */
+/* Build extended community for EVPN ES (type-4) route */
+static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_es_rt;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_es_rt;
+ bgp_encap_types tnl_type;
+ struct ethaddr mac;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (uint8_t *)eval.val;
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* ES import RT */
+ memset(&mac, 0, sizeof(struct ethaddr));
+ memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
+ es_get_system_mac(&es->esi, &mac);
+ encode_es_rt_extcomm(&eval_es_rt, &mac);
+ ecom_es_rt.size = 1;
+ ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
+ attr->ecommunity =
+ ecommunity_merge(attr->ecommunity, &ecom_es_rt);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/* Create or update local type-4 route */
+static int bgp_evpn_type4_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p)
+{
+ int ret = 0;
+ int route_changed = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct attr *attr_new = NULL;
+ struct bgp_node *rn = NULL;
+ struct bgp_path_info *pi = NULL;
+
+ memset(&attr, 0, sizeof(struct attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = es->originator_ip;
+ attr.mp_nexthop_global_in = es->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ /* Set up extended community. */
+ bgp_evpn_type4_route_extcomm_build(es, &attr);
+
+ /* First, create (or fetch) route node within the ESI. */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi,
+ rn, &attr, 1, &pi, &route_changed);
+ if (ret != 0) {
+ flog_err(EC_BGP_ES_INVALID,
+ "%u ERROR: Failed to updated ES route ESI: %s VTEP %s",
+ bgp->vrf_id, es->esi_str,
+ inet_ntoa(es->originator_ip));
+ }
+
+ assert(pi);
+ attr_new = pi->attr;
+
+ /* Perform route selection;
+ * this is just to set the flags correctly
+ * as local route in the ES always wins.
+ */
+ bgp_evpn_es_route_select_install(bgp, es, rn);
+ bgp_dest_unlock_node(rn);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_changed) {
+ struct bgp_path_info *global_pi;
+
+ rn = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ p, &es->prd);
+ bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi,
+ rn, attr_new, 1, &global_pi, &route_changed);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, rn, afi, safi);
+ bgp_dest_unlock_node(rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
+/* Delete local type-4 route */
+static int bgp_evpn_type4_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p)
+{
+ return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p);
+}
+
+/* Process remote/received EVPN type-4 route (advertise or withdraw) */
+int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ int ret;
+ esi_t esi;
+ uint8_t ipaddr_len;
+ struct in_addr vtep_ip;
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+
+ /* Type-4 route should be either 23 or 35 bytes
+ * RD (8), ESI (10), ip-len (1), ip (4 or 16)
+ */
+ if (psize != BGP_EVPN_TYPE4_V4_PSIZE &&
+ psize != BGP_EVPN_TYPE4_V6_PSIZE) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, RD_BYTES);
+ pfx += RD_BYTES;
+
+ /* get the ESI */
+ memcpy(&esi, pfx, ESI_BYTES);
+ pfx += ESI_BYTES;
+
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len == IPV4_MAX_BITLEN) {
+ memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN);
+ } else {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ build_evpn_type4_prefix(&p, &esi, vtep_ip);
+ /* Process the route. */
+ if (attr) {
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ } else {
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ }
+ return ret;
+}
+
+/* Check if a prefix belongs to the local ES */
+static bool bgp_evpn_type4_prefix_match(struct prefix_evpn *p,
+ struct bgp_evpn_es *es)
+{
+ return (p->prefix.route_type == BGP_EVPN_ES_ROUTE) &&
+ !memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t));
+}
+
+/* Import remote ESRs on local ethernet segment add */
+static int bgp_evpn_type4_remote_routes_import(struct bgp *bgp,
+ struct bgp_evpn_es *es, bool install)
+{
+ int ret;
+ afi_t afi;
+ safi_t safi;
+ char buf[PREFIX_STRLEN];
+ struct bgp_node *rd_rn, *rn;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this Ethernet Segment.
+ */
+ for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn;
+ rd_rn = bgp_route_next(rd_rn)) {
+ table = bgp_dest_get_bgp_table_info(rd_rn);
+ if (!table)
+ continue;
+
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ for (pi = bgp_dest_get_bgp_path_info(rn); pi;
+ pi = pi->next) {
+ /*
+ * Consider "valid" remote routes applicable for
+ * this ES.
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (!bgp_evpn_type4_prefix_match(evp, es))
+ continue;
+
+ if (install)
+ ret = bgp_evpn_es_route_install(
+ bgp, es, evp, pi);
+ else
+ ret = bgp_evpn_es_route_uninstall(
+ bgp, es, evp, pi);
+
+ if (ret) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "Failed to %s EVPN %s route in ESI %s",
+ install ? "install"
+ : "uninstall",
+ prefix2str(evp, buf,
+ sizeof(buf)),
+ es->esi_str);
+ return ret;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ * Ethernet Auto Discovery (EAD/Type-1) route handling
+ * There are two types of EAD routes -
+ * 1. EAD-per-ES - Key: {ESI, ET=0xffffffff}
+ * 2. EAD-per-EVI - Key: {ESI, ET=0}
+ */
+
+/* Extended communities associated with EAD-per-ES */
+static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_esi_label;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_esi_label;
+ bgp_encap_types tnl_type;
+ struct listnode *evi_node, *rt_node;
+ struct ecommunity *ecom;
+ struct bgp_evpn_es_evi *es_evi;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (uint8_t *)eval.val;
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* ESI label */
+ encode_esi_label_extcomm(&eval_esi_label,
+ false /*single_active*/);
+ ecom_esi_label.size = 1;
+ ecom_esi_label.val = (uint8_t *)eval_esi_label.val;
+ attr->ecommunity =
+ ecommunity_merge(attr->ecommunity, &ecom_esi_label);
+
+ /* Add export RTs for all L2-VNIs associated with this ES */
+ /* XXX - suppress EAD-ES advertisment if there are no EVIs associated
+ * with it.
+ */
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list,
+ evi_node, es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl,
+ rt_node, ecom))
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ ecom);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/* Extended communities associated with EAD-per-EVI */
+static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn, struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity_val eval;
+ bgp_encap_types tnl_type;
+ struct listnode *rt_node;
+ struct ecommunity *ecom;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (uint8_t *)eval.val;
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* Add export RTs for the L2-VNI */
+ for (ALL_LIST_ELEMENTS_RO(vpn->export_rtl, rt_node, ecom))
+ attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/* Update EVPN EAD (type-1) route -
+ * vpn - valid for EAD-EVI routes and NULL for EAD-ES routes
+ */
+static int bgp_evpn_type1_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ int ret = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct attr *attr_new = NULL;
+ struct bgp_node *rn = NULL;
+ struct bgp_path_info *pi = NULL;
+ int route_changed = 0;
+ struct prefix_rd *global_rd;
+
+ memset(&attr, 0, sizeof(struct attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = es->originator_ip;
+ attr.mp_nexthop_global_in = es->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ if (vpn) {
+ /* EAD-EVI route update */
+ /* MPLS label */
+ vni2label(vpn->vni, &(attr.label));
+
+ /* Set up extended community */
+ bgp_evpn_type1_evi_route_extcomm_build(es, vpn, &attr);
+
+ /* First, create (or fetch) route node within the VNI. */
+ rn = bgp_node_get(vpn->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi,
+ rn, &attr, 1, &pi, &route_changed);
+ if (ret != 0) {
+ flog_err(EC_BGP_ES_INVALID,
+ "%u Failed to update EAD-EVI route ESI: %s VNI %u VTEP %s",
+ bgp->vrf_id, es->esi_str, vpn->vni,
+ inet_ntoa(es->originator_ip));
+ }
+ global_rd = &vpn->prd;
+ } else {
+ /* EAD-ES route update */
+ /* MPLS label is 0 for EAD-ES route */
+
+ /* Set up extended community */
+ bgp_evpn_type1_es_route_extcomm_build(es, &attr);
+
+ /* First, create (or fetch) route node within the ES. */
+ /* NOTE: There is no RD here. */
+ /* XXX: fragment ID must be included as a part of the prefix. */
+ rn = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi,
+ rn, &attr, 1, &pi, &route_changed);
+ if (ret != 0) {
+ flog_err(EC_BGP_ES_INVALID,
+ "%u ERROR: Failed to updated EAD-EVI route ESI: %s VTEP %s",
+ bgp->vrf_id, es->esi_str,
+ inet_ntoa(es->originator_ip));
+ }
+ global_rd = &es->prd;
+ }
+
+
+ assert(pi);
+ attr_new = pi->attr;
+
+ /* Perform route selection;
+ * this is just to set the flags correctly as local route in
+ * the ES always wins.
+ */
+ evpn_route_select_install(bgp, vpn, rn);
+ bgp_dest_unlock_node(rn);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_changed) {
+ struct bgp_path_info *global_pi;
+
+ rn = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ p, global_rd);
+ bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi,
+ rn, attr_new, 1, &global_pi, &route_changed);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, rn, afi, safi);
+ bgp_dest_unlock_node(rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
+/* Delete local Type-1 route */
+static int bgp_evpn_type1_es_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p)
+{
+ return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p);
+}
+
+static int bgp_evpn_type1_evi_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ return bgp_evpn_mh_route_delete(bgp, es, vpn, p);
+}
+
+/* Generate EAD-EVI for all VNIs */
+static void bgp_evpn_local_type1_evi_route_add(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *evi_node;
+ struct prefix_evpn p;
+ struct bgp_evpn_es_evi *es_evi;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI))
+ /* EAD-EVI route add for this ES is already done */
+ return;
+
+ SET_FLAG(es->flags, BGP_EVPNES_ADV_EVI);
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ if (bgp_evpn_type1_route_update(bgp, es, es_evi->vpn, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+}
+
+/*
+ * Withdraw EAD-EVI for all VNIs
+ */
+static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *evi_node;
+ struct prefix_evpn p;
+ struct bgp_evpn_es_evi *es_evi;
+
+ /* Delete and withdraw locally learnt EAD-EVI route */
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI))
+ /* EAD-EVI route has not been advertised for this ES */
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_ADV_EVI);
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ if (bgp_evpn_mh_route_delete(bgp, es, es_evi->vpn, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+}
+
+/*
+ * Process received EVPN type-1 route (advertise or withdraw).
+ */
+int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ int ret;
+ struct prefix_rd prd;
+ esi_t esi;
+ uint32_t eth_tag;
+ mpls_label_t label;
+ struct in_addr vtep_ip;
+ struct prefix_evpn p;
+
+ if (psize != BGP_EVPN_TYPE1_PSIZE) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-1 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, RD_BYTES);
+ pfx += RD_BYTES;
+
+ /* get the ESI */
+ memcpy(&esi, pfx, ESI_BYTES);
+ pfx += ESI_BYTES;
+
+ /* Copy Ethernet Tag */
+ memcpy(&eth_tag, pfx, EVPN_ETH_TAG_BYTES);
+ eth_tag = ntohl(eth_tag);
+ pfx += EVPN_ETH_TAG_BYTES;
+
+ memcpy(&label, pfx, BGP_LABEL_BYTES);
+
+ /* EAD route prefix doesn't include the nexthop in the global
+ * table
+ */
+ vtep_ip.s_addr = 0;
+ build_evpn_type1_prefix(&p, eth_tag, &esi, vtep_ip);
+ /* Process the route. */
+ if (attr) {
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ } else {
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ }
+ return ret;
+}
+
+/*****************************************************************************/
+/* Ethernet Segment Management
+ * 1. Ethernet Segment is a collection of links attached to the same
+ * server (MHD) or switch (MHN)
+ * 2. An Ethernet Segment can span multiple PEs and is identified by the
+ * 10-byte ES-ID.
+ * 3. Local ESs are configured in zebra and sent to BGP
+ * 4. Remote ESs are created by BGP when one or more ES-EVIs reference it i.e.
+ * created on first reference and release on last de-reference
+ * 5. An ES can be both local and remote. Infact most local ESs are expected
+ * to have an ES peer.
+ */
+
+/* A list of remote VTEPs is maintained for each ES. This list includes -
+ * 1. VTEPs for which we have imported the ESR i.e. ES-peers
+ * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI
+ * have been imported into one or more VNIs
+ */
+static int bgp_evpn_es_vtep_cmp(void *p1, void *p2)
+{
+ const struct bgp_evpn_es_vtep *es_vtep1 = p1;
+ const struct bgp_evpn_es_vtep *es_vtep2 = p2;
+
+ return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr;
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_new(struct bgp_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(*es_vtep));
+
+ es_vtep->es = es;
+ es_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ listnode_init(&es_vtep->es_listnode, es_vtep);
+ listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode);
+
+ return es_vtep;
+}
+
+static void bgp_evpn_es_vtep_free(struct bgp_evpn_es_vtep *es_vtep)
+{
+ struct bgp_evpn_es *es = es_vtep->es;
+
+ if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR) ||
+ es_vtep->evi_cnt)
+ /* as long as there is some reference we can't free it */
+ return;
+
+ list_delete_node(es->es_vtep_list, &es_vtep->es_listnode);
+ XFREE(MTYPE_BGP_EVPN_ES_VTEP, es_vtep);
+}
+
+/* check if VTEP is already part of the list */
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_find(struct bgp_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct listnode *node = NULL;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr)
+ return es_vtep;
+ }
+ return NULL;
+}
+
+/* Send the remote ES to zebra for NHG programming */
+static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep, bool add)
+{
+ struct bgp_evpn_es *es = es_vtep->es;
+ struct stream *s;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("No zebra instance, not installing remote es %s",
+ es->esi_str);
+ return 0;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ add ? ZEBRA_REMOTE_ES_VTEP_ADD : ZEBRA_REMOTE_ES_VTEP_DEL,
+ bgp->vrf_id);
+ stream_put(s, &es->esi, sizeof(esi_t));
+ stream_put_ipv4(s, es_vtep->vtep_ip.s_addr);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("Tx %s Remote ESI %s VTEP %s",
+ add ? "ADD" : "DEL", es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip));
+
+ return zclient_send_message(zclient);
+}
+
+static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep)
+{
+ bool old_active;
+ bool new_active;
+
+ old_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ /* currently we need an active EVI reference to use the VTEP as
+ * a nexthop. this may change...
+ */
+ if (es_vtep->evi_cnt)
+ SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ else
+ UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+
+ new_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+
+ if (old_active == new_active)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %s %s",
+ es_vtep->es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip),
+ new_active ? "active" : "inactive");
+
+ /* send remote ES to zebra */
+ bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active);
+
+ /* queue up the es for background consistency checks */
+ bgp_evpn_es_cons_checks_pend_add(es_vtep->es);
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip);
+
+ if (!es_vtep)
+ es_vtep = bgp_evpn_es_vtep_new(es, vtep_ip);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %s add %s",
+ es_vtep->es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip),
+ esr ? "esr" : "ead");
+
+ if (esr)
+ SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR);
+ else
+ ++es_vtep->evi_cnt;
+
+ bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep);
+
+ return es_vtep;
+}
+
+static void bgp_evpn_es_vtep_do_del(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep, bool esr)
+{
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %s del %s",
+ es_vtep->es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip),
+ esr ? "esr" : "ead");
+ if (esr) {
+ UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR);
+ } else {
+ if (es_vtep->evi_cnt)
+ --es_vtep->evi_cnt;
+ }
+
+ bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep);
+ bgp_evpn_es_vtep_free(es_vtep);
+}
+
+static void bgp_evpn_es_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip);
+ if (es_vtep)
+ bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
+}
+
+/* compare ES-IDs for the global ES RB tree */
+static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1,
+ const struct bgp_evpn_es *es2)
+{
+ return memcmp(&es1->esi, &es2->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp);
+
+struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi)
+{
+ struct bgp_evpn_es tmp;
+
+ memcpy(&tmp.esi, esi, sizeof(esi_t));
+ return RB_FIND(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, &tmp);
+}
+
+static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi)
+{
+ struct bgp_evpn_es *es;
+
+ if (!bgp)
+ return NULL;
+
+ es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct bgp_evpn_es));
+
+ /* set the ESI */
+ memcpy(&es->esi, esi, sizeof(esi_t));
+
+ /* Initialise the VTEP list */
+ es->es_vtep_list = list_new();
+ listset_app_node_mem(es->es_vtep_list);
+ es->es_vtep_list->cmp = bgp_evpn_es_vtep_cmp;
+
+ esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str));
+
+ /* Initialize the ES routing table */
+ es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
+
+ /* Add to rb_tree */
+ if (RB_INSERT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es)) {
+ XFREE(MTYPE_BGP_EVPN_ES, es);
+ return NULL;
+ }
+
+ /* Initialise the ES-EVI list */
+ es->es_evi_list = list_new();
+ listset_app_node_mem(es->es_evi_list);
+
+ QOBJ_REG(es, bgp_evpn_es);
+
+ return es;
+}
+
+/* Free a given ES -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+static void bgp_evpn_es_free(struct bgp_evpn_es *es)
+{
+ if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
+ return;
+
+ /* cleanup resources maintained against the ES */
+ list_delete(&es->es_evi_list);
+ list_delete(&es->es_vtep_list);
+ bgp_table_unlock(es->route_table);
+
+ /* remove the entry from various databases */
+ RB_REMOVE(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es);
+ bgp_evpn_es_cons_checks_pend_del(es);
+
+ QOBJ_UNREG(es);
+ XFREE(MTYPE_BGP_EVPN_ES, es);
+}
+
+/* init local info associated with the ES */
+static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ char buf[BGP_EVPN_PREFIX_RD_LEN];
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ return;
+
+ SET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+ listnode_init(&es->es_listnode, es);
+ listnode_add(bgp_mh_info->local_es_list, &es->es_listnode);
+
+ /* auto derive RD for this es */
+ bf_assign_index(bm->rd_idspace, es->rd_id);
+ es->prd.family = AF_UNSPEC;
+ es->prd.prefixlen = 64;
+ snprintf(buf, sizeof(buf), "%s:%hu", inet_ntoa(bgp->router_id),
+ es->rd_id);
+ (void)str2prefix_rd(buf, &es->prd);
+}
+
+/* clear any local info associated with the ES */
+static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es)
+{
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+
+ /* remove from the ES local list */
+ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode);
+
+ bf_release_index(bm->rd_idspace, es->rd_id);
+
+ bgp_evpn_es_free(es);
+}
+
+/* eval remote info associated with the ES */
+static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es)
+{
+ if (es->remote_es_evi_cnt) {
+ SET_FLAG(es->flags, BGP_EVPNES_REMOTE);
+ } else {
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_REMOTE)) {
+ UNSET_FLAG(es->flags, BGP_EVPNES_REMOTE);
+ bgp_evpn_es_free(es);
+ }
+ }
+}
+
+/* Process ES link oper-down by withdrawing ES-EAD and ESR */
+static void bgp_evpn_local_es_down(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct prefix_evpn p;
+ int ret;
+
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP))
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_OPER_UP);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s down", es->esi_str);
+
+ /* withdraw ESR */
+ /* Delete and withdraw locally learnt ES route */
+ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip);
+ ret = bgp_evpn_type4_route_delete(bgp, es, &p);
+ if (ret) {
+ flog_err(EC_BGP_EVPN_ROUTE_DELETE,
+ "%u failed to delete type-4 route for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+
+ /* withdraw EAD-EVI */
+ if (!bgp_mh_info->ead_evi_adv_for_down_links)
+ bgp_evpn_local_type1_evi_route_del(bgp, es);
+
+ /* withdraw EAD-ES */
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG,
+ &es->esi, es->originator_ip);
+ ret = bgp_evpn_type1_es_route_delete(bgp, es, &p);
+ if (ret) {
+ flog_err(EC_BGP_EVPN_ROUTE_DELETE,
+ "%u failed to delete type-1 route for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+}
+
+/* Process ES link oper-up by generating ES-EAD and ESR */
+static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ struct prefix_evpn p;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP))
+ return;
+
+ SET_FLAG(es->flags, BGP_EVPNES_OPER_UP);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s up", es->esi_str);
+
+ /* generate ESR */
+ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip);
+ if (bgp_evpn_type4_route_update(bgp, es, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, es->esi_str);
+
+ /* generate EAD-EVI */
+ bgp_evpn_local_type1_evi_route_add(bgp, es);
+
+ /* generate EAD-ES */
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG,
+ &es->esi, es->originator_ip);
+ bgp_evpn_type1_route_update(bgp, es, NULL, &p);
+}
+
+static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node, *evi_next_node;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del local es %s", es->esi_str);
+
+ /* Delete all local EVPN ES routes from ESI table
+ * and schedule for processing (to withdraw from peers))
+ */
+ bgp_evpn_es_route_del_all(bgp, es);
+
+ /* release all local ES EVIs associated with the ES */
+ for (ALL_LIST_ELEMENTS(es->es_evi_list, evi_node,
+ evi_next_node, es_evi)) {
+ bgp_evpn_local_es_evi_do_del(es_evi);
+ }
+
+ /* Clear local info associated with the ES and free it up if there is
+ * no remote reference
+ */
+ bgp_evpn_es_local_info_clear(es);
+}
+
+bool bgp_evpn_is_esi_local(esi_t *esi)
+{
+ struct bgp_evpn_es *es = NULL;
+
+ /* Lookup ESI hash - should exist. */
+ es = bgp_evpn_es_find(esi);
+ return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false;
+}
+
+int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi)
+{
+ struct bgp_evpn_es *es = NULL;
+
+ /* Lookup ESI hash - should exist. */
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_warn(EC_BGP_EVPN_ESI,
+ "%u: ES %s missing at local ES DEL",
+ bgp->vrf_id, es->esi_str);
+ return -1;
+ }
+
+ bgp_evpn_local_es_do_del(bgp, es);
+ return 0;
+}
+
+/* Handle device to ES id association. Results in the creation of a local
+ * ES.
+ */
+int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
+ struct in_addr originator_ip, bool oper_up)
+{
+ char buf[ESI_STR_LEN];
+ struct bgp_evpn_es *es;
+ bool new_es = true;
+
+ /* create the new es */
+ es = bgp_evpn_es_find(esi);
+ if (es) {
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ new_es = false;
+ } else {
+ es = bgp_evpn_es_new(bgp, esi);
+ if (!es) {
+ flog_err(EC_BGP_ES_CREATE,
+ "%u: Failed to allocate ES entry for ESI %s - at Local ES Add",
+ bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add local es %s orig-ip %s",
+ es->esi_str,
+ inet_ntoa(originator_ip));
+
+ es->originator_ip = originator_ip;
+ bgp_evpn_es_local_info_set(bgp, es);
+
+ /* import all remote Type-4 routes in the ES table */
+ if (new_es)
+ bgp_evpn_type4_remote_routes_import(bgp, es,
+ true /* install */);
+
+ /* create and advertise EAD-EVI routes for the ES -
+ * XXX - till an ES-EVI reference is created there is really nothing to
+ * advertise
+ */
+ if (bgp_mh_info->ead_evi_adv_for_down_links)
+ bgp_evpn_local_type1_evi_route_add(bgp, es);
+
+ /* If the ES link is operationally up generate EAD-ES. EAD-EVI
+ * can be generated even if the link is inactive.
+ */
+ if (oper_up)
+ bgp_evpn_local_es_up(bgp, es);
+ else
+ bgp_evpn_local_es_down(bgp, es);
+
+ return 0;
+}
+
+static char *bgp_evpn_es_vteps_str(char *vtep_str, struct bgp_evpn_es *es,
+ uint8_t vtep_str_size)
+{
+ char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ];
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ bool first = true;
+
+ vtep_str[0] = '\0';
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ vtep_flag_str[0] = '\0';
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str));
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE)
+ strlcat(vtep_flag_str, "A", sizeof(vtep_flag_str));
+
+ if (!strlen(vtep_flag_str))
+ strlcat(vtep_flag_str, "-", sizeof(vtep_flag_str));
+ if (first)
+ first = false;
+ else
+ strlcat(vtep_str, ",", vtep_str_size);
+ strlcat(vtep_str, inet_ntoa(es_vtep->vtep_ip), vtep_str_size);
+ strlcat(vtep_str, "(", vtep_str_size);
+ strlcat(vtep_str, vtep_flag_str, vtep_str_size);
+ strlcat(vtep_str, ")", vtep_str_size);
+ }
+
+ return vtep_str;
+}
+
+static inline void json_array_string_add(json_object *json, const char *str)
+{
+ json_object_array_add(json, json_object_new_string(str));
+}
+
+static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps,
+ struct bgp_evpn_es_vtep *es_vtep)
+{
+ json_object *json_vtep_entry;
+ json_object *json_flags;
+
+ json_vtep_entry = json_object_new_object();
+
+ json_object_string_add(json_vtep_entry, "vtep_ip",
+ inet_ntoa(es_vtep->vtep_ip));
+ if (es_vtep->flags & (BGP_EVPNES_VTEP_ESR |
+ BGP_EVPNES_VTEP_ACTIVE)) {
+ json_flags = json_object_new_array();
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ json_array_string_add(json_flags, "esr");
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE)
+ json_array_string_add(json_flags, "active");
+ json_object_object_add(json_vtep_entry, "flags", json_flags);
+ }
+
+ json_object_array_add(json_vteps,
+ json_vtep_entry);
+}
+
+static void bgp_evpn_es_show_entry(struct vty *vty,
+ struct bgp_evpn_es *es, json_object *json)
+{
+ char buf1[RD_ADDRSTRLEN];
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ if (json) {
+ json_object *json_vteps;
+ json_object *json_types;
+
+ json_object_string_add(json, "esi", es->esi_str);
+ json_object_string_add(json, "rd",
+ prefix_rd2str(&es->prd, buf1,
+ sizeof(buf1)));
+
+ if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) {
+ json_types = json_object_new_array();
+ if (es->flags & BGP_EVPNES_LOCAL)
+ json_array_string_add(json_types, "local");
+ if (es->flags & BGP_EVPNES_REMOTE)
+ json_array_string_add(json_types, "remote");
+ json_object_object_add(json, "type", json_types);
+ }
+
+ if (listcount(es->es_vtep_list)) {
+ json_vteps = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list,
+ node, es_vtep)) {
+ bgp_evpn_es_json_vtep_fill(json_vteps, es_vtep);
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ }
+ json_object_int_add(json, "vniCount",
+ listcount(es->es_evi_list));
+ } else {
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+
+ type_str[0] = '\0';
+ if (es->flags & BGP_EVPNES_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es->flags & BGP_EVPNES_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+ if (es->inconsistencies)
+ strlcat(type_str, "I", sizeof(type_str));
+
+ bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str));
+
+ if (es->flags & BGP_EVPNES_LOCAL)
+ prefix_rd2str(&es->prd, buf1, sizeof(buf1));
+ else
+ strlcpy(buf1, "-", sizeof(buf1));
+
+ vty_out(vty, "%-30s %-5s %-21s %-8d %s\n",
+ es->esi_str, type_str, buf1,
+ listcount(es->es_evi_list), vtep_str);
+ }
+}
+
+static void bgp_evpn_es_show_entry_detail(struct vty *vty,
+ struct bgp_evpn_es *es, json_object *json)
+{
+ if (json) {
+ json_object *json_flags;
+ json_object *json_incons;
+
+ /* Add the "brief" info first */
+ bgp_evpn_es_show_entry(vty, es, json);
+ if (es->flags & (BGP_EVPNES_OPER_UP | BGP_EVPNES_ADV_EVI)) {
+ json_flags = json_object_new_array();
+ if (es->flags & BGP_EVPNES_OPER_UP)
+ json_array_string_add(json_flags, "up");
+ if (es->flags & BGP_EVPNES_ADV_EVI)
+ json_array_string_add(json_flags,
+ "advertiseEVI");
+ json_object_object_add(json, "flags", json_flags);
+ }
+ json_object_string_add(json, "originator_ip",
+ inet_ntoa(es->originator_ip));
+ json_object_int_add(json, "remoteVniCount",
+ es->remote_es_evi_cnt);
+ json_object_int_add(json, "inconsistentVniVtepCount",
+ es->incons_evi_vtep_cnt);
+ if (es->inconsistencies) {
+ json_incons = json_object_new_array();
+ if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST)
+ json_array_string_add(json_incons,
+ "vni-vtep-mismatch");
+ json_object_object_add(json, "inconsistencies",
+ json_incons);
+ }
+ } else {
+ char incons_str[BGP_EVPNES_INCONS_STR_SZ];
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+ char buf1[RD_ADDRSTRLEN];
+
+ type_str[0] = '\0';
+ if (es->flags & BGP_EVPNES_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es->flags & BGP_EVPNES_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+
+ bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str));
+ if (!strlen(vtep_str))
+ strlcpy(buf1, "-", sizeof(buf1));
+
+ if (es->flags & BGP_EVPNES_LOCAL)
+ prefix_rd2str(&es->prd, buf1, sizeof(buf1));
+ else
+ strlcpy(buf1, "-", sizeof(buf1));
+
+ vty_out(vty, "ESI: %s\n", es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " RD: %s\n", buf1);
+ vty_out(vty, " Originator-IP: %s\n",
+ inet_ntoa(es->originator_ip));
+ vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list));
+ vty_out(vty, " Remote VNI Count: %d\n",
+ es->remote_es_evi_cnt);
+ vty_out(vty, " Inconsistent VNI VTEP Count: %d\n",
+ es->incons_evi_vtep_cnt);
+ if (es->inconsistencies) {
+ incons_str[0] = '\0';
+ if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST)
+ strlcat(incons_str, "vni-vtep-mismatch",
+ sizeof(incons_str));
+ } else {
+ strlcpy(incons_str, "-", sizeof(incons_str));
+ }
+ vty_out(vty, " Inconsistencies: %s\n",
+ incons_str);
+ vty_out(vty, " VTEPs: %s\n", vtep_str);
+ vty_out(vty, "\n");
+ }
+}
+
+/* Display all ESs */
+void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail)
+{
+ struct bgp_evpn_es *es;
+ json_object *json_array = NULL;
+ json_object *json = NULL;
+
+ if (uj) {
+ /* create an array of ESs */
+ json_array = json_object_new_array();
+ } else {
+ if (!detail) {
+ vty_out(vty,
+ "ES Flags: L local, R remote, I inconsistent\n");
+ vty_out(vty,
+ "VTEP Flags: E ESR/Type-4, A active nexthop\n");
+ vty_out(vty,
+ "%-30s %-5s %-21s %-8s %s\n",
+ "ESI", "Flags", "RD", "#VNIs", "VTEPs");
+ }
+ }
+
+ RB_FOREACH(es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ if (uj)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+ if (detail)
+ bgp_evpn_es_show_entry_detail(vty, es, json);
+ else
+ bgp_evpn_es_show_entry(vty, es, json);
+ /* add ES to the json array */
+ if (uj)
+ json_object_array_add(json_array, json);
+ }
+
+ /* print the array of json-ESs */
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json_array, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_array);
+ }
+}
+
+/* Display specific ES */
+void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj)
+{
+ struct bgp_evpn_es *es;
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ es = bgp_evpn_es_find(esi);
+ if (es) {
+ bgp_evpn_es_show_entry_detail(vty, es, json);
+ } else {
+ if (!uj)
+ vty_out(vty, "ESI not found\n");
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*****************************************************************************/
+/* Ethernet Segment to EVI association -
+ * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI
+ * (bgpevpn->es_evi_rb_tree).
+ * 2. Each local ES-EVI entry is rxed from zebra and then used by BGP to
+ * advertises an EAD-EVI (Type-1 EVPN) route
+ * 3. The remote ES-EVI is created when a bgp_evpn_es_evi_vtep references
+ * it.
+ */
+
+/* A list of remote VTEPs is maintained for each ES-EVI. This list includes -
+ * 1. VTEPs for which we have imported the EAD-per-ES Type1 route
+ * 2. VTEPs for which we have imported the EAD-per-EVI Type1 route
+ * VTEPs for which both routes have been rxed are activated. Activation
+ * creates a NHG in the parent ES.
+ */
+static int bgp_evpn_es_evi_vtep_cmp(void *p1, void *p2)
+{
+ const struct bgp_evpn_es_evi_vtep *evi_vtep1 = p1;
+ const struct bgp_evpn_es_evi_vtep *evi_vtep2 = p2;
+
+ return evi_vtep1->vtep_ip.s_addr - evi_vtep2->vtep_ip.s_addr;
+}
+
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_new(
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_EVI_VTEP, sizeof(*evi_vtep));
+
+ evi_vtep->es_evi = es_evi;
+ evi_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ listnode_init(&evi_vtep->es_evi_listnode, evi_vtep);
+ listnode_add_sort(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode);
+
+ return evi_vtep;
+}
+
+static void bgp_evpn_es_evi_vtep_free(struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ struct bgp_evpn_es_evi *es_evi = evi_vtep->es_evi;
+
+ if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD))
+ /* as long as there is some reference we can't free it */
+ return;
+
+ list_delete_node(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode);
+ XFREE(MTYPE_BGP_EVPN_ES_EVI_VTEP, evi_vtep);
+}
+
+/* check if VTEP is already part of the list */
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_find(
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip)
+{
+ struct listnode *node = NULL;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ if (evi_vtep->vtep_ip.s_addr == vtep_ip.s_addr)
+ return evi_vtep;
+ }
+ return NULL;
+}
+
+/* A VTEP can be added as "active" attach to an ES if EAD-per-ES and
+ * EAD-per-EVI routes are rxed from it.
+ */
+static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ bool old_active;
+ bool new_active;
+
+ old_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ /* Both EAD-per-ES and EAD-per-EVI routes must be rxed from a PE
+ * before it can be activated.
+ */
+ if ((evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD) ==
+ BGP_EVPN_EVI_VTEP_EAD)
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+ else
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ new_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ if (old_active == new_active)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s evi %u vtep %s %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni,
+ inet_ntoa(evi_vtep->vtep_ip),
+ new_active ? "active" : "inactive");
+
+ /* add VTEP to parent es */
+ if (new_active) {
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = bgp_evpn_es_vtep_add(bgp, evi_vtep->es_evi->es,
+ evi_vtep->vtep_ip, false /*esr*/);
+ evi_vtep->es_vtep = es_vtep;
+ } else {
+ if (evi_vtep->es_vtep) {
+ bgp_evpn_es_vtep_do_del(bgp, evi_vtep->es_vtep,
+ false /*esr*/);
+ evi_vtep->es_vtep = NULL;
+ }
+ }
+ /* queue up the parent es for background consistency checks */
+ bgp_evpn_es_cons_checks_pend_add(evi_vtep->es_evi->es);
+}
+
+static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
+ bool ead_es)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip);
+
+ if (!evi_vtep)
+ evi_vtep = bgp_evpn_es_evi_vtep_new(es_evi, vtep_ip);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add es %s evi %u vtep %s %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni,
+ inet_ntoa(evi_vtep->vtep_ip),
+ ead_es ? "ead_es" : "ead_evi");
+
+ if (ead_es)
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES);
+ else
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+}
+
+static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
+ bool ead_es)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip);
+ if (!evi_vtep)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del es %s evi %u vtep %s %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni,
+ inet_ntoa(evi_vtep->vtep_ip),
+ ead_es ? "ead_es" : "ead_evi");
+
+ if (ead_es)
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES);
+ else
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ bgp_evpn_es_evi_vtep_free(evi_vtep);
+}
+
+/* compare ES-IDs for the ES-EVI RB tree maintained per-VNI */
+static int bgp_es_evi_rb_cmp(const struct bgp_evpn_es_evi *es_evi1,
+ const struct bgp_evpn_es_evi *es_evi2)
+{
+ return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, bgp_es_evi_rb_cmp);
+
+/* find the ES-EVI in the per-L2-VNI RB tree */
+static struct bgp_evpn_es_evi *bgp_evpn_es_evi_find(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi es_evi;
+
+ es_evi.es = es;
+
+ return RB_FIND(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, &es_evi);
+}
+
+/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES
+ * tables.
+ */
+static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+
+ es_evi = XCALLOC(MTYPE_BGP_EVPN_ES_EVI, sizeof(*es_evi));
+
+ es_evi->es = es;
+ es_evi->vpn = vpn;
+
+ /* Initialise the VTEP list */
+ es_evi->es_evi_vtep_list = list_new();
+ listset_app_node_mem(es_evi->es_evi_vtep_list);
+ es_evi->es_evi_vtep_list->cmp = bgp_evpn_es_evi_vtep_cmp;
+
+ /* insert into the VNI-ESI rb tree */
+ if (RB_INSERT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi)) {
+ XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi);
+ return NULL;
+ }
+
+ /* add to the ES's VNI list */
+ listnode_init(&es_evi->es_listnode, es_evi);
+ listnode_add(es->es_evi_list, &es_evi->es_listnode);
+
+ return es_evi;
+}
+
+/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free
+ * up the memory.
+ */
+static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es *es = es_evi->es;
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ /* cannot free the element as long as there is a local or remote
+ * reference
+ */
+ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE))
+ return;
+
+ /* remove from the ES's VNI list */
+ list_delete_node(es->es_evi_list, &es_evi->es_listnode);
+
+ /* remove from the VNI-ESI rb tree */
+ RB_REMOVE(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi);
+
+ /* free the VTEP list */
+ list_delete(&es_evi->es_evi_vtep_list);
+
+ /* remove from the VNI-ESI rb tree */
+ XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi);
+}
+
+/* init local info associated with the ES-EVI */
+static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ return;
+
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
+ listnode_init(&es_evi->l2vni_listnode, es_evi);
+ listnode_add(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
+}
+
+/* clear any local info associated with the ES-EVI */
+static void bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ return;
+
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
+ list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
+
+ bgp_evpn_es_evi_free(es_evi);
+}
+
+/* eval remote info associated with the ES */
+static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es *es = es_evi->es;
+
+ /* if there are remote VTEPs the ES-EVI is classified as "remote" */
+ if (listcount(es_evi->es_evi_vtep_list)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) {
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE);
+ ++es->remote_es_evi_cnt;
+ /* set remote on the parent es */
+ bgp_evpn_es_remote_info_re_eval(es);
+ }
+ } else {
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) {
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE);
+ if (es->remote_es_evi_cnt)
+ --es->remote_es_evi_cnt;
+ bgp_evpn_es_evi_free(es_evi);
+ /* check if "remote" can be cleared from the
+ * parent es.
+ */
+ bgp_evpn_es_remote_info_re_eval(es);
+ }
+ }
+}
+
+static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi)
+{
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es = es_evi->es;
+ struct bgp *bgp;
+
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del local es %s evi %u",
+ es_evi->es->esi_str,
+ es_evi->vpn->vni);
+
+ bgp = bgp_get_evpn();
+
+ if (bgp) {
+ /* update EAD-ES with new list of VNIs */
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) {
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG,
+ &es->esi, es->originator_ip);
+ if (bgp_evpn_type1_route_update(bgp, es, NULL, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: EAD-ES route update failure for ESI %s VNI %u",
+ bgp->vrf_id, es->esi_str,
+ es_evi->vpn->vni);
+ }
+
+ /* withdraw and delete EAD-EVI */
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ if (bgp_evpn_type1_evi_route_delete(bgp,
+ es, es_evi->vpn, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_DELETE,
+ "%u: EAD-EVI route deletion failure for ESI %s VNI %u",
+ bgp->vrf_id, es->esi_str,
+ es_evi->vpn->vni);
+ }
+ }
+
+ bgp_evpn_es_evi_local_info_clear(es_evi);
+
+}
+
+int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ char buf[ESI_STR_LEN];
+
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; ES not present",
+ bgp->vrf_id, vni,
+ esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; ES-VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ bgp_evpn_local_es_evi_do_del(es_evi);
+ return 0;
+}
+
+/* Create ES-EVI and advertise the corresponding EAD routes */
+int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ char buf[ESI_STR_LEN];
+
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to associate VNI %d with ESI %s; ES not present",
+ bgp->vrf_id, vni,
+ esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to associate VNI %d with ESI %s; VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add local es %s evi %u",
+ es->esi_str, vni);
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+
+ if (es_evi) {
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ /* dup */
+ return 0;
+ } else {
+ es_evi = bgp_evpn_es_evi_new(es, vpn);
+ if (!es_evi)
+ return -1;
+ }
+
+ bgp_evpn_es_evi_local_info_set(es_evi);
+
+ /* generate an EAD-EVI for this new VNI */
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
+ if (bgp_evpn_type1_route_update(bgp, es, vpn, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: EAD-EVI route creation failure for ESI %s VNI %u",
+ bgp->vrf_id, es->esi_str, vni);
+ }
+
+ /* update EAD-ES */
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG,
+ &es->esi, es->originator_ip);
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) {
+ if (bgp_evpn_type1_route_update(bgp, es, NULL, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: EAD-ES route creation failure for ESI %s VNI %u",
+ bgp->vrf_id, es->esi_str, vni);
+ }
+
+ return 0;
+}
+
+/* Add remote ES-EVI entry. This is actually the remote VTEP add and the
+ * ES-EVI is implicity created on first VTEP's reference.
+ */
+int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
+{
+ char buf[ESI_STR_LEN];
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ bool ead_es;
+ const esi_t *esi = &p->prefix.ead_addr.esi;
+
+ if (!vpn)
+ /* local EAD-ES need not be sent back to zebra */
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add remote %s es %s evi %u vtep %s",
+ p->prefix.ead_addr.eth_tag ?
+ "ead-es" : "ead-evi",
+ esi_to_str(esi, buf,
+ sizeof(buf)),
+ vpn->vni,
+ inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4));
+
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ es = bgp_evpn_es_new(bgp, esi);
+ if (!es) {
+ flog_err(EC_BGP_ES_CREATE,
+ "%u: Failed to allocate ES entry for ESI %s - at remote ES Add",
+ bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+ }
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi) {
+ es_evi = bgp_evpn_es_evi_new(es, vpn);
+ if (!es_evi) {
+ bgp_evpn_es_free(es);
+ return -1;
+ }
+ }
+
+ ead_es = !!p->prefix.ead_addr.eth_tag;
+ bgp_evpn_es_evi_vtep_add(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
+ ead_es);
+
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+ return 0;
+}
+
+/* A remote VTEP has withdrawn. The es-evi-vtep will be deleted and the
+ * parent es-evi freed up implicitly in last VTEP's deref.
+ */
+int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
+{
+ char buf[ESI_STR_LEN];
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ bool ead_es;
+
+ if (!vpn)
+ /* local EAD-ES need not be sent back to zebra */
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del remote %s es %s evi %u vtep %s",
+ p->prefix.ead_addr.eth_tag ?
+ "ead-es" : "ead-evi",
+ esi_to_str(&p->prefix.ead_addr.esi, buf,
+ sizeof(buf)),
+ vpn->vni,
+ inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4));
+
+ es = bgp_evpn_es_find(&p->prefix.ead_addr.esi);
+ if (!es)
+ /* XXX - error logs */
+ return 0;
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi)
+ /* XXX - error logs */
+ return 0;
+
+ ead_es = !!p->prefix.ead_addr.eth_tag;
+ bgp_evpn_es_evi_vtep_del(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
+ ead_es);
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+ return 0;
+}
+
+/* Initialize the ES tables maintained per-L2_VNI */
+void bgp_evpn_vni_es_init(struct bgpevpn *vpn)
+{
+ /* Initialize the ES-EVI RB tree */
+ RB_INIT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree);
+
+ /* Initialize the local list maintained for quick walks by type */
+ vpn->local_es_evi_list = list_new();
+ listset_app_node_mem(vpn->local_es_evi_list);
+}
+
+/* Cleanup the ES info maintained per-L2_VNI */
+void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ struct bgp_evpn_es_evi *es_evi_next;
+
+ RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head,
+ &vpn->es_evi_rb_tree, es_evi_next) {
+ bgp_evpn_local_es_evi_do_del(es_evi);
+ }
+
+ list_delete(&vpn->local_es_evi_list);
+}
+
+static char *bgp_evpn_es_evi_vteps_str(char *vtep_str,
+ struct bgp_evpn_es_evi *es_evi,
+ uint8_t vtep_str_size)
+{
+ char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ];
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ bool first = true;
+
+ vtep_str[0] = '\0';
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ vtep_flag_str[0] = '\0';
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES)
+ strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str));
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+ strlcat(vtep_flag_str, "V", sizeof(vtep_flag_str));
+
+ if (!strnlen(vtep_flag_str, sizeof(vtep_flag_str)))
+ strlcpy(vtep_flag_str, "-", sizeof(vtep_flag_str));
+ if (first)
+ first = false;
+ else
+ strlcat(vtep_str, ",", vtep_str_size);
+ strlcat(vtep_str, inet_ntoa(evi_vtep->vtep_ip), vtep_str_size);
+ strlcat(vtep_str, "(", vtep_str_size);
+ strlcat(vtep_str, vtep_flag_str, vtep_str_size);
+ strlcat(vtep_str, ")", vtep_str_size);
+ }
+
+ return vtep_str;
+}
+
+static void bgp_evpn_es_evi_json_vtep_fill(json_object *json_vteps,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ json_object *json_vtep_entry;
+ json_object *json_flags;
+
+ json_vtep_entry = json_object_new_object();
+
+ json_object_string_add(json_vtep_entry,
+ "vtep_ip",
+ inet_ntoa(evi_vtep->vtep_ip));
+ if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD_PER_ES |
+ BGP_EVPN_EVI_VTEP_EAD_PER_EVI)) {
+ json_flags = json_object_new_array();
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES)
+ json_array_string_add(json_flags, "ead-per-es");
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+ json_array_string_add(json_flags, "ed-per-evi");
+ json_object_object_add(json_vtep_entry,
+ "flags", json_flags);
+ }
+
+ json_object_array_add(json_vteps,
+ json_vtep_entry);
+}
+
+static void bgp_evpn_es_evi_show_entry(struct vty *vty,
+ struct bgp_evpn_es_evi *es_evi, json_object *json)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ if (json) {
+ json_object *json_vteps;
+ json_object *json_types;
+
+ json_object_string_add(json, "esi", es_evi->es->esi_str);
+ json_object_int_add(json, "vni", es_evi->vpn->vni);
+
+ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL |
+ BGP_EVPNES_EVI_REMOTE)) {
+ json_types = json_object_new_array();
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ json_array_string_add(json_types, "local");
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ json_array_string_add(json_types, "remote");
+ json_object_object_add(json, "type", json_types);
+ }
+
+ if (listcount(es_evi->es_evi_vtep_list)) {
+ json_vteps = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list,
+ node, evi_vtep)) {
+ bgp_evpn_es_evi_json_vtep_fill(json_vteps,
+ evi_vtep);
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ }
+ } else {
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+
+ type_str[0] = '\0';
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST)
+ strlcat(type_str, "I", sizeof(type_str));
+
+ bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str));
+
+ vty_out(vty, "%-8d %-30s %-5s %s\n",
+ es_evi->vpn->vni, es_evi->es->esi_str,
+ type_str, vtep_str);
+ }
+}
+
+static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty,
+ struct bgp_evpn_es_evi *es_evi, json_object *json)
+{
+ if (json) {
+ json_object *json_flags;
+
+ /* Add the "brief" info first */
+ bgp_evpn_es_evi_show_entry(vty, es_evi, json);
+ if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) {
+ json_flags = json_object_new_array();
+ json_array_string_add(json_flags, "es-vtep-mismatch");
+ json_object_object_add(json, "flags", json_flags);
+ }
+ } else {
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+ char type_str[4];
+
+ type_str[0] = '\0';
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+
+ bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str));
+ if (!strlen(vtep_str))
+ strlcpy(vtep_str, "-", sizeof(type_str));
+
+ vty_out(vty, "VNI: %d ESI: %s\n",
+ es_evi->vpn->vni, es_evi->es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " Inconsistencies: %s\n",
+ (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ?
+ "es-vtep-mismatch":"-");
+ vty_out(vty, " VTEPs: %s\n", vtep_str);
+ vty_out(vty, "\n");
+ }
+}
+
+static void bgp_evpn_es_evi_show_one_vni(struct bgpevpn *vpn, struct vty *vty,
+ json_object *json_array, bool detail)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ json_object *json = NULL;
+
+ RB_FOREACH(es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree) {
+ if (json_array)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+ if (detail)
+ bgp_evpn_es_evi_show_entry_detail(vty, es_evi, json);
+ else
+ bgp_evpn_es_evi_show_entry(vty, es_evi, json);
+ /* add ES to the json array */
+ if (json_array)
+ json_object_array_add(json_array, json);
+ }
+}
+
+struct es_evi_show_ctx {
+ struct vty *vty;
+ json_object *json;
+ int detail;
+};
+
+static void bgp_evpn_es_evi_show_one_vni_hash_cb(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct es_evi_show_ctx *wctx = (struct es_evi_show_ctx *)ctxt;
+
+ bgp_evpn_es_evi_show_one_vni(vpn, wctx->vty, wctx->json, wctx->detail);
+}
+
+/* Display all ES EVIs */
+void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail)
+{
+ json_object *json_array = NULL;
+ struct es_evi_show_ctx wctx;
+ struct bgp *bgp;
+
+ if (uj) {
+ /* create an array of ES-EVIs */
+ json_array = json_object_new_array();
+ }
+
+ wctx.vty = vty;
+ wctx.json = json_array;
+ wctx.detail = detail;
+
+ bgp = bgp_get_evpn();
+
+ if (!json_array && !detail) {
+ vty_out(vty, "Flags: L local, R remote, I inconsistent\n");
+ vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n");
+ vty_out(vty, "%-8s %-30s %-5s %s\n",
+ "VNI", "ESI", "Flags", "VTEPs");
+ }
+
+ if (bgp)
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_es_evi_show_one_vni_hash_cb,
+ &wctx);
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json_array, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_array);
+ }
+}
+
+/* Display specific ES EVI */
+void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
+ bool uj, bool detail)
+{
+ struct bgpevpn *vpn = NULL;
+ json_object *json_array = NULL;
+ struct bgp *bgp;
+
+ if (uj) {
+ /* create an array of ES-EVIs */
+ json_array = json_object_new_array();
+ }
+
+ bgp = bgp_get_evpn();
+ if (bgp)
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+
+ if (vpn) {
+ if (!json_array && !detail) {
+ vty_out(vty, "Flags: L local, R remote, I inconsistent\n");
+ vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n");
+ vty_out(vty, "%-8s %-30s %-5s %s\n",
+ "VNI", "ESI", "Flags", "VTEPs");
+ }
+
+ bgp_evpn_es_evi_show_one_vni(vpn, vty, json_array, detail);
+ } else {
+ if (!uj)
+ vty_out(vty, "VNI not found\n");
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json_array, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_array);
+ }
+}
+
+/*****************************************************************************
+ * Ethernet Segment Consistency checks
+ * Consistency checking is done to detect misconfig or mis-cabling. When
+ * an inconsistency is detected it is simply logged (and displayed via
+ * show commands) at this point. A more drastic action can be executed (based
+ * on user config) in the future.
+ */
+/* queue up the es for background consistency checks */
+static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es)
+{
+ if (!bgp_mh_info->consistency_checking)
+ /* consistency checking is not enabled */
+ return;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND))
+ /* already queued for consistency checking */
+ return;
+
+ SET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND);
+ listnode_init(&es->pend_es_listnode, es);
+ listnode_add_after(bgp_mh_info->pend_es_list,
+ listtail_unchecked(bgp_mh_info->pend_es_list),
+ &es->pend_es_listnode);
+}
+
+/* pull the ES from the consistency check list */
+static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es)
+{
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND))
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND);
+ list_delete_node(bgp_mh_info->pend_es_list,
+ &es->pend_es_listnode);
+}
+
+/* Number of active VTEPs associated with the ES-per-EVI */
+static uint32_t bgp_evpn_es_evi_get_active_vtep_cnt(
+ struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ struct listnode *node;
+ uint32_t vtep_cnt = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ if (CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE))
+ ++vtep_cnt;
+ }
+
+ return vtep_cnt;
+}
+
+/* Number of active VTEPs associated with the ES */
+static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ uint32_t vtep_cnt = 0;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ ++vtep_cnt;
+ }
+
+ return vtep_cnt;
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_get_next_active_vtep(
+ struct bgp_evpn_es *es, struct bgp_evpn_es_vtep *es_vtep)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *next_es_vtep;
+
+ if (es_vtep)
+ node = listnextnode_unchecked(&es_vtep->es_listnode);
+ else
+ node = listhead(es->es_vtep_list);
+
+ for (; node; node = listnextnode_unchecked(node)) {
+ next_es_vtep = listgetdata(node);
+ if (CHECK_FLAG(next_es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ return next_es_vtep;
+ }
+
+ return NULL;
+}
+
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_get_next_active_vtep(
+ struct bgp_evpn_es_evi *es_evi,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *next_evi_vtep;
+
+ if (evi_vtep)
+ node = listnextnode_unchecked(&evi_vtep->es_evi_listnode);
+ else
+ node = listhead(es_evi->es_evi_vtep_list);
+
+ for (; node; node = listnextnode_unchecked(node)) {
+ next_evi_vtep = listgetdata(node);
+ if (CHECK_FLAG(next_evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE))
+ return next_evi_vtep;
+ }
+
+ return NULL;
+}
+
+static void bgp_evpn_es_evi_set_inconsistent(struct bgp_evpn_es_evi *es_evi)
+{
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("inconsistency detected - es %s evi %u vtep list mismatch",
+ es_evi->es->esi_str,
+ es_evi->vpn->vni);
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST);
+
+ /* update parent ES with the incosistency setting */
+ if (!es_evi->es->incons_evi_vtep_cnt &&
+ BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("inconsistency detected - es %s vtep list mismatch",
+ es_evi->es->esi_str);
+ ++es_evi->es->incons_evi_vtep_cnt;
+ SET_FLAG(es_evi->es->inconsistencies,
+ BGP_EVPNES_INCONS_VTEP_LIST);
+ }
+}
+
+static uint32_t bgp_evpn_es_run_consistency_checks(struct bgp_evpn_es *es)
+{
+ int proc_cnt = 0;
+ int es_active_vtep_cnt;
+ int evi_active_vtep_cnt;
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ /* reset the inconsistencies and re-evaluate */
+ es->incons_evi_vtep_cnt = 0;
+ es->inconsistencies = 0;
+
+ es_active_vtep_cnt = bgp_evpn_es_get_active_vtep_cnt(es);
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list,
+ evi_node, es_evi)) {
+ ++proc_cnt;
+
+ /* reset the inconsistencies on the EVI and re-evaluate*/
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST);
+
+ evi_active_vtep_cnt =
+ bgp_evpn_es_evi_get_active_vtep_cnt(es_evi);
+ if (es_active_vtep_cnt != evi_active_vtep_cnt) {
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ continue;
+ }
+
+ if (!es_active_vtep_cnt)
+ continue;
+
+ es_vtep = NULL;
+ evi_vtep = NULL;
+ while ((es_vtep = bgp_evpn_es_get_next_active_vtep(
+ es, es_vtep))) {
+ evi_vtep = bgp_evpn_es_evi_get_next_active_vtep(es_evi,
+ evi_vtep);
+ if (!evi_vtep) {
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ break;
+ }
+ if (es_vtep->vtep_ip.s_addr !=
+ evi_vtep->vtep_ip.s_addr) {
+ /* inconsistency detected; set it and move
+ * to the next evi
+ */
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ break;
+ }
+ }
+ }
+
+ return proc_cnt;
+}
+
+static int bgp_evpn_run_consistency_checks(struct thread *t)
+{
+ int proc_cnt = 0;
+ int es_cnt = 0;
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct bgp_evpn_es *es;
+
+ for (ALL_LIST_ELEMENTS(bgp_mh_info->pend_es_list,
+ node, nextnode, es)) {
+ ++es_cnt;
+ ++proc_cnt;
+ /* run consistency checks on the ES and remove it from the
+ * pending list
+ */
+ proc_cnt += bgp_evpn_es_run_consistency_checks(es);
+ bgp_evpn_es_cons_checks_pend_del(es);
+ if (proc_cnt > 500)
+ break;
+ }
+
+ /* restart the timer */
+ thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL,
+ BGP_EVPN_CONS_CHECK_INTERVAL,
+ &bgp_mh_info->t_cons_check);
+
+ return 0;
+}
+
+/*****************************************************************************/
+void bgp_evpn_mh_init(void)
+{
+ bm->mh_info = XCALLOC(MTYPE_BGP_EVPN_MH_INFO, sizeof(*bm->mh_info));
+
+ /* setup ES tables */
+ RB_INIT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree);
+ /* local ES list */
+ bgp_mh_info->local_es_list = list_new();
+ listset_app_node_mem(bgp_mh_info->local_es_list);
+ /* list of ESs with pending processing */
+ bgp_mh_info->pend_es_list = list_new();
+ listset_app_node_mem(bgp_mh_info->pend_es_list);
+
+ /* config knobs - XXX add cli to control it */
+ bgp_mh_info->ead_evi_adv_for_down_links = true;
+ bgp_mh_info->consistency_checking = true;
+
+ if (bgp_mh_info->consistency_checking)
+ thread_add_timer(bm->master, bgp_evpn_run_consistency_checks,
+ NULL, BGP_EVPN_CONS_CHECK_INTERVAL,
+ &bgp_mh_info->t_cons_check);
+
+ memset(&zero_esi_buf, 0, sizeof(esi_t));
+}
+
+void bgp_evpn_mh_finish(void)
+{
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es *es_next;
+ struct bgp *bgp;
+
+ bgp = bgp_get_evpn();
+ if (bgp) {
+ RB_FOREACH_SAFE(es, bgp_es_rb_head,
+ &bgp_mh_info->es_rb_tree, es_next) {
+ /* XXX - need to force free remote ESs here */
+ bgp_evpn_local_es_do_del(bgp, es);
+ }
+ }
+ thread_cancel(bgp_mh_info->t_cons_check);
+ list_delete(&bgp_mh_info->local_es_list);
+ list_delete(&bgp_mh_info->pend_es_list);
+
+ XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info);
+}
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
new file mode 100644
index 0000000000..93355d495a
--- /dev/null
+++ b/bgpd/bgp_evpn_mh.h
@@ -0,0 +1,299 @@
+/* EVPN header for multihoming procedures
+ *
+ * Copyright (C) 2019 Cumulus Networks
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _FRR_BGP_EVPN_MH_H
+#define _FRR_BGP_EVPN_MH_H
+
+#include "vxlan.h"
+#include "bgpd.h"
+#include "bgp_evpn.h"
+#include "bgp_evpn_private.h"
+
+#define BGP_EVPN_AD_ES_ETH_TAG 0xffffffff
+#define BGP_EVPN_AD_EVI_ETH_TAG 0
+
+#define BGP_EVPNES_INCONS_STR_SZ 80
+#define BGP_EVPN_FLAG_STR_SZ 5
+#define BGP_EVPN_VTEPS_FLAG_STR_SZ (BGP_EVPN_FLAG_STR_SZ * ES_VTEP_MAX_CNT)
+
+#define BGP_EVPN_CONS_CHECK_INTERVAL 60
+
+
+/* Ethernet Segment entry -
+ * - Local and remote ESs are maintained in a global RB tree,
+ * bgp_mh_info->es_rb_tree using ESI as key
+ * - Local ESs are received from zebra (BGP_EVPNES_LOCAL)
+ * - Remotes ESs are implicitly created (by reference) by a remote ES-EVI
+ * (BGP_EVPNES_REMOTE)
+ * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are
+ * expected to have REMOTE ES peers.
+ */
+struct bgp_evpn_es {
+ /* Ethernet Segment Identifier */
+ esi_t esi;
+ char esi_str[ESI_STR_LEN];
+
+ /* es flags */
+ uint32_t flags;
+ /* created via zebra config */
+#define BGP_EVPNES_LOCAL (1 << 0)
+ /* created implicitly by a remote ES-EVI reference */
+#define BGP_EVPNES_REMOTE (1 << 1)
+ /* local ES link is oper-up */
+#define BGP_EVPNES_OPER_UP (1 << 2)
+ /* enable generation of EAD-EVI routes */
+#define BGP_EVPNES_ADV_EVI (1 << 3)
+ /* consistency checks pending */
+#define BGP_EVPNES_CONS_CHECK_PEND (1 << 4)
+
+ /* memory used for adding the es to bgp->es_rb_tree */
+ RB_ENTRY(bgp_evpn_es) rb_node;
+
+ /* [EVPNES_LOCAL] memory used for linking the es to
+ * bgp_mh_info->local_es_list
+ */
+ struct listnode es_listnode;
+
+ /* memory used for linking the es to "processing" pending list
+ * bgp_mh_info->pend_es_list
+ */
+ struct listnode pend_es_listnode;
+
+ /* [EVPNES_LOCAL] Id for deriving the RD automatically for this ESI */
+ uint16_t rd_id;
+
+ /* [EVPNES_LOCAL] RD for this ES */
+ struct prefix_rd prd;
+
+ /* [EVPNES_LOCAL] originator ip address */
+ struct in_addr originator_ip;
+
+ /* [EVPNES_LOCAL] Route table for EVPN routes for this ESI-
+ * - Type-4 local and remote routes
+ * - Type-1 local routes
+ */
+ struct bgp_table *route_table;
+
+ /* list of PEs (bgp_evpn_es_vtep) attached to the ES */
+ struct list *es_vtep_list;
+
+ /* List of ES-EVIs associated with this ES */
+ struct list *es_evi_list;
+
+ /* Number of remote VNIs referencing this ES */
+ uint32_t remote_es_evi_cnt;
+
+ uint32_t inconsistencies;
+ /* there are one or more EVIs whose VTEP list doesn't match
+ * with the ES's VTEP list
+ */
+#define BGP_EVPNES_INCONS_VTEP_LIST (1 << 0)
+
+ /* number of es-evi entries whose VTEP list doesn't match
+ * with the ES's
+ */
+ uint32_t incons_evi_vtep_cnt;
+
+ QOBJ_FIELDS
+};
+DECLARE_QOBJ_TYPE(bgp_evpn_es)
+RB_HEAD(bgp_es_rb_head, bgp_evpn_es);
+RB_PROTOTYPE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp);
+
+/* PE attached to an ES */
+struct bgp_evpn_es_vtep {
+ struct bgp_evpn_es *es; /* parent ES */
+ struct in_addr vtep_ip;
+
+ uint32_t flags;
+ /* Rxed a Type4 route from this PE */
+#define BGP_EVPNES_VTEP_ESR (1 << 0)
+ /* Active (rxed EAD-ES and EAD-EVI) and can be included as
+ * a nexthop
+ */
+#define BGP_EVPNES_VTEP_ACTIVE (1 << 1)
+
+ uint32_t evi_cnt; /* es_evis referencing this vtep as an active path */
+
+ /* memory used for adding the entry to es->es_vtep_list */
+ struct listnode es_listnode;
+};
+
+/* ES per-EVI info
+ * - ES-EVIs are maintained per-L2-VNI (vpn->es_evi_rb_tree)
+ * - ES-EVIs are also linked to the parent ES (es->es_evi_list)
+ * - Local ES-EVIs are created by zebra (via config). They are linked to a
+ * per-VNI list (vpn->local_es_evi_list) for quick access
+ * - Remote ES-EVIs are created implicitly when a bgp_evpn_es_evi_vtep
+ * references it.
+ */
+struct bgp_evpn_es_evi {
+ struct bgp_evpn_es *es;
+ struct bgpevpn *vpn;
+
+ /* ES-EVI flags */
+ uint32_t flags;
+/* local ES-EVI, created by zebra */
+#define BGP_EVPNES_EVI_LOCAL (1 << 0)
+/* created via a remote VTEP imported by BGP */
+#define BGP_EVPNES_EVI_REMOTE (1 << 1)
+#define BGP_EVPNES_EVI_INCONS_VTEP_LIST (1 << 2)
+
+ /* memory used for adding the es_evi to es_evi->vpn->es_evi_rb_tree */
+ RB_ENTRY(bgp_evpn_es_evi) rb_node;
+ /* memory used for linking the es_evi to
+ * es_evi->vpn->local_es_evi_list
+ */
+ struct listnode l2vni_listnode;
+ /* memory used for linking the es_evi to
+ * es_evi->es->es_evi_list
+ */
+ struct listnode es_listnode;
+
+ /* list of PEs (bgp_evpn_es_evi_vtep) attached to the ES for this VNI */
+ struct list *es_evi_vtep_list;
+};
+
+/* PE attached to an ES for a VNI. This entry is created when an EAD-per-ES
+ * or EAD-per-EVI Type1 route is imported into the VNI.
+ */
+struct bgp_evpn_es_evi_vtep {
+ struct bgp_evpn_es_evi *es_evi; /* parent ES-EVI */
+ struct in_addr vtep_ip;
+
+ uint32_t flags;
+ /* Rxed an EAD-per-ES route from the PE */
+#define BGP_EVPN_EVI_VTEP_EAD_PER_ES (1 << 0) /* rxed EAD-per-ES */
+ /* Rxed an EAD-per-EVI route from the PE */
+#define BGP_EVPN_EVI_VTEP_EAD_PER_EVI (1 << 1) /* rxed EAD-per-EVI */
+ /* VTEP is active i.e. will result in the creation of an es-vtep */
+#define BGP_EVPN_EVI_VTEP_ACTIVE (1 << 2)
+#define BGP_EVPN_EVI_VTEP_EAD (BGP_EVPN_EVI_VTEP_EAD_PER_ES |\
+ BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+
+ /* memory used for adding the entry to es_evi->es_evi_vtep_list */
+ struct listnode es_evi_listnode;
+ struct bgp_evpn_es_vtep *es_vtep;
+};
+
+/* multihoming information stored in bgp_master */
+#define bgp_mh_info (bm->mh_info)
+struct bgp_evpn_mh_info {
+ /* RB tree of Ethernet segments (used for EVPN-MH) */
+ struct bgp_es_rb_head es_rb_tree;
+ /* List of local ESs */
+ struct list *local_es_list;
+ /* List of ESs with pending/periodic processing */
+ struct list *pend_es_list;
+ /* periodic timer for running background consistency checks */
+ struct thread *t_cons_check;
+
+ /* config knobs for optimizing or interop */
+ /* Generate EAD-EVI routes even if the ES is oper-down. This can be
+ * enabled as an optimization to avoid a storm of updates when an ES
+ * link flaps.
+ */
+ bool ead_evi_adv_for_down_links;
+ /* Enable ES consistency checking */
+ bool consistency_checking;
+};
+
+/****************************************************************************/
+static inline int bgp_evpn_is_es_local(struct bgp_evpn_es *es)
+{
+ return CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL) ? 1 : 0;
+}
+
+extern esi_t *zero_esi;
+static inline bool bgp_evpn_is_esi_valid(esi_t *esi)
+{
+ return !!memcmp(esi, zero_esi, sizeof(esi_t));
+}
+
+static inline esi_t *bgp_evpn_attr_get_esi(struct attr *attr)
+{
+ return attr ? &attr->esi : zero_esi;
+}
+
+static inline bool bgp_evpn_attr_is_sync(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags &
+ (ATTR_ES_PEER_PROXY | ATTR_ES_PEER_ACTIVE)) : false;
+}
+
+static inline uint32_t bgp_evpn_attr_get_sync_seq(struct attr *attr)
+{
+ return attr ? attr->mm_sync_seqnum : 0;
+}
+
+static inline bool bgp_evpn_attr_is_active_on_peer(struct attr *attr)
+{
+ return attr ?
+ !!(attr->es_flags & ATTR_ES_PEER_ACTIVE) : false;
+}
+
+static inline bool bgp_evpn_attr_is_router_on_peer(struct attr *attr)
+{
+ return attr ?
+ !!(attr->es_flags & ATTR_ES_PEER_ROUTER) : false;
+}
+
+static inline bool bgp_evpn_attr_is_proxy(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags & ATTR_ES_PROXY_ADVERT) : false;
+}
+
+static inline bool bgp_evpn_attr_is_local_es(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags & ATTR_ES_IS_LOCAL) : false;
+}
+
+/****************************************************************************/
+extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp,
+ struct bgp_evpn_es *es, afi_t afi, safi_t safi,
+ struct prefix_evpn *evp, struct bgp_path_info *pi,
+ int install);
+int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id);
+int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id);
+extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
+ struct in_addr originator_ip, bool oper_up);
+extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi);
+extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni);
+extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni);
+extern int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
+extern int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
+extern void bgp_evpn_mh_init(void);
+extern void bgp_evpn_mh_finish(void);
+void bgp_evpn_vni_es_init(struct bgpevpn *vpn);
+void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn);
+void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj);
+void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail);
+void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
+ bool uj, bool detail);
+void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail);
+struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi);
+extern bool bgp_evpn_is_esi_local(esi_t *esi);
+
+#endif /* _FRR_BGP_EVPN_MH_H */
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index c7ccf69f05..ca45b198a7 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -34,15 +34,23 @@
* in bits */
#define EVPN_ROUTE_PREFIXLEN (sizeof(struct evpn_addr) * 8)
-/* EVPN route types. */
-typedef enum {
- BGP_EVPN_AD_ROUTE = 1, /* Ethernet Auto-Discovery (A-D) route */
- BGP_EVPN_MAC_IP_ROUTE, /* MAC/IP Advertisement route */
- BGP_EVPN_IMET_ROUTE, /* Inclusive Multicast Ethernet Tag route */
- BGP_EVPN_ES_ROUTE, /* Ethernet Segment route */
- BGP_EVPN_IP_PREFIX_ROUTE, /* IP Prefix route */
-} bgp_evpn_route_type;
+/* EVPN route RD buffer length */
+#define BGP_EVPN_PREFIX_RD_LEN 100
+/* packet sizes for EVPN routes */
+/* Type-1 route should be 25 bytes
+ * RD (8), ESI (10), eth-tag (4), vni (3)
+ */
+#define BGP_EVPN_TYPE1_PSIZE 25
+/* Type-4 route should be either 23 or 35 bytes
+ * RD (8), ESI (10), ip-len (1), ip (4 or 16)
+ */
+#define BGP_EVPN_TYPE4_V4_PSIZE 23
+#define BGP_EVPN_TYPE4_V6_PSIZE 34
+
+RB_HEAD(bgp_es_evi_rb_head, bgp_evpn_es_evi);
+RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node,
+ bgp_es_evi_rb_cmp);
/*
* Hash table of EVIs. Right now, the only type of EVI supported is with
* VxLAN encapsulation, hence each EVI corresponds to a L2 VNI.
@@ -98,46 +106,16 @@ struct bgpevpn {
* this VNI. */
struct bgp_table *route_table;
- QOBJ_FIELDS
-};
-
-DECLARE_QOBJ_TYPE(bgpevpn)
+ /* RB tree of ES-EVIs */
+ struct bgp_es_evi_rb_head es_evi_rb_tree;
-struct evpnes {
-
- /* Ethernet Segment Identifier */
- esi_t esi;
-
- /* es flags */
- uint16_t flags;
-#define EVPNES_LOCAL 0x01
-#define EVPNES_REMOTE 0x02
-
- /*
- * Id for deriving the RD
- * automatically for this ESI
- */
- uint16_t rd_id;
-
- /* RD for this VNI. */
- struct prefix_rd prd;
-
- /* originator ip address */
- struct ipaddr originator_ip;
-
- /* list of VTEPs in the same site */
- struct list *vtep_list;
-
- /*
- * Route table for EVPN routes for
- * this ESI. - type4 routes
- */
- struct bgp_table *route_table;
+ /* List of local ESs */
+ struct list *local_es_evi_list;
QOBJ_FIELDS
};
-DECLARE_QOBJ_TYPE(evpnes)
+DECLARE_QOBJ_TYPE(bgpevpn)
/* Mapping of Import RT to VNIs.
* The Import RTs of all VNIs are maintained in a hash table with each
@@ -330,6 +308,16 @@ static inline void encode_es_rt_extcomm(struct ecommunity_val *eval,
memcpy(&eval->val[2], mac, ETH_ALEN);
}
+static inline void encode_esi_label_extcomm(struct ecommunity_val *eval,
+ bool single_active)
+{
+ memset(eval, 0, sizeof(struct ecommunity_val));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL;
+ if (single_active)
+ eval->val[2] |= (1 << 0);
+}
+
static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
struct ethaddr *rmac)
{
@@ -361,13 +349,15 @@ static inline void encode_mac_mobility_extcomm(int static_mac, uint32_t seq,
}
static inline void encode_na_flag_extcomm(struct ecommunity_val *eval,
- uint8_t na_flag)
+ uint8_t na_flag, bool proxy)
{
memset(eval, 0, sizeof(*eval));
eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ND;
if (na_flag)
eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG;
+ if (proxy)
+ eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG;
}
static inline void ip_prefix_from_type5_prefix(const struct prefix_evpn *evp,
@@ -487,6 +477,44 @@ static inline void build_evpn_type4_prefix(struct prefix_evpn *p,
memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t));
}
+static inline void build_evpn_type1_prefix(struct prefix_evpn *p,
+ uint32_t eth_tag,
+ esi_t *esi,
+ struct in_addr originator_ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_AD_ROUTE;
+ p->prefix.ead_addr.eth_tag = eth_tag;
+ p->prefix.ead_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip;
+ memcpy(&p->prefix.ead_addr.esi, esi, sizeof(esi_t));
+}
+
+static inline void evpn_type1_prefix_global_copy(struct prefix_evpn *global_p,
+ const struct prefix_evpn *vni_p)
+{
+ memcpy(global_p, vni_p, sizeof(*global_p));
+ global_p->prefix.ead_addr.ip.ipa_type = 0;
+ global_p->prefix.ead_addr.ip.ipaddr_v4.s_addr = 0;
+}
+
+/* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+static inline struct prefix_evpn *evpn_type1_prefix_vni_copy(
+ struct prefix_evpn *vni_p,
+ const struct prefix_evpn *global_p,
+ struct in_addr originator_ip)
+{
+ memcpy(vni_p, global_p, sizeof(*vni_p));
+ vni_p->prefix.ead_addr.ip.ipa_type = IPADDR_V4;
+ vni_p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip;
+
+ return vni_p;
+}
+
static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
safi_t safi)
{
@@ -511,11 +539,6 @@ static inline void es_get_system_mac(esi_t *esi,
memcpy(mac, &esi->val[1], ETH_ALEN);
}
-static inline int is_es_local(struct evpnes *es)
-{
- return CHECK_FLAG(es->flags, EVPNES_LOCAL) ? 1 : 0;
-}
-
static inline bool bgp_evpn_is_svi_macip_enabled(struct bgpevpn *vpn)
{
struct bgp *bgp_evpn = NULL;
@@ -526,6 +549,16 @@ static inline bool bgp_evpn_is_svi_macip_enabled(struct bgpevpn *vpn)
vpn->advertise_svi_macip);
}
+static inline bool bgp_evpn_is_path_local(struct bgp *bgp,
+ struct bgp_path_info *pi)
+{
+ return (pi->peer == bgp->peer_self
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC);
+}
+
+extern struct zclient *zclient;
+
extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf,
afi_t afi, safi_t safi,
bool add);
@@ -563,10 +596,18 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
vrf_id_t tenant_vrf_id,
struct in_addr mcast_grp);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
-extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi);
-extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi,
- struct ipaddr *originator_ip);
-extern void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es);
extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
+extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *dest,
+ struct bgp_path_info **pi);
+int vni_list_cmp(void *p1, void *p2);
+extern int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_node *rn);
+extern struct bgp_node *bgp_global_evpn_node_get(
+ struct bgp_table *table, afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp, struct prefix_rd *prd);
+extern struct bgp_node *bgp_global_evpn_node_lookup(
+ struct bgp_table *table, afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp, struct prefix_rd *prd);
#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 1a0e5c0cd3..15ecffdc72 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -33,6 +33,7 @@
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_errors.h"
@@ -345,10 +346,11 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
vty_out(vty, "BGP table version is %" PRIu64 ", local router ID is %s\n",
tbl_ver, inet_ntoa(bgp->router_id));
vty_out(vty,
- "Status codes: s suppressed, d damped, h history, "
- "* valid, > best, i - internal\n");
+ "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n");
vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
vty_out(vty,
+ "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n");
+ vty_out(vty,
"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
@@ -462,47 +464,6 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
json_object_object_add(json, "exportRts", json_export_rtl);
}
-static void display_es(struct vty *vty, struct evpnes *es, json_object *json)
-{
- struct in_addr *vtep;
- char buf[ESI_STR_LEN];
- char buf1[RD_ADDRSTRLEN];
- char buf2[INET6_ADDRSTRLEN];
- struct listnode *node = NULL;
- json_object *json_vteps = NULL;
-
- if (json) {
- json_vteps = json_object_new_array();
- json_object_string_add(json, "esi",
- esi_to_str(&es->esi, buf, sizeof(buf)));
- json_object_string_add(json, "rd",
- prefix_rd2str(&es->prd, buf1,
- sizeof(buf1)));
- json_object_string_add(
- json, "originatorIp",
- ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
- if (es->vtep_list) {
- for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
- json_object_array_add(
- json_vteps, json_object_new_string(
- inet_ntoa(*vtep)));
- }
- json_object_object_add(json, "vteps", json_vteps);
- } else {
- vty_out(vty, "ESI: %s\n",
- esi_to_str(&es->esi, buf, sizeof(buf)));
- vty_out(vty, " RD: %s\n", prefix_rd2str(&es->prd, buf1,
- sizeof(buf1)));
- vty_out(vty, " Originator-IP: %s\n",
- ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
- if (es->vtep_list) {
- vty_out(vty, " VTEP List:\n");
- for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
- vty_out(vty, " %s\n", inet_ntoa(*vtep));
- }
- }
-}
-
static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
{
char buf1[RD_ADDRSTRLEN];
@@ -629,7 +590,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
}
static void show_esi_routes(struct bgp *bgp,
- struct evpnes *es,
+ struct bgp_evpn_es *es,
struct vty *vty,
json_object *json)
{
@@ -680,7 +641,8 @@ static void show_esi_routes(struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path);
+ route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path,
+ false);
if (json)
json_object_array_add(json_paths, json_path);
@@ -789,7 +751,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
json_path);
else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
- json_path);
+ json_path, false);
if (json)
json_object_array_add(json_paths, json_path);
@@ -979,48 +941,6 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
}
}
-static void show_es_entry(struct hash_bucket *bucket, void *args[])
-{
- char buf[ESI_STR_LEN];
- char buf1[RD_ADDRSTRLEN];
- char buf2[INET6_ADDRSTRLEN];
- struct in_addr *vtep = NULL;
- struct vty *vty = args[0];
- json_object *json = args[1];
- json_object *json_vteps = NULL;
- struct listnode *node = NULL;
- struct evpnes *es = (struct evpnes *)bucket->data;
-
- if (json) {
- json_vteps = json_object_new_array();
- json_object_string_add(json, "esi",
- esi_to_str(&es->esi, buf, sizeof(buf)));
- json_object_string_add(json, "type",
- is_es_local(es) ? "Local" : "Remote");
- json_object_string_add(json, "rd",
- prefix_rd2str(&es->prd, buf1,
- sizeof(buf1)));
- json_object_string_add(
- json, "originatorIp",
- ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
- if (es->vtep_list) {
- for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
- json_object_array_add(json_vteps,
- json_object_new_string(
- inet_ntoa(*vtep)));
- }
- json_object_object_add(json, "vteps", json_vteps);
- } else {
- vty_out(vty, "%-30s %-6s %-21s %-15s %-6d\n",
- esi_to_str(&es->esi, buf, sizeof(buf)),
- is_es_local(es) ? "Local" : "Remote",
- prefix_rd2str(&es->prd, buf1, sizeof(buf1)),
- ipaddr2str(&es->originator_ip, buf2,
- sizeof(buf2)),
- es->vtep_list ? listcount(es->vtep_list) : 0);
- }
-}
-
static void show_vni_entry(struct hash_bucket *bucket, void *args[])
{
struct vty *vty;
@@ -1315,7 +1235,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
route_vty_out(vty,
bgp_dest_get_prefix(rm),
pi, no_display, SAFI_EVPN,
- json_array);
+ json_array, false);
no_display = 1;
}
@@ -2454,10 +2374,10 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp,
esi_t *esi, json_object *json)
{
- struct evpnes *es = NULL;
+ struct bgp_evpn_es *es = NULL;
/* locate the ES */
- es = bgp_evpn_lookup_es(bgp, esi);
+ es = bgp_evpn_es_find(esi);
if (!es) {
if (!json)
vty_out(vty, "ESI not found\n");
@@ -2814,7 +2734,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
SAFI_EVPN, json_path);
} else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
- json_path);
+ json_path, false);
if (json)
json_object_array_add(json_paths,
@@ -2863,43 +2783,6 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
}
}
-/* Display specific ES */
-static void evpn_show_es(struct vty *vty, struct bgp *bgp, esi_t *esi,
- json_object *json)
-{
- struct evpnes *es = NULL;
-
- es = bgp_evpn_lookup_es(bgp, esi);
- if (es) {
- display_es(vty, es, json);
- } else {
- if (json) {
- vty_out(vty, "{}\n");
- } else {
- vty_out(vty, "ESI not found\n");
- return;
- }
- }
-}
-
-/* Display all ESs */
-static void evpn_show_all_es(struct vty *vty, struct bgp *bgp,
- json_object *json)
-{
- void *args[2];
-
- if (!json)
- vty_out(vty, "%-30s %-6s %-21s %-15s %-6s\n",
- "ESI", "Type", "RD", "Originator-IP", "#VTEPs");
-
- /* print all ESs */
- args[0] = vty;
- args[1] = json;
- hash_iterate(bgp->esihash,
- (void (*)(struct hash_bucket *, void *))show_es_entry,
- args);
-}
-
/*
* Display specified VNI (vty handler)
*/
@@ -3899,6 +3782,12 @@ DEFPY (bgp_evpn_advertise_pip_ip_mac,
struct listnode *node = NULL;
struct bgpevpn *vpn = NULL;
+ /*
+ * At this point if bgp_evpn is NULL and evpn is enabled
+ * something stupid has gone wrong
+ */
+ assert(bgp_evpn);
+
update_advertise_vrf_routes(bgp_vrf);
/* Update (svi) type-2 routes */
@@ -4016,55 +3905,50 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
return CMD_SUCCESS;
}
-/* Disaply ES */
-DEFUN(show_bgp_l2vpn_evpn_es,
+DEFPY(show_bgp_l2vpn_evpn_es_evi,
+ show_bgp_l2vpn_evpn_es_evi_cmd,
+ "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "ES per EVI\n"
+ "VxLAN Network Identifier\n"
+ "VNI\n"
+ JSON_STR
+ "Detailed information\n")
+{
+ if (vni)
+ bgp_evpn_es_evi_show_vni(vty, vni, !!uj, !!detail);
+ else
+ bgp_evpn_es_evi_show(vty, !!uj, !!detail);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_bgp_l2vpn_evpn_es,
show_bgp_l2vpn_evpn_es_cmd,
- "show bgp l2vpn evpn es [ESI] [json]",
+ "show bgp l2vpn evpn es [NAME$esi_str|detail$detail] [json$uj]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
- "ethernet-Segment\n"
- "Ethernet-Segment Identifier\n"
+ "Ethernet Segment\n"
+ "ES ID\n"
+ "Detailed information\n"
JSON_STR)
{
- int idx = 0;
- bool uj = false;
esi_t esi;
- json_object *json = NULL;
- struct bgp *bgp = NULL;
- memset(&esi, 0, sizeof(esi));
- uj = use_json(argc, argv);
-
- bgp = bgp_get_evpn();
- if (!bgp)
- return CMD_WARNING;
-
- if (!argv_find(argv, argc, "evpn", &idx))
- return CMD_WARNING;
-
- if ((uj && argc == ((idx + 1) + 2)) ||
- (!uj && argc == (idx + 1) + 1)) {
-
- /* show all ESs */
- evpn_show_all_es(vty, bgp, json);
- } else {
-
- /* show a specific ES */
-
- /* get the ESI - ESI-ID is at argv[5] */
- if (!str_to_esi(argv[idx + 2]->arg, &esi)) {
- vty_out(vty, "%% Malformed ESI\n");
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%%Malformed ESI\n");
return CMD_WARNING;
}
- evpn_show_es(vty, bgp, &esi, json);
- }
+ bgp_evpn_es_show_esi(vty, &esi, uj);
+ } else {
- if (uj) {
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
+ bgp_evpn_es_show(vty, uj, !!detail);
}
return CMD_SUCCESS;
@@ -4109,7 +3993,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary,
*/
DEFUN(show_bgp_l2vpn_evpn_route,
show_bgp_l2vpn_evpn_route_cmd,
- "show bgp l2vpn evpn route [detail] [type <macip|2|multicast|3|es|4|prefix|5>] [json]",
+ "show bgp l2vpn evpn route [detail] [type <ead|1|macip|2|multicast|3|es|4|prefix|5>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
@@ -4117,6 +4001,8 @@ DEFUN(show_bgp_l2vpn_evpn_route,
"EVPN route information\n"
"Display Detailed Information\n"
"Specify Route type\n"
+ "EAD (Type-1) route\n"
+ "EAD (Type-1) route\n"
"MAC-IP (Type-2) route\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
@@ -4152,9 +4038,12 @@ DEFUN(show_bgp_l2vpn_evpn_route,
else if ((strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
|| (strmatch(argv[type_idx + 1]->arg, "3")))
type = BGP_EVPN_IMET_ROUTE;
- else if ((strncmp(argv[type_idx + 1]->arg, "e", 1) == 0)
+ else if ((strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
|| (strmatch(argv[type_idx + 1]->arg, "4")))
type = BGP_EVPN_ES_ROUTE;
+ else if ((strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "1")))
+ type = BGP_EVPN_AD_ROUTE;
else if ((strncmp(argv[type_idx + 1]->arg, "p", 1) == 0)
|| (strmatch(argv[type_idx + 1]->arg, "5")))
type = BGP_EVPN_IP_PREFIX_ROUTE;
@@ -4180,7 +4069,7 @@ DEFUN(show_bgp_l2vpn_evpn_route,
*/
DEFUN(show_bgp_l2vpn_evpn_route_rd,
show_bgp_l2vpn_evpn_route_rd_cmd,
- "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|es|prefix>] [json]",
+ "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <ead|macip|multicast|es|prefix>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
@@ -4189,6 +4078,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
"Route Distinguisher\n"
"ASN:XX or A.B.C.D:XX\n"
"Specify Route type\n"
+ "EAD (Type-1) route\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
"Ethernet Segment route\n"
@@ -4230,6 +4120,10 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
+ type = BGP_EVPN_ES_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0)
+ type = BGP_EVPN_AD_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
type = BGP_EVPN_IP_PREFIX_ROUTE;
else
@@ -4374,7 +4268,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_esi,
* Display per-VNI EVPN routing table.
*/
DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
- "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [<type <macip|multicast> | vtep A.B.C.D>] [json]",
+ "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [<type <ead|macip|multicast> | vtep A.B.C.D>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
@@ -4383,6 +4277,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
"VXLAN Network Identifier\n"
"VNI number\n"
"Specify Route type\n"
+ "EAD (Type-1) route\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
"Remote VTEP\n"
@@ -4420,6 +4315,8 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[idx + 5]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[idx + 5]->arg, "ea", 2) == 0)
+ type = BGP_EVPN_AD_ROUTE;
else
return CMD_WARNING;
} else if (strncmp(argv[idx + 4]->arg, "vtep", 4) == 0) {
@@ -4705,17 +4602,22 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt,
return CMD_SUCCESS;
}
-DEFUN(test_adv_evpn_type4_route,
- test_adv_evpn_type4_route_cmd,
- "advertise es ESI",
- "Advertise EVPN ES route\n"
+DEFPY(test_es_add,
+ test_es_add_cmd,
+ "[no$no] test es NAME$esi_str [state NAME$state_str]",
+ NO_STR
+ "Test\n"
"Ethernet-segment\n"
- "Ethernet-Segment Identifier\n")
+ "Ethernet-Segment Identifier\n"
+ "ES link state\n"
+ "up|down\n"
+)
{
int ret = 0;
esi_t esi;
struct bgp *bgp;
- struct ipaddr vtep_ip;
+ struct in_addr vtep_ip;
+ bool oper_up;
bgp = bgp_get_evpn();
if (!bgp) {
@@ -4723,33 +4625,47 @@ DEFUN(test_adv_evpn_type4_route,
return CMD_WARNING;
}
- if (!str_to_esi(argv[2]->arg, &esi)) {
+ if (!str_to_esi(esi_str, &esi)) {
vty_out(vty, "%%Malformed ESI\n");
return CMD_WARNING;
}
- vtep_ip.ipa_type = IPADDR_V4;
- vtep_ip.ipaddr_v4 = bgp->router_id;
+ if (no) {
+ ret = bgp_evpn_local_es_del(bgp, &esi);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to delete ES\n");
+ return CMD_WARNING;
+ }
+ } else {
+ if (state_str && !strcmp(state_str, "up"))
+ oper_up = true;
+ else
+ oper_up = false;
+ vtep_ip = bgp->router_id;
- ret = bgp_evpn_local_es_add(bgp, &esi, &vtep_ip);
- if (ret == -1) {
- vty_out(vty, "%%Failed to EVPN advertise type-4 route\n");
- return CMD_WARNING;
+ ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to add ES\n");
+ return CMD_WARNING;
+ }
}
return CMD_SUCCESS;
}
-DEFUN(test_withdraw_evpn_type4_route,
- test_withdraw_evpn_type4_route_cmd,
- "withdraw es ESI",
- "Advertise EVPN ES route\n"
+DEFPY(test_es_vni_add,
+ test_es_vni_add_cmd,
+ "[no$no] test es NAME$esi_str vni (1-16777215)$vni",
+ NO_STR
+ "Test\n"
"Ethernet-segment\n"
- "Ethernet-Segment Identifier\n")
+ "Ethernet-Segment Identifier\n"
+ "VNI\n"
+ "1-16777215\n"
+)
{
int ret = 0;
esi_t esi;
struct bgp *bgp;
- struct ipaddr vtep_ip;
bgp = bgp_get_evpn();
if (!bgp) {
@@ -4757,22 +4673,23 @@ DEFUN(test_withdraw_evpn_type4_route,
return CMD_WARNING;
}
- if (!bgp->peer_self) {
- vty_out(vty, "%%BGP instance doesn't have self peer\n");
- return CMD_WARNING;
- }
-
- if (!str_to_esi(argv[2]->arg, &esi)) {
+ if (!str_to_esi(esi_str, &esi)) {
vty_out(vty, "%%Malformed ESI\n");
return CMD_WARNING;
}
- vtep_ip.ipa_type = IPADDR_V4;
- vtep_ip.ipaddr_v4 = bgp->router_id;
- ret = bgp_evpn_local_es_del(bgp, &esi, &vtep_ip);
- if (ret == -1) {
- vty_out(vty, "%%Failed to withdraw EVPN type-4 route\n");
- return CMD_WARNING;
+ if (no) {
+ ret = bgp_evpn_local_es_evi_del(bgp, &esi, vni);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to deref ES VNI\n");
+ return CMD_WARNING;
+ }
+ } else {
+ ret = bgp_evpn_local_es_evi_add(bgp, &esi, vni);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to ref ES VNI\n");
+ return CMD_WARNING;
+ }
}
return CMD_SUCCESS;
}
@@ -5830,11 +5747,12 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_pip_ip_mac_cmd);
/* test commands */
- install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd);
- install_element(BGP_EVPN_NODE, &test_withdraw_evpn_type4_route_cmd);
+ install_element(BGP_EVPN_NODE, &test_es_add_cmd);
+ install_element(BGP_EVPN_NODE, &test_es_vni_add_cmd);
/* "show bgp l2vpn evpn" commands. */
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
index 64a6c2ea8f..e309fa948e 100644
--- a/bgpd/bgp_flowspec_vty.c
+++ b/bgpd/bgp_flowspec_vty.c
@@ -376,11 +376,10 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
bpr->priority,
bpr->action->table_id);
}
- if (list_began)
- vty_out(vty, ")");
- vty_out(vty, "\n");
}
- if (!list_began)
+ if (list_began)
+ vty_out(vty, ")\n");
+ else
vty_out(vty, "\tnot installed in PBR\n");
}
}
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index e78682a3bb..ab3b88da7a 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -470,7 +470,6 @@ static int bgp_start_timer(struct thread *thread)
struct peer *peer;
peer = THREAD_ARG(thread);
- peer->t_start = NULL;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [FSM] Timer (start timer expire).", peer->host);
@@ -492,8 +491,6 @@ static int bgp_connect_timer(struct thread *thread)
assert(!peer->t_write);
assert(!peer->t_read);
- peer->t_connect = NULL;
-
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [FSM] Timer (connect timer expire)", peer->host);
@@ -516,7 +513,6 @@ static int bgp_holdtime_timer(struct thread *thread)
struct peer *peer;
peer = THREAD_ARG(thread);
- peer->t_holdtime = NULL;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [FSM] Timer (holdtime timer expire)",
@@ -552,7 +548,6 @@ int bgp_routeadv_timer(struct thread *thread)
struct peer *peer;
peer = THREAD_ARG(thread);
- peer->t_routeadv = NULL;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [FSM] Timer (routeadv timer expire)",
@@ -612,7 +607,6 @@ static int bgp_graceful_restart_timer_expire(struct thread *thread)
safi_t safi;
peer = THREAD_ARG(thread);
- peer->t_gr_restart = NULL;
/* NSF delete stale route */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
@@ -641,7 +635,6 @@ static int bgp_graceful_stale_timer_expire(struct thread *thread)
safi_t safi;
peer = THREAD_ARG(thread);
- peer->t_gr_stale = NULL;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s graceful restart stalepath timer expired",
@@ -674,8 +667,6 @@ static int bgp_graceful_deferral_timer_expire(struct thread *thread)
"afi %d, safi %d : graceful restart deferral timer expired",
afi, safi);
- bgp->gr_info[afi][safi].t_select_deferral = NULL;
-
bgp->gr_info[afi][safi].eor_required = 0;
bgp->gr_info[afi][safi].eor_received = 0;
XFREE(MTYPE_TMP, info);
@@ -1553,8 +1544,7 @@ int bgp_start(struct peer *peer)
if (BGP_PEER_START_SUPPRESSED(peer)) {
if (bgp_debug_neighbor_events(peer))
flog_err(EC_BGP_FSM,
- "%s [FSM] Trying to start suppressed peer"
- " - this is never supposed to happen!",
+ "%s [FSM] Trying to start suppressed peer - this is never supposed to happen!",
peer->host);
if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
@@ -2285,8 +2275,7 @@ int bgp_event_update(struct peer *peer, enum bgp_fsm_events event)
if (!dyn_nbr && !passive_conn && peer->bgp) {
flog_err(
EC_BGP_FSM,
- "%s [FSM] Failure handling event %s in state %s, "
- "prior events %s, %s, fd %d",
+ "%s [FSM] Failure handling event %s in state %s, prior events %s, %s, fd %d",
peer->host, bgp_event_str[peer->cur_event],
lookup_msg(bgp_status_msg, peer->status, NULL),
bgp_event_str[peer->last_event],
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index 4121a8e4b7..fba954c432 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -394,8 +394,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
/* There needs to be at least one label */
if (prefixlen < 24) {
flog_err(EC_BGP_UPDATE_RCV,
- "%s [Error] Update packet error"
- " (wrong label length %d)",
+ "%s [Error] Update packet error (wrong label length %d)",
peer->host, prefixlen);
bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
BGP_NOTIFY_UPDATE_INVAL_NETWORK);
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index ebf2328a71..b082aa9c6a 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -61,16 +61,10 @@
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_errors.h"
-DEFINE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf),
- (vty, vrf))
-
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h"
#endif
-DEFINE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled),
- (vrf, enabled))
-
/* bgpd options, we use GNU getopt library. */
static const struct option longopts[] = {
{"bgp_port", required_argument, NULL, 'p'},
@@ -139,19 +133,20 @@ void sighup(void)
/*
* This is turned off for the moment. There is all
* sorts of config turned off by bgp_terminate
- * that is not setup properly again in bgp_rest.
+ * that is not setup properly again in bgp_reset.
* I see no easy way to do this nor do I see that
* this is a desirable way to reload config
* given the yang work.
*/
/* Terminate all thread. */
- bgp_terminate();
- bgp_reset();
- zlog_info("bgpd restarting!");
-
- /* Reload config file. */
- vty_read_config(NULL, bgpd_di.config_file, config_default);
+ /*
+ * bgp_terminate();
+ * bgp_reset();
+ * zlog_info("bgpd restarting!");
+ * Reload config file.
+ * vty_read_config(NULL, bgpd_di.config_file, config_default);
+ */
/* Try to return to normal operation. */
}
@@ -308,7 +303,6 @@ static int bgp_vrf_enable(struct vrf *vrf)
if (old_vrf_id != bgp->vrf_id)
bgp_redistribute_redo(bgp);
bgp_instance_up(bgp);
- hook_call(bgp_hook_vrf_update, vrf, true);
vpn_leak_zebra_vrf_label_update(bgp, AFI_IP);
vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6);
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP,
@@ -358,37 +352,16 @@ static int bgp_vrf_disable(struct vrf *vrf)
if (old_vrf_id != bgp->vrf_id)
bgp_unset_redist_vrf_bitmaps(bgp, old_vrf_id);
bgp_instance_down(bgp);
- hook_call(bgp_hook_vrf_update, vrf, false);
}
/* Note: This is a callback, the VRF will be deleted by the caller. */
return 0;
}
-static int bgp_vrf_config_write(struct vty *vty)
-{
- struct vrf *vrf;
-
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- if (vrf->vrf_id == VRF_DEFAULT) {
- vty_out(vty, "!\n");
- continue;
- }
- vty_out(vty, "vrf %s\n", vrf->name);
-
- hook_call(bgp_hook_config_write_vrf, vty, vrf);
-
- vty_out(vty, " exit-vrf\n!\n");
- }
-
- return 0;
-}
-
static void bgp_vrf_init(void)
{
vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable,
bgp_vrf_delete, bgp_vrf_enable);
- vrf_cmd_init(bgp_vrf_config_write, &bgpd_privs);
}
static void bgp_vrf_terminate(void)
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 41c4108c0a..8bdab16680 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -116,8 +116,11 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string")
DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
-DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP Ip")
+DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN Multihoming Information")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP")
DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information")
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information")
DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT")
DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index 5428022551..d1ae392c65 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -111,8 +111,11 @@ DECLARE_MTYPE(LCOMMUNITY)
DECLARE_MTYPE(LCOMMUNITY_STR)
DECLARE_MTYPE(LCOMMUNITY_VAL)
+DECLARE_MTYPE(BGP_EVPN_MH_INFO)
DECLARE_MTYPE(BGP_EVPN_ES)
+DECLARE_MTYPE(BGP_EVPN_ES_EVI)
DECLARE_MTYPE(BGP_EVPN_ES_VTEP)
+DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP)
DECLARE_MTYPE(BGP_EVPN)
DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index 5dd1cf6de8..b7f516eaf7 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -739,8 +739,7 @@ void bgp_path_info_mpath_update(struct bgp_dest *dest,
if (debug)
zlog_debug(
- "%pRN: New mpath count (incl newbest) %d mpath-change %s"
- " all_paths_lb %d cum_bw u%" PRIu64,
+ "%pRN: New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw u%" PRIu64,
bgp_dest_to_rnode(dest), mpath_count,
mpath_changed ? "YES" : "NO",
all_paths_lb, cum_bw);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 86b1b3e3ac..314337d194 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -274,8 +274,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
if (bgp->vrf_id == VRF_UNKNOWN) {
if (debug) {
zlog_debug(
- "%s: vrf %s: afi %s: vrf_id not set, "
- "can't set zebra vrf label",
+ "%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label",
__func__, bgp->name_pretty, afi2str(afi));
}
return;
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 00cc1f67a1..06aec9412c 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -501,8 +501,7 @@ static int bgp_accept(struct thread *thread)
if (BGP_PEER_START_SUPPRESSED(peer1)) {
if (bgp_debug_neighbor_events(peer1))
zlog_debug(
- "[Event] Incoming BGP connection rejected from %s "
- "due to maximum-prefix or shutdown",
+ "[Event] Incoming BGP connection rejected from %s due to maximum-prefix or shutdown",
peer1->host);
close(bgp_sock);
return -1;
@@ -518,8 +517,7 @@ static int bgp_accept(struct thread *thread)
*/
if (bgp_debug_neighbor_events(peer1))
zlog_debug(
- "[Event] New active connection from peer %s, Killing"
- " previous active connection",
+ "[Event] New active connection from peer %s, Killing previous active connection",
peer1->host);
peer_delete(peer1->doppelganger);
}
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index aefcaeff3a..a74b5f91ac 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -146,6 +146,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
afi = BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr) ? AFI_IP6
: AFI_IP;
+ /* Validation for the ipv4 mapped ipv6 nexthop. */
+ if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
+ afi = AFI_IP;
+ }
+
/* This will return true if the global IPv6 NH is a link local
* addr */
if (make_prefix(afi, pi, &p) < 0)
@@ -533,6 +538,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
: 0;
struct bgp_dest *net = pi->net;
const struct prefix *p_orig = bgp_dest_get_prefix(net);
+ struct in_addr ipv4;
if (p_orig->family == AF_FLOWSPEC) {
if (!pi->peer)
@@ -548,8 +554,15 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
p->u.prefix4 = p_orig->u.prefix4;
p->prefixlen = p_orig->prefixlen;
} else {
- p->u.prefix4 = pi->attr->nexthop;
- p->prefixlen = IPV4_MAX_BITLEN;
+ if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
+ ipv4_mapped_ipv6_to_ipv4(
+ &pi->attr->mp_nexthop_global, &ipv4);
+ p->u.prefix4 = ipv4;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ } else {
+ p->u.prefix4 = pi->attr->nexthop;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ }
}
break;
case AFI_IP6:
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 4a2f7d5882..732c8e6753 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -332,8 +332,7 @@ static int bgp_capability_orf_entry(struct peer *peer,
/* Convert AFI, SAFI to internal values, check. */
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
zlog_info(
- "%s Addr-family %d/%d not supported."
- " Ignoring the ORF capability",
+ "%s Addr-family %d/%d not supported. Ignoring the ORF capability",
peer->host, pkt_afi, pkt_safi);
return 0;
}
@@ -344,8 +343,7 @@ static int bgp_capability_orf_entry(struct peer *peer,
/* validate number field */
if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) {
zlog_info(
- "%s ORF Capability entry length error,"
- " Cap length %u, num %u",
+ "%s ORF Capability entry length error, Cap length %u, num %u",
peer->host, hdr->length, num);
bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_MALFORMED_ATTR);
@@ -407,8 +405,7 @@ static int bgp_capability_orf_entry(struct peer *peer,
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s OPEN has %s ORF capability"
- " as %s for afi/safi: %s/%s",
+ "%s OPEN has %s ORF capability as %s for afi/safi: %s/%s",
peer->host,
lookup_msg(orf_type_str, type, NULL),
lookup_msg(orf_mode_str, mode, NULL),
@@ -490,15 +487,13 @@ static int bgp_capability_restart(struct peer *peer,
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Addr-family %s/%s(afi/safi) not supported."
- " Ignore the Graceful Restart capability for this AFI/SAFI",
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Graceful Restart capability for this AFI/SAFI",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
} else if (!peer->afc[afi][safi]) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Addr-family %s/%s(afi/safi) not enabled."
- " Ignore the Graceful Restart capability",
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Graceful Restart capability",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
} else {
@@ -581,16 +576,14 @@ static int bgp_capability_addpath(struct peer *peer,
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Addr-family %s/%s(afi/safi) not supported."
- " Ignore the Addpath Attribute for this AFI/SAFI",
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
continue;
} else if (!peer->afc[afi][safi]) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Addr-family %s/%s(afi/safi) not enabled."
- " Ignore the AddPath capability for this AFI/SAFI",
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
continue;
@@ -640,8 +633,7 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr)
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Addr-family %s/%s(afi/safi) not supported."
- " Ignore the ENHE Attribute for this AFI/SAFI",
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the ENHE Attribute for this AFI/SAFI",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
continue;
@@ -662,8 +654,7 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr)
|| safi == SAFI_LABELED_UNICAST)) {
flog_warn(
EC_BGP_CAPABILITY_INVALID_DATA,
- "%s Unexpected afi/safi/next-hop afi: %s/%s/%u "
- "in Extended Next-hop capability, ignoring",
+ "%s Unexpected afi/safi/next-hop afi: %s/%s/%u in Extended Next-hop capability, ignoring",
peer->host, iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi), pkt_nh_afi);
continue;
@@ -875,8 +866,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
- "%s %s Capability length error: got %u,"
- " expected at least %u",
+ "%s %s Capability length error: got %u, expected at least %u",
peer->host,
lookup_msg(capcode_str, caphdr.code,
NULL),
@@ -889,8 +879,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
if (caphdr.length
&& caphdr.length % cap_modsizes[caphdr.code] != 0) {
zlog_info(
- "%s %s Capability length error: got %u,"
- " expected a multiple of %u",
+ "%s %s Capability length error: got %u, expected a multiple of %u",
peer->host,
lookup_msg(capcode_str, caphdr.code,
NULL),
@@ -1036,8 +1025,7 @@ as_t peek_for_as4_capability(struct peer *peer, uint8_t length)
if (BGP_DEBUG(as4, AS4))
zlog_debug(
- "%s [AS4] rcv OPEN w/ OPTION parameter len: %u,"
- " peeking for as4",
+ "%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.
@@ -1211,8 +1199,7 @@ int bgp_open_option_parse(struct peer *peer, uint8_t length, int *mp_capability)
&& !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
&& !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) {
flog_err(EC_BGP_PKT_OPEN,
- "%s [Error] Configured AFI/SAFIs do not "
- "overlap with received MP capabilities",
+ "%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities",
peer->host);
if (error != error_data)
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 29c03f4014..701b9f446f 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -408,6 +408,9 @@ int bgp_generate_updgrp_packets(struct thread *thread)
if (peer->bgp->main_peers_update_hold)
return 0;
+ if (peer->t_routeadv)
+ return 0;
+
do {
s = NULL;
FOREACH_AFI_SAFI (afi, safi) {
@@ -1110,8 +1113,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
/* Receive OPEN message log */
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s rcv OPEN, version %d, remote-as (in open) %u,"
- " holdtime %d, id %s",
+ "%s rcv OPEN, version %d, remote-as (in open) %u, holdtime %d, id %s",
peer->host, version, remote_as, holdtime,
inet_ntoa(remote_id));
@@ -1177,13 +1179,11 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
if (!as4 && BGP_DEBUG(as4, AS4))
zlog_debug(
- "%s [AS4] OPEN remote_as is AS_TRANS, but no AS4."
- " Odd, but proceeding.",
+ "%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.",
+ "%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;
@@ -1197,8 +1197,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
/* raise error, log this, close session */
flog_err(
EC_BGP_PKT_OPEN,
- "%s bad OPEN, got AS4 capability, but remote_as %u"
- " mismatch with 16bit 'myasn' %u in open",
+ "%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_with_data(peer, BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_BAD_PEER_AS,
@@ -1215,7 +1214,6 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size)
* "Bad BGP Identifier".
*/
if (remote_id.s_addr == INADDR_ANY
- || IPV4_CLASS_DE(ntohl(remote_id.s_addr))
|| (peer->sort == BGP_PEER_IBGP
&& ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr))) {
if (bgp_debug_neighbor_events(peer))
@@ -1493,8 +1491,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
Subcode is set to Malformed Attribute List. */
if (stream_pnt(s) + 2 > end) {
flog_err(EC_BGP_UPDATE_RCV,
- "%s [Error] Update packet error"
- " (packet length is short for unfeasible length)",
+ "%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);
@@ -1507,8 +1504,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
/* Unfeasible Route Length check. */
if (stream_pnt(s) + withdraw_len > end) {
flog_err(EC_BGP_UPDATE_RCV,
- "%s [Error] Update packet error"
- " (packet unfeasible length overflow %d)",
+ "%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);
@@ -1577,7 +1573,8 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW
|| BGP_DEBUG(update, UPDATE_IN)
|| BGP_DEBUG(update, UPDATE_PREFIX)) {
- ret = bgp_dump_attr(&attr, peer->rcvd_attr_str, BUFSIZ);
+ ret = bgp_dump_attr(&attr, peer->rcvd_attr_str,
+ sizeof(peer->rcvd_attr_str));
peer->stat_upd_7606++;
@@ -2063,8 +2060,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
if (!ok || (ok && ret != CMD_SUCCESS)) {
zlog_info(
- "%s Received misformatted prefixlist ORF."
- " Remove All pfxlist",
+ "%s Received misformatted prefixlist ORF. Remove All pfxlist",
peer->host);
prefix_bgp_orf_remove_all(afi,
name);
@@ -2194,8 +2190,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
&safi)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "%s Dynamic Capability MP_EXT afi/safi invalid "
- "(%s/%s)",
+ "%s Dynamic Capability MP_EXT afi/safi invalid (%s/%s)",
peer->host,
iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
index 535a45690b..7c3e8cd70e 100644
--- a/bgpd/bgp_pbr.c
+++ b/bgpd/bgp_pbr.c
@@ -486,8 +486,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
*/
if (api->match_protocol_num > 1) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match protocol operations:"
- "multiple protocols ( %d). ignoring.",
+ zlog_debug("BGP: match protocol operations:multiple protocols ( %d). ignoring.",
api->match_protocol_num);
return 0;
}
@@ -496,21 +495,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
api->protocol[0].value != PROTOCOL_ICMP &&
api->protocol[0].value != PROTOCOL_TCP) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match protocol operations:"
- "protocol (%d) not supported. ignoring",
+ zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring",
api->match_protocol_num);
return 0;
}
if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match src port operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match src port operations:too complex. ignoring.");
return 0;
}
if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match dst port operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match dst port operations:too complex. ignoring.");
return 0;
}
if (!bgp_pbr_extract_enumerate(api->tcpflags,
@@ -519,8 +515,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
OPERATOR_UNARY_OR, NULL,
FLOWSPEC_TCP_FLAGS)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match tcp flags:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match tcp flags:too complex. ignoring.");
return 0;
}
if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) {
@@ -529,8 +524,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
OPERATOR_UNARY_OR, NULL,
FLOWSPEC_ICMP_TYPE)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match icmp type operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match icmp type operations:too complex. ignoring.");
return 0;
}
enumerate_icmp = true;
@@ -541,22 +535,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
OPERATOR_UNARY_OR, NULL,
FLOWSPEC_ICMP_CODE)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match icmp code operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match icmp code operations:too complex. ignoring.");
return 0;
} else if (api->match_icmp_type_num > 1 &&
!enumerate_icmp) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match icmp code is enumerate"
- ", and icmp type is not."
- " too complex. ignoring.");
+ zlog_debug("BGP: match icmp code is enumerate, and icmp type is not. too complex. ignoring.");
return 0;
}
}
if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match port operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match port operations:too complex. ignoring.");
return 0;
}
if (api->match_packet_length_num) {
@@ -572,8 +562,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
NULL, FLOWSPEC_PKT_LEN);
if (!ret) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match packet length operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match packet length operations:too complex. ignoring.");
return 0;
}
}
@@ -582,8 +571,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
NULL, FLOWSPEC_DSCP)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match DSCP operations:"
- "too complex. ignoring.");
+ zlog_debug("BGP: match DSCP operations:too complex. ignoring.");
return 0;
}
}
@@ -629,16 +617,14 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
if (api->match_src_port_num + api->match_dst_port_num +
api->match_port_num > 3) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match multiple port operations:"
- " too complex. ignoring.");
+ zlog_debug("BGP: match multiple port operations: too complex. ignoring.");
return 0;
}
if ((api->match_src_port_num || api->match_dst_port_num
|| api->match_port_num) && (api->match_icmp_type_num
|| api->match_icmp_code_num)) {
if (BGP_DEBUG(pbr, PBR))
- zlog_debug("BGP: match multiple port/imcp operations:"
- " too complex. ignoring.");
+ zlog_debug("BGP: match multiple port/imcp operations: too complex. ignoring.");
return 0;
}
/* iprule only supports redirect IP */
@@ -650,24 +636,21 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
api->actions[i].u.r.rate == 0) {
if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api);
- zlog_debug("BGP: iprule match actions"
- " drop not supported");
+ zlog_debug("BGP: iprule match actions drop not supported");
}
return 0;
}
if (api->actions[i].action == ACTION_MARKING) {
if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api);
- zlog_warn("PBR: iprule set DSCP %u"
- " not supported",
+ zlog_warn("PBR: iprule set DSCP %u not supported",
api->actions[i].u.marking_dscp);
}
}
if (api->actions[i].action == ACTION_REDIRECT) {
if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api);
- zlog_warn("PBR: iprule redirect VRF %u"
- " not supported",
+ zlog_warn("PBR: iprule redirect VRF %u not supported",
api->actions[i].u.redirect_vrf);
}
}
@@ -677,9 +660,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
!(api->match_bitmask & PREFIX_DST_PRESENT)) {
if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api);
- zlog_debug("BGP: match actions without src"
- " or dst address can not operate."
- " ignoring.");
+ zlog_debug("BGP: match actions without src or dst address can not operate. ignoring.");
}
return 0;
}
@@ -845,8 +826,7 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
if (valid_prefix && afi != family2afi(dst->family)) {
if (BGP_DEBUG(pbr, PBR)) {
bgp_pbr_print_policy_route(api);
- zlog_debug("%s: inconsistency:"
- " no match for afi src and dst (%u/%u)",
+ zlog_debug("%s: inconsistency: no match for afi src and dst (%u/%u)",
__func__, afi, family2afi(dst->family));
}
return -1;
@@ -2097,8 +2077,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
listnode_lookup_nocheck(extra->bgp_fs_iprule,
bpr)) {
if (BGP_DEBUG(pbr, PBR_ERROR))
- zlog_err("%s: entry %p/%p already "
- "installed in bgp pbr iprule",
+ zlog_err("%s: entry %p/%p already installed in bgp pbr iprule",
__func__, path, bpr);
return;
}
diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c
index e7f1108057..6ba56c7011 100644
--- a/bgpd/bgp_rd.c
+++ b/bgpd/bgp_rd.c
@@ -174,15 +174,15 @@ char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size)
if (type == RD_TYPE_AS) {
decode_rd_as(pnt + 2, &rd_as);
- snprintf(buf, size, "%u:%" PRIu32, rd_as.as, rd_as.val);
+ snprintf(buf, size, "%u:%u", 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:%" PRIu32, rd_as.as, rd_as.val);
+ snprintf(buf, size, "%u:%u", 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:%" PRIu16, inet_ntoa(rd_ip.ip),
+ snprintf(buf, size, "%s:%hu", inet_ntoa(rd_ip.ip),
rd_ip.val);
return buf;
}
diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h
index b5ad9d624d..2aee44c721 100644
--- a/bgpd/bgp_rd.h
+++ b/bgpd/bgp_rd.h
@@ -33,6 +33,7 @@
#endif
#define RD_ADDRSTRLEN 28
+#define RD_BYTES 8
struct rd_as {
uint16_t type;
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 19e398fc88..3a627b4486 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -79,6 +79,7 @@
#include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_encap_tlv.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_flowspec_util.h"
@@ -544,6 +545,11 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
uint32_t new_mm_seq;
uint32_t exist_mm_seq;
int nh_cmp;
+ esi_t *exist_esi;
+ esi_t *new_esi;
+ bool same_esi;
+ bool old_proxy;
+ bool new_proxy;
*paths_eq = 0;
@@ -620,6 +626,47 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
}
+ new_esi = bgp_evpn_attr_get_esi(newattr);
+ exist_esi = bgp_evpn_attr_get_esi(existattr);
+ if (bgp_evpn_is_esi_valid(new_esi) &&
+ !memcmp(new_esi, exist_esi, sizeof(esi_t))) {
+ same_esi = true;
+ } else {
+ same_esi = false;
+ }
+
+ /* If both paths have the same non-zero ES and
+ * one path is local it wins.
+ * PS: Note the local path wins even if the remote
+ * has the higher MM seq. The local path's
+ * MM seq will be fixed up to match the highest
+ * rem seq, subsequently.
+ */
+ if (same_esi) {
+ char esi_buf[ESI_STR_LEN];
+
+ if (bgp_evpn_is_path_local(bgp, new)) {
+ *reason = bgp_path_selection_evpn_local_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s as ES %s is same and local",
+ pfx_buf, new_buf, exist_buf,
+ esi_to_str(new_esi, esi_buf,
+ sizeof(esi_buf)));
+ return 1;
+ }
+ if (bgp_evpn_is_path_local(bgp, exist)) {
+ *reason = bgp_path_selection_evpn_local_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s as ES %s is same and local",
+ pfx_buf, new_buf, exist_buf,
+ esi_to_str(new_esi, esi_buf,
+ sizeof(esi_buf)));
+ return 0;
+ }
+ }
+
new_mm_seq = mac_mobility_seqnum(newattr);
exist_mm_seq = mac_mobility_seqnum(existattr);
@@ -643,6 +690,30 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
return 0;
}
+ /* if the sequence numbers and ESI are the same and one path
+ * is non-proxy it wins (over proxy)
+ */
+ new_proxy = bgp_evpn_attr_is_proxy(newattr);
+ old_proxy = bgp_evpn_attr_is_proxy(existattr);
+ if (same_esi && bgp_evpn_attr_is_local_es(newattr) &&
+ old_proxy != new_proxy) {
+ if (!new_proxy) {
+ *reason = bgp_path_selection_evpn_non_proxy;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s, same seq/es and non-proxy",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ *reason = bgp_path_selection_evpn_non_proxy;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s, same seq/es and non-proxy",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
/*
* if sequence numbers are the same path with the lowest IP
* wins
@@ -1175,6 +1246,17 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
return 1;
}
+
+int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, int *paths_eq)
+{
+ enum bgp_path_selection_reason reason;
+ char pfx_buf[PREFIX2STR_BUFFER];
+
+ return bgp_path_info_cmp(bgp, new, exist, paths_eq, NULL, 0, pfx_buf,
+ AFI_L2VPN, SAFI_EVPN, &reason);
+}
+
/* 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
@@ -1644,8 +1726,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
mpls_label_t label = bgp_adv_label(dest, pi, peer, afi, safi);
if (!bgp_is_valid_label(&label)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s/%d is filtered - no label (%p)",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s/%d is filtered - no label (%p)",
subgrp->update_group->id, subgrp->id,
inet_ntop(p->family, &p->u.prefix,
buf, SU_ADDRSTRLEN),
@@ -1690,8 +1771,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
&& (IPV4_ADDR_SAME(&onlypeer->remote_id, &piattr->originator_id))) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
- "%s [Update:SEND] %s originator-id is same as "
- "remote router-id",
+ "%s [Update:SEND] %s originator-id is same as remote router-id",
onlypeer->host,
prefix2str(p, buf, sizeof(buf)));
return false;
@@ -1729,8 +1809,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
&& aspath_loop_check(piattr->aspath, onlypeer->as)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
- "%s [Update:SEND] suppress announcement to peer AS %u "
- "that is part of AS path.",
+ "%s [Update:SEND] suppress announcement to peer AS %u that is part of AS path.",
onlypeer->host, onlypeer->as);
return false;
}
@@ -1740,8 +1819,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
if (aspath_loop_check(piattr->aspath, bgp->confed_id)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
- "%s [Update:SEND] suppress announcement to peer AS %u"
- " is AS path.",
+ "%s [Update:SEND] suppress announcement to peer AS %u is AS path.",
peer->host, bgp->confed_id);
return false;
}
@@ -2978,26 +3056,52 @@ static int bgp_maximum_prefix_restart_timer(struct thread *thread)
return 0;
}
+static uint32_t bgp_filtered_routes_count(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ uint32_t count = 0;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct bgp_table *table = peer->bgp->rib[afi][safi];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ const struct prefix *rn_p = bgp_dest_get_prefix(dest);
+ struct attr attr = {};
+
+ if (bgp_input_filter(peer, rn_p, &attr, afi, safi)
+ == FILTER_DENY)
+ count++;
+ }
+ }
+
+ return count;
+}
+
bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
int always)
{
iana_afi_t pkt_afi;
iana_safi_t pkt_safi;
+ uint32_t pcount = (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ ? bgp_filtered_routes_count(peer, afi, safi)
+ + peer->pcount[afi][safi]
+ : peer->pcount[afi][safi];
if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
return false;
- if (peer->pcount[afi][safi] > peer->pmax[afi][safi]) {
+ if (pcount > peer->pmax[afi][safi]) {
if (CHECK_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_LIMIT)
&& !always)
return false;
zlog_info(
- "%%MAXPFXEXCEED: No. of %s prefix received from %s %" PRIu32
- " exceed, limit %" PRIu32,
- get_afi_safi_str(afi, safi, false), peer->host,
- peer->pcount[afi][safi], peer->pmax[afi][safi]);
+ "%%MAXPFXEXCEED: No. of %s prefix received from %s %u exceed, limit %u",
+ get_afi_safi_str(afi, safi, false), peer->host, pcount,
+ peer->pmax[afi][safi]);
SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
if (CHECK_FLAG(peer->af_flags[afi][safi],
@@ -3048,18 +3152,16 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
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 (pcount > (pcount * peer->pmax_threshold[afi][safi] / 100)) {
if (CHECK_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_THRESHOLD)
&& !always)
return false;
zlog_info(
- "%%MAXPFX: No. of %s prefix received from %s reaches %" PRIu32
- ", max %" PRIu32,
- get_afi_safi_str(afi, safi, false), peer->host,
- peer->pcount[afi][safi], peer->pmax[afi][safi]);
+ "%%MAXPFX: No. of %s prefix received from %s reaches %u, max %u",
+ get_afi_safi_str(afi, safi, false), peer->host, pcount,
+ peer->pmax[afi][safi]);
SET_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_THRESHOLD);
} else
@@ -3178,19 +3280,10 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance,
}
static void overlay_index_update(struct attr *attr,
- struct eth_segment_id *eth_s_id,
union gw_addr *gw_ip)
{
if (!attr)
return;
-
- if (eth_s_id == NULL) {
- memset(&(attr->evpn_overlay.eth_s_id), 0,
- sizeof(struct eth_segment_id));
- } else {
- memcpy(&(attr->evpn_overlay.eth_s_id), eth_s_id,
- sizeof(struct eth_segment_id));
- }
if (gw_ip == NULL) {
memset(&(attr->evpn_overlay.gw_ip), 0, sizeof(union gw_addr));
} else {
@@ -3200,20 +3293,17 @@ static void overlay_index_update(struct attr *attr,
}
static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path,
- struct eth_segment_id *eth_s_id,
union gw_addr *gw_ip)
{
- struct eth_segment_id *path_eth_s_id, *path_eth_s_id_remote;
union gw_addr *path_gw_ip, *path_gw_ip_remote;
union {
- struct eth_segment_id esi;
+ esi_t esi;
union gw_addr ip;
} temp;
if (afi != AFI_L2VPN)
return true;
- path_eth_s_id = &(path->attr->evpn_overlay.eth_s_id);
path_gw_ip = &(path->attr->evpn_overlay.gw_ip);
if (gw_ip == NULL) {
@@ -3222,17 +3312,7 @@ static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path,
} else
path_gw_ip_remote = gw_ip;
- if (eth_s_id == NULL) {
- memset(&temp, 0, sizeof(temp));
- path_eth_s_id_remote = &temp.esi;
- } else
- path_eth_s_id_remote = eth_s_id;
-
- if (!memcmp(path_gw_ip, path_gw_ip_remote, sizeof(union gw_addr)))
- return false;
-
- return !memcmp(path_eth_s_id, path_eth_s_id_remote,
- sizeof(struct eth_segment_id));
+ return !!memcmp(path_gw_ip, path_gw_ip_remote, sizeof(union gw_addr));
}
/* Check if received nexthop is valid or not. */
@@ -3512,6 +3592,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
attr_new = bgp_attr_intern(&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;
+
/* If the update is implicit withdraw. */
if (pi) {
pi->uptime = bgp_clock();
@@ -3527,7 +3613,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
num_labels * sizeof(mpls_label_t))
== 0)
&& (overlay_index_equal(
- afi, pi, evpn == NULL ? NULL : &evpn->eth_s_id,
+ afi, pi,
evpn == NULL ? NULL : &evpn->gw_ip))) {
if (CHECK_FLAG(bgp->af_flags[afi][safi],
BGP_CONFIG_DAMPENING)
@@ -3752,7 +3838,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Update Overlay Index */
if (afi == AFI_L2VPN) {
overlay_index_update(
- pi->attr, evpn == NULL ? NULL : &evpn->eth_s_id,
+ pi->attr,
evpn == NULL ? NULL : &evpn->gw_ip);
}
@@ -3797,13 +3883,9 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
BGP_PATH_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",
- __func__, buf1);
+ zlog_debug("%s(%pI4): NH unresolved",
+ __func__,
+ (in_addr_t *)&attr_new->nexthop);
}
bgp_path_info_unset_flag(dest, pi,
BGP_PATH_VALID);
@@ -3922,7 +4004,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Update Overlay Index */
if (afi == AFI_L2VPN) {
overlay_index_update(new->attr,
- evpn == NULL ? NULL : &evpn->eth_s_id,
evpn == NULL ? NULL : &evpn->gw_ip);
}
/* Nexthop reachability check. */
@@ -3987,11 +4068,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
}
#endif
- /* If maximum prefix count is configured and current prefix
- count exeed it. */
- if (bgp_maximum_prefix_overflow(peer, afi, safi, 0))
- return -1;
-
/* If this is an EVPN route, process for import. */
if (safi == SAFI_EVPN && CHECK_FLAG(new->flags, BGP_PATH_VALID))
bgp_evpn_import_route(bgp, afi, safi, p, new);
@@ -5311,7 +5387,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
else if (bgp_static->gatewayIp.family == AF_INET6)
memcpy(&(add.ipv6), &(bgp_static->gatewayIp.u.prefix6),
sizeof(struct in6_addr));
- overlay_index_update(&attr, bgp_static->eth_s_id, &add);
+ memcpy(&attr.esi, bgp_static->eth_s_id, sizeof(esi_t));
if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) {
struct bgp_encap_type_vxlan bet;
memset(&bet, 0, sizeof(struct bgp_encap_type_vxlan));
@@ -5362,7 +5438,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
if (pi) {
memset(&add, 0, sizeof(union gw_addr));
if (attrhash_cmp(pi->attr, attr_new)
- && overlay_index_equal(afi, pi, bgp_static->eth_s_id, &add)
+ && overlay_index_equal(afi, pi, &add)
&& !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
bgp_dest_unlock_node(dest);
bgp_attr_unintern(&attr_new);
@@ -5866,7 +5942,7 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
if (esi) {
bgp_static->eth_s_id =
XCALLOC(MTYPE_ATTR,
- sizeof(struct eth_segment_id));
+ sizeof(esi_t));
str2esi(esi, bgp_static->eth_s_id);
}
if (routermac) {
@@ -7417,7 +7493,7 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,
/* Static function to display route. */
static void route_vty_out_route(const struct prefix *p, struct vty *vty,
- json_object *json)
+ json_object *json, bool wide)
{
int len = 0;
char buf[BUFSIZ];
@@ -7472,7 +7548,7 @@ static void route_vty_out_route(const struct prefix *p, struct vty *vty,
}
if (!json) {
- len = 17 - len;
+ len = wide ? (45 - len) : (17 - len);
if (len < 1)
vty_out(vty, "\n%*s", 20, " ");
else
@@ -7575,7 +7651,7 @@ static char *bgp_nexthop_hostname(struct peer *peer,
/* called from terminal list command */
void route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display, safi_t safi,
- json_object *json_paths)
+ json_object *json_paths, bool wide)
{
int len;
struct attr *attr = path->attr;
@@ -7592,6 +7668,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
const char *nexthop_vrfname = VRF_DEFAULT_NAME;
char *nexthop_hostname =
bgp_nexthop_hostname(path->peer, path->nexthop);
+ char esi_buf[ESI_STR_LEN];
if (json_paths)
json_path = json_object_new_object();
@@ -7602,11 +7679,11 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (!json_paths) {
/* print prefix and mask */
if (!display)
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, wide);
else
- vty_out(vty, "%*s", 17, " ");
+ vty_out(vty, "%*s", (wide ? 45 : 17), " ");
} else {
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, wide);
}
/*
@@ -7696,7 +7773,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
else
len = vty_out(vty, "%s%s", nexthop, vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7726,7 +7803,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
len = vty_out(vty, "%pI4%s", &attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7762,7 +7839,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7793,7 +7870,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
len = vty_out(vty, "%pI4%s", &attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7866,10 +7943,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (path->peer->conf_if) {
len = vty_out(vty, "%s",
path->peer->conf_if);
- len = 16 - len; /* len of IPv6
- addr + max
- len of def
- ifname */
+ /* len of IPv6 addr + max len of def
+ * ifname */
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7888,7 +7964,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->mp_nexthop_local,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7906,7 +7982,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->mp_nexthop_global,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7920,10 +7996,16 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
if (json_paths)
json_object_int_add(json_path, "metric", attr->med);
+ else if (wide)
+ vty_out(vty, "%7u", attr->med);
else
vty_out(vty, "%10u", attr->med);
- else if (!json_paths)
- vty_out(vty, " ");
+ else if (!json_paths) {
+ if (wide)
+ vty_out(vty, "%*s", 7, " ");
+ else
+ vty_out(vty, "%*s", 10, " ");
+ }
/* Local Pref */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
@@ -7964,6 +8046,11 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
if (json_paths) {
+ if (bgp_evpn_is_esi_valid(&attr->esi)) {
+ json_object_string_add(json_path, "esi",
+ esi_to_str(&attr->esi,
+ esi_buf, sizeof(esi_buf)));
+ }
if (safi == SAFI_EVPN &&
attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
json_ext_community = json_object_new_object();
@@ -8009,10 +8096,18 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
} else {
vty_out(vty, "\n");
- if (safi == SAFI_EVPN &&
- attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
- vty_out(vty, "%*s", 20, " ");
- vty_out(vty, "%s\n", attr->ecommunity->str);
+ if (safi == SAFI_EVPN) {
+ if (bgp_evpn_is_esi_valid(&attr->esi)) {
+ vty_out(vty, "%*s", 20, " ");
+ vty_out(vty, "ESI:%s\n",
+ esi_to_str(&attr->esi,
+ esi_buf, sizeof(esi_buf)));
+ }
+ if (attr->flag &
+ ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
+ vty_out(vty, "%*s", 20, " ");
+ vty_out(vty, "%s\n", attr->ecommunity->str);
+ }
}
#ifdef ENABLE_BGP_VNC
@@ -8027,7 +8122,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
/* called from terminal list command */
void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
struct attr *attr, safi_t safi, bool use_json,
- json_object *json_ar)
+ json_object *json_ar, bool wide)
{
json_object *json_status = NULL;
json_object *json_net = NULL;
@@ -8059,7 +8154,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
json_object_string_add(json_net, "network", buff);
}
} else
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, wide);
/* Print attribute */
if (attr) {
@@ -8120,6 +8215,9 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
vty_out(vty, "%-16s",
inet_ntoa(
attr->mp_nexthop_global_in));
+ else if (wide)
+ vty_out(vty, "%-41s",
+ inet_ntoa(attr->nexthop));
else
vty_out(vty, "%-16s",
inet_ntoa(attr->nexthop));
@@ -8132,7 +8230,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
inet_ntop(AF_INET6,
&attr->mp_nexthop_global, buf,
BUFSIZ));
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -8140,7 +8238,12 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
}
if (attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
- vty_out(vty, "%10u", attr->med);
+ if (wide)
+ vty_out(vty, "%7u", attr->med);
+ else
+ vty_out(vty, "%10u", attr->med);
+ else if (wide)
+ vty_out(vty, " ");
else
vty_out(vty, " ");
@@ -8191,7 +8294,7 @@ void route_vty_out_tag(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (json == NULL) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -8288,7 +8391,7 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!display)
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, false);
else
vty_out(vty, "%*s", 17, " ");
@@ -8337,15 +8440,6 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
}
}
- char *str = esi2str(&(attr->evpn_overlay.eth_s_id));
-
- if (!json_path)
- vty_out(vty, "%s", str);
- else
- json_object_string_add(json_overlay, "esi", str);
-
- XFREE(MTYPE_TMP, str);
-
if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) {
inet_ntop(AF_INET, &(attr->evpn_overlay.gw_ip.ipv4), buf,
BUFSIZ);
@@ -8403,7 +8497,7 @@ static void damp_route_vty_out(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!use_json) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -8474,7 +8568,7 @@ static void flap_route_vty_out(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!use_json) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -8629,6 +8723,10 @@ static const char *bgp_path_selection_reason2str(
return "EVPN sequence number";
case bgp_path_selection_evpn_lower_ip:
return "EVPN lower IP";
+ case bgp_path_selection_evpn_local_path:
+ return "EVPN local ES path";
+ case bgp_path_selection_evpn_non_proxy:
+ return "EVPN non proxy";
case bgp_path_selection_weight:
return "Weight";
case bgp_path_selection_local_pref:
@@ -8667,9 +8765,67 @@ static const char *bgp_path_selection_reason2str(
return "Invalid (internal error)";
}
-void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
- struct bgp_path_info *path, afi_t afi, safi_t safi,
- json_object *json_paths)
+static void route_vty_out_detail_es_info(struct vty *vty,
+ struct attr *attr, json_object *json_path)
+{
+ char esi_buf[ESI_STR_LEN];
+ bool es_local = !!CHECK_FLAG(attr->es_flags, ATTR_ES_IS_LOCAL);
+ bool peer_router = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_ROUTER);
+ bool peer_active = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_ACTIVE);
+ bool peer_proxy = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_PROXY);
+
+ esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf));
+ if (json_path) {
+ json_object *json_es_info = NULL;
+
+ json_object_string_add(
+ json_path, "esi",
+ esi_buf);
+ if (es_local || bgp_evpn_attr_is_sync(attr)) {
+ json_es_info = json_object_new_object();
+ if (es_local)
+ json_object_boolean_true_add(
+ json_es_info, "localEs");
+ if (peer_active)
+ json_object_boolean_true_add(
+ json_es_info, "peerActive");
+ if (peer_proxy)
+ json_object_boolean_true_add(
+ json_es_info, "peerProxy");
+ if (peer_router)
+ json_object_boolean_true_add(
+ json_es_info, "peerRouter");
+ if (attr->mm_sync_seqnum)
+ json_object_int_add(
+ json_es_info, "peerSeq",
+ attr->mm_sync_seqnum);
+ json_object_object_add(
+ json_path, "es_info",
+ json_es_info);
+ }
+ } else {
+ if (bgp_evpn_attr_is_sync(attr))
+ vty_out(vty,
+ " ESI %s %s peer-info: (%s%s%sMM: %d)\n",
+ esi_buf,
+ es_local ? "local-es":"",
+ peer_proxy ? "proxy " : "",
+ peer_active ? "active ":"",
+ peer_router ? "router ":"",
+ attr->mm_sync_seqnum);
+ else
+ vty_out(vty, " ESI %s %s\n",
+ esi_buf,
+ es_local ? "local-es":"");
+ }
+}
+
+void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+ struct bgp_dest *bn, struct bgp_path_info *path,
+ afi_t afi, safi_t safi, json_object *json_paths)
{
char buf[INET6_ADDRSTRLEN];
char buf1[BUFSIZ];
@@ -9139,6 +9295,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
"used");
}
+ if (safi == SAFI_EVPN &&
+ bgp_evpn_is_esi_valid(&attr->esi)) {
+ route_vty_out_detail_es_info(vty, attr, json_path);
+ }
+
/* Line 3 display Origin, Med, Locpref, Weight, Tag, valid,
* Int/Ext/Local, Atomic, best */
if (json_paths)
@@ -9589,7 +9750,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
void *output_arg, bool use_json, char *rd,
int is_last, unsigned long *output_cum,
unsigned long *total_cum,
- unsigned long *json_header_depth)
+ unsigned long *json_header_depth, bool wide)
{
struct bgp_path_info *pi;
struct bgp_dest *dest;
@@ -9598,7 +9759,6 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
unsigned long output_count = 0;
unsigned long total_count = 0;
struct prefix *p;
- char buf2[BUFSIZ];
json_object *json_paths = NULL;
int first = 1;
@@ -9607,8 +9767,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
if (use_json && !*json_header_depth) {
vty_out(vty,
- "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64
- ",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n"
+ "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n"
" \"localAS\": %u,\n \"routes\": { ",
bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id,
bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
@@ -9793,8 +9952,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
}
if (!use_json && header) {
- vty_out(vty, "BGP table version is %" PRIu64
- ", local router ID is %s, vrf id ",
+ vty_out(vty, "BGP table version is %" PRIu64", local router ID is %s, vrf id ",
table->version,
inet_ntoa(bgp->router_id));
if (bgp->vrf_id == VRF_UNKNOWN)
@@ -9815,7 +9973,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
|| type == bgp_show_type_flap_neighbor)
vty_out(vty, BGP_SHOW_FLAP_HEADER);
else
- vty_out(vty, BGP_SHOW_HEADER);
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
header = 0;
}
if (rd != NULL && !display && !output_count) {
@@ -9836,7 +9995,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
json_paths);
else
route_vty_out(vty, dest_p, pi, display, safi,
- json_paths);
+ json_paths, wide);
display++;
}
@@ -9863,11 +10022,10 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
dest_p->u.prefix_flowspec
.prefixlen);
} else {
- prefix2str(dest_p, buf2, sizeof(buf2));
if (first)
- vty_out(vty, "\"%s\": ", buf2);
+ vty_out(vty, "\"%pFX\": ", dest_p);
else
- vty_out(vty, ",\"%s\": ", buf2);
+ vty_out(vty, ",\"%pFX\": ", dest_p);
}
vty_out(vty, "%s",
json_object_to_json_string_ext(
@@ -9944,7 +10102,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
prefix_rd2str(&prd, rd, sizeof(rd));
bgp_show_table(vty, bgp, safi, itable, type, output_arg,
use_json, rd, next == NULL, &output_cum,
- &total_cum, &json_header_depth);
+ &total_cum, &json_header_depth, false);
if (next == NULL)
show_msg = false;
}
@@ -9961,7 +10119,8 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
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, bool use_json)
+ enum bgp_show_type type, void *output_arg, bool use_json,
+ bool wide)
{
struct bgp_table *table;
unsigned long json_header_depth = 0;
@@ -9995,11 +10154,12 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, use_json,
- NULL, 1, NULL, NULL, &json_header_depth);
+ NULL, 1, NULL, NULL, &json_header_depth, wide);
}
static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
- safi_t safi, bool use_json)
+ safi_t safi, bool use_json,
+ bool wide)
{
struct listnode *node, *nnode;
struct bgp *bgp;
@@ -10028,7 +10188,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
: bgp->name);
}
bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL,
- use_json);
+ use_json, wide);
}
if (use_json)
@@ -10515,8 +10675,8 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_lcommunity_exact
- : bgp_show_type_lcommunity),
- lcom, uj);
+ : bgp_show_type_lcommunity),
+ lcom, uj, false);
}
static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
@@ -10535,8 +10695,8 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_lcommunity_list_exact
- : bgp_show_type_lcommunity_list),
- list, uj);
+ : bgp_show_type_lcommunity_list),
+ list, uj, false);
}
DEFUN (show_ip_bgp_large_community_list,
@@ -10615,9 +10775,11 @@ DEFUN (show_ip_bgp_large_community,
exact_match, afi, safi, uj);
} else
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_lcommunity_all, NULL, uj);
+ bgp_show_type_lcommunity_all, NULL, uj, false);
}
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array);
static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
safi_t safi, struct json_object *json);
@@ -10637,7 +10799,7 @@ DEFUN(show_ip_bgp_statistics_all, show_ip_bgp_statistics_all_cmd,
bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
&bgp, false);
- if (!bgp)
+ if (!idx)
return CMD_WARNING;
if (uj)
@@ -10837,7 +10999,7 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd,
}
/* BGP route print out function with JSON */
-DEFUN (show_ip_bgp_json,
+DEFPY (show_ip_bgp_json,
show_ip_bgp_json_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\
[cidr-only\
@@ -10847,7 +11009,7 @@ DEFUN (show_ip_bgp_json,
|accept-own|accept-own-nexthop|route-filter-v6\
|route-filter-v4|route-filter-translated-v6\
|route-filter-translated-v4] [exact-match]\
- ] [json]",
+ ] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
@@ -10875,7 +11037,8 @@ DEFUN (show_ip_bgp_json,
"RT translated VPNv6 route filtering (well-known community)\n"
"RT translated VPNv4 route filtering (well-known community)\n"
"Exact match of the communities\n"
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
@@ -10883,7 +11046,6 @@ DEFUN (show_ip_bgp_json,
struct bgp *bgp = NULL;
int idx = 0;
int exact_match = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -10895,16 +11057,17 @@ DEFUN (show_ip_bgp_json,
if (argv_find(argv, argc, "cidr-only", &idx))
return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only,
- NULL, uj);
+ NULL, uj, wide);
if (argv_find(argv, argc, "dampening", &idx)) {
if (argv_find(argv, argc, "dampened-paths", &idx))
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_dampend_paths, NULL, uj);
+ bgp_show_type_dampend_paths, NULL, uj,
+ wide);
else if (argv_find(argv, argc, "flap-statistics", &idx))
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_flap_statistics, NULL,
- uj);
+ bgp_show_type_flap_statistics, NULL, uj,
+ wide);
}
if (argv_find(argv, argc, "community", &idx)) {
@@ -10930,17 +11093,16 @@ DEFUN (show_ip_bgp_json,
exact_match, afi, safi, uj);
else
return (bgp_show(vty, bgp, afi, safi,
- bgp_show_type_community_all, NULL,
- uj));
+ bgp_show_type_community_all, NULL, uj,
+ wide));
}
- return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj);
+ return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj, wide);
}
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]"
- "<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -11046,22 +11208,22 @@ DEFUN (show_ip_bgp_regexp,
bgp_show_type_regexp, uj);
}
-DEFUN (show_ip_bgp_instance_all,
+DEFPY (show_ip_bgp_instance_all,
show_ip_bgp_instance_all_cmd,
- "show [ip] bgp <view|vrf> all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json]",
+ "show [ip] bgp <view|vrf> all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
BGP_INSTANCE_ALL_HELP_STR
BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP;
safi_t safi = SAFI_UNICAST;
struct bgp *bgp = NULL;
int idx = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -11071,7 +11233,7 @@ DEFUN (show_ip_bgp_instance_all,
if (!idx)
return CMD_WARNING;
- bgp_show_all_instances_routes_vty(vty, afi, safi, uj);
+ bgp_show_all_instances_routes_vty(vty, afi, safi, uj, wide);
return CMD_SUCCESS;
}
@@ -11094,7 +11256,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
return CMD_WARNING;
}
- rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json);
+ rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json, false);
bgp_regex_free(regex);
return rc;
}
@@ -11112,7 +11274,7 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, plist, 0);
+ return bgp_show(vty, bgp, afi, safi, type, plist, 0, false);
}
static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
@@ -11128,7 +11290,7 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, as_list, 0);
+ return bgp_show(vty, bgp, afi, safi, type, as_list, 0, false);
}
static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
@@ -11143,7 +11305,7 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, rmap, 0);
+ return bgp_show(vty, bgp, afi, safi, type, rmap, 0, false);
}
static int bgp_show_community(struct vty *vty, struct bgp *bgp,
@@ -11162,7 +11324,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp,
ret = bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_exact
: bgp_show_type_community),
- com, use_json);
+ com, use_json, false);
community_free(&com);
return ret;
@@ -11183,7 +11345,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_list_exact
: bgp_show_type_community_list),
- list, 0);
+ list, 0, false);
}
static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
@@ -11201,7 +11363,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- ret = bgp_show(vty, bgp, afi, safi, type, p, 0);
+ ret = bgp_show(vty, bgp, afi, safi, type, p, 0, false);
prefix_free(&p);
return ret;
}
@@ -11396,8 +11558,18 @@ static int bgp_table_stats_walker(struct thread *t)
return 0;
}
-static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi, struct json_object *json_array)
+static void bgp_table_stats_all(struct vty *vty, afi_t afi, safi_t safi,
+ struct json_object *json_array)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
{
struct bgp_table_stats ts;
unsigned int i;
@@ -11425,8 +11597,10 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
}
if (!json)
- vty_out(vty, "BGP %s RIB statistics\n",
- get_afi_safi_str(afi, safi, false));
+ vty_out(vty, "BGP %s RIB statistics (%s)\n",
+ get_afi_safi_str(afi, safi, false), bgp->name_pretty);
+ else
+ json_object_string_add(json, "instance", bgp->name_pretty);
/* labeled-unicast routes live in the unicast table */
if (safi == SAFI_LABELED_UNICAST)
@@ -11615,6 +11789,17 @@ end_table_stats:
return ret;
}
+static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
+{
+ if (!bgp) {
+ bgp_table_stats_all(vty, afi, safi, json_array);
+ return CMD_SUCCESS;
+ }
+
+ return bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
enum bgp_pcounts {
PCOUNT_ADJ_IN = 0,
PCOUNT_DAMPED,
@@ -11624,6 +11809,7 @@ enum bgp_pcounts {
PCOUNT_VALID,
PCOUNT_ALL,
PCOUNT_COUNTED,
+ PCOUNT_BPATH_SELECTED,
PCOUNT_PFCNT, /* the figure we display to users */
PCOUNT_MAX,
};
@@ -11637,6 +11823,7 @@ static const char *const pcount_strs[] = {
[PCOUNT_VALID] = "Valid",
[PCOUNT_ALL] = "All RIB",
[PCOUNT_COUNTED] = "PfxCt counted",
+ [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected",
[PCOUNT_PFCNT] = "Useable",
[PCOUNT_MAX] = NULL,
};
@@ -11677,6 +11864,8 @@ static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc)
pc->count[PCOUNT_VALID]++;
if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
pc->count[PCOUNT_PFCNT]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ pc->count[PCOUNT_BPATH_SELECTED]++;
if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
pc->count[PCOUNT_COUNTED]++;
@@ -11790,7 +11979,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,
get_afi_safi_str(afi, safi, false));
}
- vty_out(vty, "PfxCt: %" PRIu32 "\n", peer->pcount[afi][safi]);
+ vty_out(vty, "PfxCt: %u\n", peer->pcount[afi][safi]);
vty_out(vty, "\nCounts from RIB table walk:\n\n");
for (i = 0; i < PCOUNT_MAX; i++)
@@ -11809,8 +11998,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,
DEFUN (show_ip_bgp_instance_neighbor_prefix_counts,
show_ip_bgp_instance_neighbor_prefix_counts_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] "
- "neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -11941,10 +12129,56 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
use_json(argc, argv));
}
+static void show_adj_route_header(struct vty *vty, struct bgp *bgp,
+ struct bgp_table *table, int *header1,
+ int *header2, json_object *json,
+ json_object *json_scode,
+ json_object *json_ocode, bool wide)
+{
+ uint64_t version = table ? table->version : 0;
+
+ if (*header1) {
+ if (json) {
+ json_object_int_add(json, "bgpTableVersion", version);
+ json_object_string_add(json, "bgpLocalRouterId",
+ inet_ntoa(bgp->router_id));
+ json_object_int_add(json, "defaultLocPrf",
+ bgp->default_local_pref);
+ json_object_int_add(json, "localAS", bgp->as);
+ json_object_object_add(json, "bgpStatusCodes",
+ json_scode);
+ json_object_object_add(json, "bgpOriginCodes",
+ json_ocode);
+ } else {
+ vty_out(vty,
+ "BGP table version is %" PRIu64 ", local router ID is %s, vrf id ",
+ version, inet_ntoa(bgp->router_id));
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "\n");
+ vty_out(vty, "Default local pref %u, ",
+ bgp->default_local_pref);
+ vty_out(vty, "local AS %u\n", bgp->as);
+ vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
+ vty_out(vty, BGP_SHOW_OCODE_HEADER);
+ }
+ *header1 = 0;
+ }
+ if (*header2) {
+ if (!json)
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
+ *header2 = 0;
+ }
+}
+
static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_adj_route_type type,
const char *rmap_name, bool use_json,
- json_object *json)
+ json_object *json, bool wide)
{
struct bgp_table *table;
struct bgp_adj_in *ain;
@@ -12024,8 +12258,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
json, "bgpOriginatingDefaultNetwork",
(afi == AFI_IP) ? "0.0.0.0/0" : "::/0");
} else {
- vty_out(vty, "BGP table version is %" PRIu64
- ", local router ID is %s, vrf id ",
+ vty_out(vty, "BGP table version is %" PRIu64", local router ID is %s, vrf id ",
table->version, inet_ntoa(bgp->router_id));
if (bgp->vrf_id == VRF_UNKNOWN)
vty_out(vty, "%s", VRFID_NONE_STR);
@@ -12052,58 +12285,9 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
if (ain->peer != peer)
continue;
- if (header1) {
- if (use_json) {
- json_object_int_add(
- json, "bgpTableVersion",
- 0);
- json_object_string_add(
- json,
- "bgpLocalRouterId",
- inet_ntoa(
- bgp->router_id));
- json_object_int_add(json,
- "defaultLocPrf",
- bgp->default_local_pref);
- json_object_int_add(json,
- "localAS", bgp->as);
- json_object_object_add(
- json, "bgpStatusCodes",
- json_scode);
- json_object_object_add(
- json, "bgpOriginCodes",
- json_ocode);
- } else {
- vty_out(vty,
- "BGP table version is 0, local router ID is %s, vrf id ",
- inet_ntoa(
- bgp->router_id));
- if (bgp->vrf_id == VRF_UNKNOWN)
- vty_out(vty, "%s",
- VRFID_NONE_STR);
- else
- vty_out(vty, "%u",
- bgp->vrf_id);
- vty_out(vty, "\n");
- vty_out(vty,
- "Default local pref %u, ",
- bgp->default_local_pref);
- vty_out(vty, "local AS %u\n",
- bgp->as);
- vty_out(vty,
- BGP_SHOW_SCODE_HEADER);
- vty_out(vty,
- BGP_SHOW_NCODE_HEADER);
- vty_out(vty,
- BGP_SHOW_OCODE_HEADER);
- }
- header1 = 0;
- }
- if (header2) {
- if (!use_json)
- vty_out(vty, BGP_SHOW_HEADER);
- header2 = 0;
- }
+ show_adj_route_header(
+ vty, bgp, table, &header1, &header2,
+ json, json_scode, json_ocode, wide);
attr = *ain->attr;
route_filtered = false;
@@ -12134,7 +12318,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
filtered_count++;
route_vty_out_tmp(vty, rn_p, &attr, safi,
- use_json, json_ar);
+ use_json, json_ar, wide);
bgp_attr_undup(&attr, ain->attr);
output_count++;
}
@@ -12144,71 +12328,10 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
if (paf->peer != peer || !adj->attr)
continue;
- if (header1) {
- if (use_json) {
- json_object_int_add(
- json,
- "bgpTableVersion",
- table->version);
- json_object_string_add(
- json,
- "bgpLocalRouterId",
- inet_ntoa(
- bgp->router_id));
- json_object_int_add(
- json, "defaultLocPrf",
- bgp->default_local_pref
- );
- json_object_int_add(
- json, "localAS",
- bgp->as);
- json_object_object_add(
- json,
- "bgpStatusCodes",
- json_scode);
- json_object_object_add(
- json,
- "bgpOriginCodes",
- json_ocode);
- } else {
- vty_out(vty,
- "BGP table version is %" PRIu64
- ", local router ID is %s, vrf id ",
- table->version,
- inet_ntoa(
- bgp->router_id));
- if (bgp->vrf_id ==
- VRF_UNKNOWN)
- vty_out(vty,
- "%s",
- VRFID_NONE_STR);
- else
- vty_out(vty,
- "%u",
- bgp->vrf_id);
- vty_out(vty, "\n");
- vty_out(vty,
- "Default local pref %u, ",
- bgp->default_local_pref
- );
- vty_out(vty,
- "local AS %u\n",
- bgp->as);
- vty_out(vty,
- BGP_SHOW_SCODE_HEADER);
- vty_out(vty,
- BGP_SHOW_NCODE_HEADER);
- vty_out(vty,
- BGP_SHOW_OCODE_HEADER);
- }
- header1 = 0;
- }
- if (header2) {
- if (!use_json)
- vty_out(vty,
- BGP_SHOW_HEADER);
- header2 = 0;
- }
+ show_adj_route_header(
+ vty, bgp, table, &header1,
+ &header2, json, json_scode,
+ json_ocode, wide);
const struct prefix *rn_p =
bgp_dest_get_prefix(dest);
@@ -12221,7 +12344,8 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
if (ret != RMAP_DENY) {
route_vty_out_tmp(
vty, rn_p, &attr, safi,
- use_json, json_ar);
+ use_json, json_ar,
+ wide);
output_count++;
} else {
filtered_count++;
@@ -12229,6 +12353,27 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
bgp_attr_undup(&attr, adj->attr);
}
+ } else if (type == bgp_show_adj_route_bestpath) {
+ struct bgp_path_info *pi;
+
+ show_adj_route_header(vty, bgp, table, &header1,
+ &header2, json, json_scode,
+ json_ocode, wide);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ continue;
+
+ route_vty_out_tmp(vty,
+ bgp_dest_get_prefix(dest),
+ pi->attr, safi, use_json,
+ json_ar, wide);
+ output_count++;
+ }
}
}
@@ -12260,7 +12405,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_adj_route_type type,
- const char *rmap_name, bool use_json)
+ const char *rmap_name, bool use_json, bool wide)
{
json_object *json = NULL;
@@ -12297,15 +12442,57 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
return CMD_WARNING;
}
- show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json);
+ show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json,
+ wide);
return CMD_SUCCESS;
}
-DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
+DEFPY (show_ip_bgp_instance_neighbor_bestpath_route,
+ show_ip_bgp_instance_neighbor_bestpath_route_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> bestpath-routes [json$uj | wide$wide]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display the routes selected by best path\n"
+ JSON_STR
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ char *rmap_name = NULL;
+ char *peerstr = NULL;
+ struct bgp *bgp = NULL;
+ struct peer *peer;
+ enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath;
+ int idx = 0;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+
+ if (!idx)
+ return CMD_WARNING;
+
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide);
+}
+
+DEFPY (show_ip_bgp_instance_neighbor_advertised_route,
show_ip_bgp_instance_neighbor_advertised_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] "
- "neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
@@ -12321,7 +12508,8 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
"Display the filtered routes received from neighbor\n"
"Route-map to modify the attributes\n"
"Name of the route map\n"
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
@@ -12331,7 +12519,6 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
struct peer *peer;
enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised;
int idx = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -12359,7 +12546,7 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
if (argv_find(argv, argc, "route-map", &idx))
rmap_name = argv[++idx]->arg;
- return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj);
+ return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide);
}
DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
@@ -12467,7 +12654,8 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
return CMD_WARNING;
}
- return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json);
+ return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json,
+ false);
}
DEFUN (show_ip_bgp_flowspec_routes_detailed,
@@ -12496,13 +12684,13 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed,
if (!idx)
return CMD_WARNING;
- return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj);
+ return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj,
+ false);
}
DEFUN (show_ip_bgp_neighbor_routes,
show_ip_bgp_neighbor_routes_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] "
- "neighbors <A.B.C.D|X:X::X:X|WORD> <flap-statistics|dampened-routes|routes> [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <flap-statistics|dampened-routes|routes> [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -13286,6 +13474,7 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
char buf[PREFIX_STRLEN * 2];
char buf2[SU_ADDRSTRLEN];
char rdbuf[RD_ADDRSTRLEN];
+ char esi_buf[ESI_BYTES];
/* Network configuration. */
for (pdest = bgp_table_top(bgp->route[afi][safi]); pdest;
@@ -13301,13 +13490,13 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
continue;
char *macrouter = NULL;
- char *esi = NULL;
if (bgp_static->router_mac)
macrouter = prefix_mac2str(
bgp_static->router_mac, NULL, 0);
if (bgp_static->eth_s_id)
- esi = esi2str(bgp_static->eth_s_id);
+ esi_to_str(bgp_static->eth_s_id,
+ esi_buf, sizeof(esi_buf));
p = bgp_dest_get_prefix(dest);
prd = (struct prefix_rd *)bgp_dest_get_prefix(pdest);
@@ -13338,11 +13527,10 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
" network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n",
buf, rdbuf,
p->u.prefix_evpn.prefix_addr.eth_tag,
- decode_label(&bgp_static->label), esi, buf2,
+ decode_label(&bgp_static->label), esi_buf, buf2,
macrouter);
XFREE(MTYPE_TMP, macrouter);
- XFREE(MTYPE_TMP, esi);
}
}
}
@@ -13511,6 +13699,8 @@ void bgp_route_init(void)
install_element(VIEW_NODE,
&show_ip_bgp_instance_neighbor_advertised_route_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_instance_neighbor_bestpath_route_cmd);
install_element(VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd);
install_element(VIEW_NODE,
&show_ip_bgp_neighbor_received_prefix_filter_cmd);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index ee52dcc8c6..3f734d2672 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -62,6 +62,7 @@ enum bgp_show_adj_route_type {
bgp_show_adj_route_advertised,
bgp_show_adj_route_received,
bgp_show_adj_route_filtered,
+ bgp_show_adj_route_bestpath,
};
@@ -72,6 +73,7 @@ enum bgp_show_adj_route_type {
#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"
#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n"
#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
+#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n"
/* Maximum number of labels we can process or send with a prefix. We
* really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN.
@@ -97,6 +99,7 @@ enum bgp_show_adj_route_type {
#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12
#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13
#define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15
#define BGP_NLRI_PARSE_ERROR -32
/* Ancillary information to struct bgp_path_info,
@@ -301,7 +304,7 @@ struct bgp_static {
mpls_label_t label;
/* EVPN */
- struct eth_segment_id *eth_s_id;
+ esi_t *eth_s_id;
struct ethaddr *router_mac;
uint16_t encap_tunneltype;
struct prefix gatewayIp;
@@ -618,13 +621,13 @@ extern struct bgp_path_info *info_make(int type, int sub_type,
extern void route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display, safi_t safi,
- json_object *json_paths);
+ json_object *json_paths, bool wide);
extern void route_vty_out_tag(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
safi_t safi, json_object *json);
extern void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
struct attr *attr, safi_t safi, bool use_json,
- json_object *json_ar);
+ json_object *json_ar, bool wide);
extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
json_object *json);
@@ -679,4 +682,6 @@ extern int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi);
extern bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
uint8_t type, uint8_t stype,
struct attr *attr, struct bgp_dest *dest);
+extern int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, int *paths_eq);
#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index aafeb14762..97153cfb72 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3756,8 +3756,7 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name,
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug(
- "Processing route_map %s update on "
- "table map",
+ "Processing route_map %s update on table map",
rmap_name);
if (route_update)
bgp_zebra_announce_table(bgp, afi, safi);
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 4e0642dce2..63cfacf678 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -7,7 +7,6 @@
* Hamburg
* Copyright (C) 2017-2018 Marcel Röthke (marcel.roethke@haw-hamburg.de),
* for HAW Hamburg
- * Copyright (C) 2019 6WIND
*
* This file is part of FRRouting.
*
@@ -48,7 +47,6 @@
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
-#include "bgpd/bgp_debug.h"
#include "lib/network.h"
#include "lib/thread.h"
#ifndef VTYSH_EXTRACT_PL
@@ -62,7 +60,6 @@
#include "bgpd/bgp_rpki_clippy.c"
#endif
-DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_TEMP, "BGP RPKI Intermediate Buffer")
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server")
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group")
@@ -70,14 +67,12 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group")
#define RPKI_NOTFOUND 2
#define RPKI_INVALID 3
-#define STR_SEPARATOR 10
-
#define POLLING_PERIOD_DEFAULT 3600
#define EXPIRE_INTERVAL_DEFAULT 7200
#define RETRY_INTERVAL_DEFAULT 600
#define RPKI_DEBUG(...) \
- if (rpki_debug_conf || rpki_debug_term) { \
+ if (rpki_debug) { \
zlog_debug("RPKI: " __VA_ARGS__); \
}
@@ -92,66 +87,40 @@ struct cache {
} tr_config;
struct rtr_socket *rtr_socket;
uint8_t preference;
- struct rpki_vrf *rpki_vrf;
};
enum return_values { SUCCESS = 0, ERROR = -1 };
-extern struct zebra_privs_t bgpd_privs;
-
struct rpki_for_each_record_arg {
struct vty *vty;
unsigned int *prefix_amount;
as_t as;
};
-struct rpki_vrf {
- struct rtr_mgr_config *rtr_config;
- struct list *cache_list;
- bool rtr_is_running;
- bool rtr_is_stopping;
- _Atomic int rtr_update_overflow;
- unsigned int polling_period;
- unsigned int expire_interval;
- unsigned int retry_interval;
- int rpki_sync_socket_rtr;
- int rpki_sync_socket_bgpd;
- char *vrfname;
- QOBJ_FIELDS
-};
-
-static struct rpki_vrf *find_rpki_vrf(const char *vrfname);
-static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled);
-static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf);
-static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf);
-static int bgp_rpki_write_debug(struct vty *vty, bool running);
-static int start(struct rpki_vrf *rpki_vrf);
-static void stop(struct rpki_vrf *rpki_vrf);
-static int reset(bool force, struct rpki_vrf *rpki_vrf);
-static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf);
-static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf);
+static int start(void);
+static void stop(void);
+static int reset(bool force);
+static struct rtr_mgr_group *get_connected_group(void);
+static void print_prefix_table(struct vty *vty);
static void install_cli_commands(void);
static int config_write(struct vty *vty);
static int config_on_exit(struct vty *vty);
static void free_cache(struct cache *cache);
-static struct rtr_mgr_group *get_groups(struct list *cache_list);
+static struct rtr_mgr_group *get_groups(void);
#if defined(FOUND_SSH)
-static int add_ssh_cache(struct rpki_vrf *rpki_vrf,
- const char *host,
- const unsigned int port,
+static int add_ssh_cache(const char *host, const unsigned int port,
const char *username, const char *client_privkey_path,
const char *client_pubkey_path,
const char *server_pubkey_path,
const uint8_t preference);
#endif
static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket);
-static struct cache *find_cache(const uint8_t preference,
- struct list *cache_list);
-static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host,
- const char *port, const uint8_t preference);
+static struct cache *find_cache(const uint8_t preference);
+static int add_tcp_cache(const char *host, const char *port,
+ const uint8_t preference);
static void print_record(const struct pfx_record *record, struct vty *vty);
-static int is_synchronized(struct rpki_vrf *rpki);
-static int is_running(struct rpki_vrf *rpki);
+static int is_synchronized(void);
+static int is_running(void);
static void route_match_free(void *rule);
static enum route_map_cmd_result_t route_match(void *rule,
const struct prefix *prefix,
@@ -159,14 +128,19 @@ static enum route_map_cmd_result_t route_match(void *rule,
void *object);
static void *route_match_compile(const char *arg);
static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi);
-static void revalidate_all_routes(struct rpki_vrf *rpki_vrf);
-
-static int rpki_debug_conf, rpki_debug_term;
-
-DECLARE_QOBJ_TYPE(rpki_vrf)
-DEFINE_QOBJ_TYPE(rpki_vrf)
-
-struct list *rpki_vrf_list;
+static void revalidate_all_routes(void);
+
+static struct rtr_mgr_config *rtr_config;
+static struct list *cache_list;
+static int rtr_is_running;
+static int rtr_is_stopping;
+static _Atomic int rtr_update_overflow;
+static int rpki_debug;
+static unsigned int polling_period;
+static unsigned int expire_interval;
+static unsigned int retry_interval;
+static int rpki_sync_socket_rtr;
+static int rpki_sync_socket_bgpd;
static struct cmd_node rpki_node = {
.name = "rpki",
@@ -176,16 +150,6 @@ static struct cmd_node rpki_node = {
.config_write = config_write,
.node_exit = config_on_exit,
};
-
-static struct cmd_node rpki_vrf_node = {
- .name = "rpki",
- .node = RPKI_VRF_NODE,
- .parent_node = VRF_NODE,
- .prompt = "%s(config-vrf-rpki)# ",
- .config_write = config_write,
- .node_exit = config_on_exit,
-};
-
static const struct route_map_rule_cmd route_match_rpki_cmd = {
"rpki", route_match, route_match_compile, route_match_free};
@@ -295,127 +259,11 @@ static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket)
return rtr_socket;
}
-static int bgp_rpki_vrf_update(struct vrf *vrf, bool enabled)
-{
- struct rpki_vrf *rpki;
-
- if (vrf->vrf_id == VRF_DEFAULT)
- rpki = find_rpki_vrf(NULL);
- else
- rpki = find_rpki_vrf(vrf->name);
- if (!rpki)
- return 0;
-
- if (enabled)
- start(rpki);
- else
- stop(rpki);
- return 1;
-}
-
-/* tcp identifier : <HOST>:<PORT>
- * ssh identifier : <user>@<HOST>:<PORT>
- */
-static struct rpki_vrf *find_rpki_vrf_from_ident(const char *ident)
-{
- char *ptr;
- unsigned int port;
- char *endptr;
- struct listnode *rpki_vrf_nnode;
- struct rpki_vrf *rpki_vrf;
- struct listnode *cache_node;
- struct cache *cache;
- char *buf, *host;
- bool is_tcp = true;
- size_t host_len;
-
- /* extract the <SOCKET> */
- ptr = strrchr(ident, ':');
- if (!ptr)
- return NULL;
- ptr++;
- /* extract port */
- port = atoi(ptr);
- if (port == 0)
- /* not ours */
- return NULL;
- /* extract host */
- ptr--;
- host_len = (size_t)(ptr - ident);
- buf = XCALLOC(MTYPE_BGP_RPKI_TEMP, host_len + 1);
- memcpy(buf, ident, host_len);
- buf[host_len] = '\0';
- endptr = strrchr(buf, '@');
- /* ssh session */
- if (endptr) {
- host = XCALLOC(MTYPE_BGP_RPKI_TEMP, (size_t)(buf + host_len - endptr) + 1);
- memcpy(host, endptr + 1, (size_t)(buf + host_len - endptr) + 1);
- is_tcp = false;
- } else {
- host = buf;
- buf = NULL;
- }
-
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) {
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list,
- cache_node, cache)) {
- if ((cache->type == TCP && !is_tcp) ||
- (cache->type == SSH && is_tcp))
- continue;
- if (is_tcp) {
- struct tr_tcp_config *tcp_config = cache->tr_config.tcp_config;
- unsigned int cache_port;
-
- cache_port = atoi(tcp_config->port);
- if (cache_port != port)
- continue;
- if (strlen(tcp_config->host) != strlen(host))
- continue;
- if (0 == memcmp(tcp_config->host, host, host_len))
- break;
- } else {
- struct tr_ssh_config *ssh_config = cache->tr_config.ssh_config;
-
- if (port != ssh_config->port)
- continue;
- if (strmatch(ssh_config->host, host))
- break;
- }
- }
- if (cache)
- break;
- }
- if (host)
- XFREE(MTYPE_BGP_RPKI_TEMP, host);
- if (buf)
- XFREE(MTYPE_BGP_RPKI_TEMP, buf);
- return rpki_vrf;
-}
-
-static struct rpki_vrf *find_rpki_vrf(const char *vrfname)
-{
- struct listnode *rpki_vrf_nnode;
- struct rpki_vrf *rpki_vrf;
-
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf_list, rpki_vrf_nnode, rpki_vrf)) {
- if ((!vrfname && rpki_vrf->vrfname) ||
- (vrfname && !rpki_vrf->vrfname) ||
- (vrfname && rpki_vrf->vrfname &&
- !strmatch(vrfname, rpki_vrf->vrfname)))
- continue;
- return rpki_vrf;
- }
- return NULL;
-}
-
-static struct cache *find_cache(const uint8_t preference,
- struct list *cache_list)
+static struct cache *find_cache(const uint8_t preference)
{
struct listnode *cache_node;
struct cache *cache;
- if (!cache_list)
- return NULL;
for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
if (cache->preference == preference)
return cache;
@@ -453,14 +301,14 @@ static void print_record_cb(const struct pfx_record *record, void *data)
print_record(record, vty);
}
-static struct rtr_mgr_group *get_groups(struct list *cache_list)
+static struct rtr_mgr_group *get_groups(void)
{
struct listnode *cache_node;
struct rtr_mgr_group *rtr_mgr_groups;
struct cache *cache;
- int group_count;
- group_count = listcount(cache_list);
+ int group_count = listcount(cache_list);
+
if (group_count == 0)
return NULL;
@@ -482,15 +330,14 @@ static struct rtr_mgr_group *get_groups(struct list *cache_list)
return rtr_mgr_groups;
}
-inline int is_synchronized(struct rpki_vrf *rpki_vrf)
+inline int is_synchronized(void)
{
- return rpki_vrf->rtr_is_running &&
- rtr_mgr_conf_in_sync(rpki_vrf->rtr_config);
+ return rtr_is_running && rtr_mgr_conf_in_sync(rtr_config);
}
-inline int is_running(struct rpki_vrf *rpki_vrf)
+inline int is_running(void)
{
- return rpki_vrf->rtr_is_running;
+ return rtr_is_running;
}
static struct prefix *pfx_record_to_prefix(struct pfx_record *record)
@@ -517,28 +364,24 @@ static int bgpd_sync_callback(struct thread *thread)
struct listnode *node;
struct prefix *prefix;
struct pfx_record rec;
- struct rpki_vrf *rpki_vrf = THREAD_ARG(thread);
- struct vrf *vrf = NULL;
- thread_add_read(bm->master, bgpd_sync_callback, rpki_vrf,
- rpki_vrf->rpki_sync_socket_bgpd, NULL);
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
- if (atomic_load_explicit(&rpki_vrf->rtr_update_overflow,
- memory_order_seq_cst)) {
- while (read(rpki_vrf->rpki_sync_socket_bgpd, &rec,
+ if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
+ while (read(rpki_sync_socket_bgpd, &rec,
sizeof(struct pfx_record))
!= -1)
;
- atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 0,
+ atomic_store_explicit(&rtr_update_overflow, 0,
memory_order_seq_cst);
- revalidate_all_routes(rpki_vrf);
+ revalidate_all_routes();
return 0;
}
int retval =
- read(rpki_vrf->rpki_sync_socket_bgpd, &rec,
- sizeof(struct pfx_record));
+ read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
if (retval != sizeof(struct pfx_record)) {
RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
return retval;
@@ -547,24 +390,10 @@ static int bgpd_sync_callback(struct thread *thread)
afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
- if (rpki_vrf->vrfname) {
- vrf = vrf_lookup_by_name(rpki_vrf->vrfname);
- if (!vrf) {
- zlog_err("%s(): vrf for rpki %s not found",
- __func__, rpki_vrf->vrfname);
- return 0;
- }
- }
-
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
struct peer *peer;
struct listnode *peer_listnode;
- if (!vrf && bgp->vrf_id != VRF_DEFAULT)
- continue;
- if (vrf && bgp->vrf_id != vrf->vrf_id)
- continue;
-
for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
safi_t safi;
@@ -623,30 +452,15 @@ static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
}
}
-static void revalidate_all_routes(struct rpki_vrf *rpki_vrf)
+static void revalidate_all_routes(void)
{
struct bgp *bgp;
struct listnode *node;
- struct vrf *vrf = NULL;
-
- if (rpki_vrf->vrfname) {
- vrf = vrf_lookup_by_name(rpki_vrf->vrfname);
- if (!vrf) {
- zlog_err("%s(): vrf for rpki %s not found",
- __func__, rpki_vrf->vrfname);
- return;
- }
- }
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
struct peer *peer;
struct listnode *peer_listnode;
- if (!vrf && bgp->vrf_id != VRF_DEFAULT)
- continue;
- if (vrf && bgp->vrf_id != vrf->vrf_id)
- continue;
-
for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
for (size_t i = 0; i < 2; i++) {
@@ -669,51 +483,21 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
const struct pfx_record rec,
const bool added __attribute__((unused)))
{
- struct rpki_vrf *rpki_vrf;
- const char *msg;
- const struct rtr_socket *rtr = rec.socket;
- struct tr_socket *tr;
- const char *ident;
- int retval;
-
- if (!rtr) {
- msg = "could not find rtr_socket from cb_sync_rtr";
- goto err;
- }
- tr = rtr->tr_socket;
- if (!tr) {
- msg = "could not find tr_socket from cb_sync_rtr";
- goto err;
- }
- ident = tr->ident_fp(tr->socket);
- if (!ident) {
- msg = "could not find rpki_vrf ident";
- goto err;
- }
- rpki_vrf = find_rpki_vrf_from_ident(ident);
- if (!rpki_vrf) {
- msg = "could not find rpki_vrf";
- goto err;
- }
- if (rpki_vrf->rtr_is_stopping
- || atomic_load_explicit(&rpki_vrf->rtr_update_overflow,
- memory_order_seq_cst))
+ if (rtr_is_stopping
+ || atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
return;
- retval =
- write(rpki_vrf->rpki_sync_socket_rtr, &rec,
- sizeof(struct pfx_record));
+
+ int retval =
+ write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
- atomic_store_explicit(&rpki_vrf->rtr_update_overflow, 1,
+ atomic_store_explicit(&rtr_update_overflow, 1,
memory_order_seq_cst);
else if (retval != sizeof(struct pfx_record))
RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
- return;
-err:
- zlog_err("RPKI: %s", msg);
}
-static void rpki_init_sync_socket(struct rpki_vrf *rpki_vrf)
+static void rpki_init_sync_socket(void)
{
int fds[2];
const char *msg;
@@ -723,22 +507,22 @@ static void rpki_init_sync_socket(struct rpki_vrf *rpki_vrf)
msg = "could not open rpki sync socketpair";
goto err;
}
- rpki_vrf->rpki_sync_socket_rtr = fds[0];
- rpki_vrf->rpki_sync_socket_bgpd = fds[1];
+ rpki_sync_socket_rtr = fds[0];
+ rpki_sync_socket_bgpd = fds[1];
- if (set_nonblocking(rpki_vrf->rpki_sync_socket_rtr) != 0) {
+ if (set_nonblocking(rpki_sync_socket_rtr) != 0) {
msg = "could not set rpki_sync_socket_rtr to non blocking";
goto err;
}
- if (set_nonblocking(rpki_vrf->rpki_sync_socket_bgpd) != 0) {
+ if (set_nonblocking(rpki_sync_socket_bgpd) != 0) {
msg = "could not set rpki_sync_socket_bgpd to non blocking";
goto err;
}
- thread_add_read(bm->master, bgpd_sync_callback, rpki_vrf,
- rpki_vrf->rpki_sync_socket_bgpd, NULL);
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
return;
@@ -748,64 +532,30 @@ err:
}
-static struct rpki_vrf *bgp_rpki_allocate(const char *vrfname)
-{
- struct rpki_vrf *rpki_vrf;
-
- rpki_vrf = XCALLOC(MTYPE_BGP_RPKI_CACHE,
- sizeof(struct rpki_vrf));
-
- rpki_vrf->rtr_is_running = false;
- rpki_vrf->rtr_is_stopping = false;
- rpki_vrf->cache_list = list_new();
- rpki_vrf->cache_list->del = (void (*)(void *)) & free_cache;
- rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT;
- rpki_vrf->expire_interval = EXPIRE_INTERVAL_DEFAULT;
- rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT;
-
- if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME))
- rpki_vrf->vrfname = XSTRDUP(MTYPE_BGP_RPKI_CACHE,
- vrfname);
- QOBJ_REG(rpki_vrf, rpki_vrf);
- listnode_add(rpki_vrf_list, rpki_vrf);
- return rpki_vrf;
-}
-
static int bgp_rpki_init(struct thread_master *master)
{
- rpki_debug_conf = 0;
- rpki_debug_term = 0;
+ rpki_debug = 0;
+ rtr_is_running = 0;
+ rtr_is_stopping = 0;
- rpki_vrf_list = list_new();
- install_cli_commands();
+ cache_list = list_new();
+ cache_list->del = (void (*)(void *)) & free_cache;
+ polling_period = POLLING_PERIOD_DEFAULT;
+ expire_interval = EXPIRE_INTERVAL_DEFAULT;
+ retry_interval = RETRY_INTERVAL_DEFAULT;
+ install_cli_commands();
+ rpki_init_sync_socket();
return 0;
}
-static void bgp_rpki_finish(struct rpki_vrf *rpki_vrf)
-{
- stop(rpki_vrf);
- list_delete(&rpki_vrf->cache_list);
-
- close(rpki_vrf->rpki_sync_socket_rtr);
- close(rpki_vrf->rpki_sync_socket_bgpd);
-
- listnode_delete(rpki_vrf_list, rpki_vrf);
- QOBJ_UNREG(rpki_vrf);
- if (rpki_vrf->vrfname)
- XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf->vrfname);
- XFREE(MTYPE_BGP_RPKI_CACHE, rpki_vrf);
-}
-
static int bgp_rpki_fini(void)
{
- struct rpki_vrf *rpki_vrf;
+ stop();
+ list_delete(&cache_list);
- /* assume default vrf */
- rpki_vrf = find_rpki_vrf(NULL);
- if (!rpki_vrf)
- return 0;
- bgp_rpki_finish(rpki_vrf);
+ close(rpki_sync_socket_rtr);
+ close(rpki_sync_socket_bgpd);
return 0;
}
@@ -816,113 +566,87 @@ static int bgp_rpki_module_init(void)
hook_register(frr_late_init, bgp_rpki_init);
hook_register(frr_early_fini, &bgp_rpki_fini);
- hook_register(bgp_hook_config_write_debug, &bgp_rpki_write_debug);
- hook_register(bgp_hook_vrf_update, &bgp_rpki_vrf_update);
- hook_register(bgp_hook_config_write_vrf, &bgp_rpki_hook_write_vrf);
return 0;
}
-static int start(struct rpki_vrf *rpki_vrf)
+static int start(void)
{
int ret;
- struct list *cache_list = NULL;
- struct vrf *vrf;
- cache_list = rpki_vrf->cache_list;
- rpki_vrf->rtr_is_stopping = false;
- rpki_vrf->rtr_update_overflow = 0;
+ rtr_is_stopping = 0;
+ rtr_update_overflow = 0;
- if (!cache_list || list_isempty(cache_list)) {
- RPKI_DEBUG("No caches were found in config."
- "Prefix validation is off.");
- return ERROR;
- }
-
- if (rpki_vrf->vrfname)
- vrf = vrf_lookup_by_name(rpki_vrf->vrfname);
- else
- vrf = vrf_lookup_by_id(VRF_DEFAULT);
- if (!vrf || !CHECK_FLAG(vrf->status, VRF_ACTIVE)) {
- RPKI_DEBUG("VRF %s not present or disabled",
- rpki_vrf->vrfname);
+ if (list_isempty(cache_list)) {
+ RPKI_DEBUG(
+ "No caches were found in config. Prefix validation is off.");
return ERROR;
}
-
- RPKI_DEBUG("Init rtr_mgr (%s).", vrf->name);
+ RPKI_DEBUG("Init rtr_mgr.");
int groups_len = listcount(cache_list);
- struct rtr_mgr_group *groups = get_groups(rpki_vrf->cache_list);
+ struct rtr_mgr_group *groups = get_groups();
- RPKI_DEBUG("Polling period: %d", rpki_vrf->polling_period);
- ret = rtr_mgr_init(&rpki_vrf->rtr_config, groups, groups_len,
- rpki_vrf->polling_period, rpki_vrf->expire_interval,
- rpki_vrf->retry_interval, rpki_update_cb_sync_rtr,
- NULL, NULL, NULL);
+ RPKI_DEBUG("Polling period: %d", polling_period);
+ ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
+ expire_interval, retry_interval,
+ rpki_update_cb_sync_rtr, NULL, NULL, NULL);
if (ret == RTR_ERROR) {
- RPKI_DEBUG("Init rtr_mgr failed (%s).", vrf->name);
+ RPKI_DEBUG("Init rtr_mgr failed.");
return ERROR;
}
- RPKI_DEBUG("Starting rtr_mgr (%s).", vrf->name);
- ret = rtr_mgr_start(rpki_vrf->rtr_config);
+ RPKI_DEBUG("Starting rtr_mgr.");
+ ret = rtr_mgr_start(rtr_config);
if (ret == RTR_ERROR) {
- RPKI_DEBUG("Starting rtr_mgr failed (%s).", vrf->name);
- rtr_mgr_free(rpki_vrf->rtr_config);
+ RPKI_DEBUG("Starting rtr_mgr failed.");
+ rtr_mgr_free(rtr_config);
return ERROR;
}
- rpki_vrf->rtr_is_running = true;
+ rtr_is_running = 1;
XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
return SUCCESS;
}
-static void stop(struct rpki_vrf *rpki_vrf)
+static void stop(void)
{
- rpki_vrf->rtr_is_stopping = true;
- if (rpki_vrf->rtr_is_running) {
- rtr_mgr_stop(rpki_vrf->rtr_config);
- rtr_mgr_free(rpki_vrf->rtr_config);
- rpki_vrf->rtr_is_running = false;
+ rtr_is_stopping = 1;
+ if (rtr_is_running) {
+ rtr_mgr_stop(rtr_config);
+ rtr_mgr_free(rtr_config);
+ rtr_is_running = 0;
}
}
-static int reset(bool force, struct rpki_vrf *rpki_vrf)
+static int reset(bool force)
{
- if (rpki_vrf->rtr_is_running && !force)
+ if (rtr_is_running && !force)
return SUCCESS;
RPKI_DEBUG("Resetting RPKI Session");
- stop(rpki_vrf);
- return start(rpki_vrf);
+ stop();
+ return start();
}
-static struct rtr_mgr_group *get_connected_group(struct rpki_vrf *rpki_vrf)
+static struct rtr_mgr_group *get_connected_group(void)
{
- struct list *cache_list;
-
- if (!rpki_vrf)
- return NULL;
- cache_list = rpki_vrf->cache_list;
if (!cache_list || list_isempty(cache_list))
return NULL;
- return rtr_mgr_get_first_group(rpki_vrf->rtr_config);
+ return rtr_mgr_get_first_group(rtr_config);
}
-static void print_prefix_table_by_asn(struct vty *vty, as_t as, struct rpki_vrf *rpki_vrf)
+static void print_prefix_table_by_asn(struct vty *vty, as_t as)
{
unsigned int number_of_ipv4_prefixes = 0;
unsigned int number_of_ipv6_prefixes = 0;
- struct rtr_mgr_group *group = get_connected_group(rpki_vrf);
+ struct rtr_mgr_group *group = get_connected_group();
struct rpki_for_each_record_arg arg;
arg.vty = vty;
arg.as = as;
- if (!rpki_vrf)
- return;
-
if (!group) {
vty_out(vty, "Cannot find a connected group.\n");
return;
@@ -943,17 +667,14 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, struct rpki_vrf
vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes);
}
-static void print_prefix_table(struct vty *vty, struct rpki_vrf *rpki_vrf)
+static void print_prefix_table(struct vty *vty)
{
struct rpki_for_each_record_arg arg;
unsigned int number_of_ipv4_prefixes = 0;
unsigned int number_of_ipv6_prefixes = 0;
- struct rtr_mgr_group *group;
+ struct rtr_mgr_group *group = get_connected_group();
- if (!rpki_vrf)
- return;
- group = get_connected_group(rpki_vrf);
arg.vty = vty;
if (!group)
@@ -983,20 +704,8 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
enum pfxv_state result;
char buf[BUFSIZ];
const char *prefix_string;
- struct bgp *bgp = peer->bgp;
- struct vrf *vrf;
- struct rpki_vrf *rpki_vrf;
- if (!bgp)
- return 0;
- vrf = vrf_lookup_by_id(bgp->vrf_id);
- if (!vrf)
- return 0;
- if (vrf->vrf_id == VRF_DEFAULT)
- rpki_vrf = find_rpki_vrf(NULL);
- else
- rpki_vrf = find_rpki_vrf(vrf->name);
- if (!rpki_vrf || !is_synchronized(rpki_vrf))
+ if (!is_synchronized())
return 0;
// No aspath means route comes from iBGP
@@ -1041,7 +750,7 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
}
// Do the actual validation
- rtr_mgr_validate(rpki_vrf->rtr_config, as_number, &ip_addr_prefix,
+ rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix,
prefix->prefixlen, &result);
// Print Debug output
@@ -1075,26 +784,15 @@ static int add_cache(struct cache *cache)
{
uint8_t preference = cache->preference;
struct rtr_mgr_group group;
- struct list *cache_list;
- struct rpki_vrf *rpki_vrf;
-
- rpki_vrf = cache->rpki_vrf;
- if (!rpki_vrf)
- return ERROR;
group.preference = preference;
group.sockets_len = 1;
group.sockets = &cache->rtr_socket;
- cache_list = rpki_vrf->cache_list;
- if (!cache_list)
- return ERROR;
-
- if (rpki_vrf->rtr_is_running) {
+ if (rtr_is_running) {
init_tr_socket(cache);
- if (rtr_mgr_add_group(rpki_vrf->rtr_config, &group)
- != RTR_SUCCESS) {
+ if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
free_tr_socket(cache);
return ERROR;
}
@@ -1105,160 +803,57 @@ static int add_cache(struct cache *cache)
return SUCCESS;
}
-static int rpki_create_socket(struct cache *cache)
-{
- struct vrf *vrf;
- int socket;
- struct addrinfo hints;
- struct addrinfo *res = NULL;
- char *host, *port;
- struct rpki_vrf *rpki_vrf = cache->rpki_vrf;
- int ret;
-
- if (rpki_vrf->vrfname == NULL)
- vrf = vrf_lookup_by_id(VRF_DEFAULT);
- else
- vrf = vrf_lookup_by_name(rpki_vrf->vrfname);
- if (!vrf)
- return 0;
-
- if (!CHECK_FLAG(vrf->status, VRF_ACTIVE) ||
- vrf->vrf_id == VRF_UNKNOWN)
- return 0;
-
- bzero(&hints, sizeof(hints));
-
- if (cache->type == TCP) {
- struct tr_tcp_config *tcp_config;
-
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_ADDRCONFIG;
-
- tcp_config = cache->tr_config.tcp_config;
- host = tcp_config->host;
- port = tcp_config->port;
- } else {
- char s_port[10];
- struct tr_ssh_config *ssh_config;
-
- ssh_config = cache->tr_config.ssh_config;
- host = ssh_config->host;
- snprintf(s_port, sizeof(s_port), "%hu",
- ssh_config->port);
- port = s_port;
-
- hints.ai_flags |= AI_NUMERICHOST;
- hints.ai_protocol = IPPROTO_TCP;
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- }
- frr_with_privs(&bgpd_privs) {
- ret = vrf_getaddrinfo(host, port,
- &hints, &res, vrf->vrf_id);
- }
- if (ret != 0) {
- zlog_err("getaddrinfo error, %u", errno);
- return 0;
- }
- frr_with_privs(&bgpd_privs) {
- socket = vrf_socket(res->ai_family, res->ai_socktype,
- res->ai_protocol, vrf->vrf_id, NULL);
- }
- if (socket <= 0) {
- zlog_err("vrf socket error, %u", errno);
- return 0;
- }
-
- if (connect(socket, res->ai_addr, res->ai_addrlen) == -1) {
- zlog_err("Couldn't establish TCP connection, %s", strerror(errno));
- if (res)
- freeaddrinfo(res);
- return 0;
- }
- if (res)
- freeaddrinfo(res);
- return socket;
-}
-
-static int rpki_get_socket(void *_cache)
-{
- int sock;
- struct cache *cache = (struct cache *)_cache;
-
- if (!cache)
- return -1;
- sock = rpki_create_socket(cache);
- if (sock <= 0)
- return -1;
- return sock;
-}
-
-static int add_tcp_cache(struct rpki_vrf *rpki_vrf, const char *host,
- const char *port, const uint8_t preference)
+static int add_tcp_cache(const char *host, const char *port,
+ const uint8_t preference)
{
struct rtr_socket *rtr_socket;
- struct tr_tcp_config *tcp_config;
- struct tr_socket *tr_socket;
- struct cache *cache;
- int ret;
-
- tcp_config = XCALLOC(MTYPE_BGP_RPKI_CACHE,
- sizeof(struct tr_tcp_config));
- tr_socket = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
- cache = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+ struct tr_tcp_config *tcp_config =
+ XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config));
+ struct tr_socket *tr_socket =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
+ struct cache *cache =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
tcp_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
tcp_config->port = XSTRDUP(MTYPE_BGP_RPKI_CACHE, port);
tcp_config->bindaddr = NULL;
- tcp_config->data = cache;
- tcp_config->new_socket = rpki_get_socket;
+
rtr_socket = create_rtr_socket(tr_socket);
- cache->rpki_vrf = rpki_vrf;
cache->type = TCP;
cache->tr_socket = tr_socket;
cache->tr_config.tcp_config = tcp_config;
cache->rtr_socket = rtr_socket;
cache->preference = preference;
- ret = add_cache(cache);
+ int ret = add_cache(cache);
if (ret != SUCCESS) {
free_cache(cache);
}
+
return ret;
}
#if defined(FOUND_SSH)
-static int add_ssh_cache(struct rpki_vrf *rpki_vrf,
- const char *host,
- const unsigned int port,
+static int add_ssh_cache(const char *host, const unsigned int port,
const char *username, const char *client_privkey_path,
const char *client_pubkey_path,
const char *server_pubkey_path,
const uint8_t preference)
{
- struct tr_ssh_config *ssh_config;
- struct cache *cache;
- struct tr_socket *tr_socket;
+ struct tr_ssh_config *ssh_config =
+ XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config));
+ struct cache *cache =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+ struct tr_socket *tr_socket =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
struct rtr_socket *rtr_socket;
- int ret;
-
- ssh_config = XCALLOC(MTYPE_BGP_RPKI_CACHE,
- sizeof(struct tr_ssh_config));
- cache = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
- tr_socket = XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
ssh_config->port = port;
ssh_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
ssh_config->bindaddr = NULL;
- ssh_config->data = cache;
- ssh_config->new_socket = rpki_get_socket;
ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username);
- /* public key path is derived from private key path
- * by appending '.pub' to the private key name
- */
ssh_config->client_privkey_path =
XSTRDUP(MTYPE_BGP_RPKI_CACHE, client_privkey_path);
ssh_config->server_hostkey_path =
@@ -1266,14 +861,13 @@ static int add_ssh_cache(struct rpki_vrf *rpki_vrf,
rtr_socket = create_rtr_socket(tr_socket);
- cache->rpki_vrf = rpki_vrf;
cache->type = SSH;
cache->tr_socket = tr_socket;
cache->tr_config.ssh_config = ssh_config;
cache->rtr_socket = rtr_socket;
cache->preference = preference;
- ret = add_cache(cache);
+ int ret = add_cache(cache);
if (ret != SUCCESS) {
free_cache(cache);
}
@@ -1306,128 +900,53 @@ static void free_cache(struct cache *cache)
XFREE(MTYPE_BGP_RPKI_CACHE, cache);
}
-/* return true if config changed from default */
-static bool config_changed(struct rpki_vrf *rpki_vrf)
-{
- if (rpki_vrf->cache_list && listcount(rpki_vrf->cache_list))
- return true;
- if (rpki_vrf->polling_period != POLLING_PERIOD_DEFAULT)
- return true;
- if (rpki_vrf->retry_interval != RETRY_INTERVAL_DEFAULT)
- return true;
- if (rpki_vrf->expire_interval != EXPIRE_INTERVAL_DEFAULT)
- return true;
- return false;
-}
-
-static int bgp_rpki_write_debug(struct vty *vty, bool running)
-{
- if (rpki_debug_conf && running) {
- vty_out(vty, "debug rpki\n");
- return 1;
- }
- if ((rpki_debug_conf || rpki_debug_term) && !running) {
- vty_out(vty, " BGP RPKI debugging is on\n");
- return 1;
- }
- return 0;
-}
-
-static int bgp_rpki_hook_write_vrf(struct vty *vty, struct vrf *vrf)
-{
- int ret;
-
- ret = bgp_rpki_write_vrf(vty, vrf);
- if (ret == ERROR)
- return 0;
- return ret;
-}
-
-static int bgp_rpki_write_vrf(struct vty *vty, struct vrf *vrf)
+static int config_write(struct vty *vty)
{
struct listnode *cache_node;
struct cache *cache;
- struct rpki_vrf *rpki_vrf = NULL;
- char sep[STR_SEPARATOR];
- vrf_id_t vrf_id = VRF_DEFAULT;
- char *host_key_pub = NULL;
- int len_host_key_pub;
-
- if (!vrf) {
- rpki_vrf = find_rpki_vrf(NULL);
- snprintf(sep, sizeof(sep), "%s", "");
- } else if (vrf->vrf_id != VRF_DEFAULT) {
- rpki_vrf = find_rpki_vrf(vrf->name);
- snprintf(sep, sizeof(sep), "%s", " ");
- vrf_id = vrf->vrf_id;
- } else
- return ERROR;
- if (!rpki_vrf)
- return ERROR;
- if (!config_changed(rpki_vrf))
- return 0;
- if (vrf_id == VRF_DEFAULT)
- vty_out(vty, "%s!\n", sep);
- vty_out(vty, "%srpki\n", sep);
- if (rpki_vrf->polling_period != POLLING_PERIOD_DEFAULT)
- vty_out(vty, "%s rpki polling_period %d\n",
- sep, rpki_vrf->polling_period);
- if (rpki_vrf->retry_interval != RETRY_INTERVAL_DEFAULT)
- vty_out(vty, "%s rpki retry-interval %d\n",
- sep, rpki_vrf->retry_interval);
- if (rpki_vrf->expire_interval != EXPIRE_INTERVAL_DEFAULT)
- vty_out(vty, "%s rpki expire_interval %d\n",
- sep, rpki_vrf->expire_interval);
-
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) {
- switch (cache->type) {
- struct tr_tcp_config *tcp_config;
+
+ if (listcount(cache_list)) {
+ if (rpki_debug)
+ vty_out(vty, "debug rpki\n");
+
+ vty_out(vty, "!\n");
+ vty_out(vty, "rpki\n");
+ vty_out(vty, " rpki polling_period %d\n", polling_period);
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ switch (cache->type) {
+ struct tr_tcp_config *tcp_config;
#if defined(FOUND_SSH)
- struct tr_ssh_config *ssh_config;
+ struct tr_ssh_config *ssh_config;
#endif
- case TCP:
- tcp_config = cache->tr_config.tcp_config;
- vty_out(vty, "%s rpki cache %s %s ", sep,
- tcp_config->host, tcp_config->port);
- break;
+ case TCP:
+ tcp_config = cache->tr_config.tcp_config;
+ vty_out(vty, " rpki cache %s %s ",
+ tcp_config->host, tcp_config->port);
+ break;
#if defined(FOUND_SSH)
- case SSH:
- ssh_config = cache->tr_config.ssh_config;
- if (ssh_config->client_privkey_path) {
- len_host_key_pub = strlen(ssh_config->client_privkey_path) + 4 /* strlen(".pub")*/ + 1;
- host_key_pub = XCALLOC(MTYPE_BGP_RPKI_CACHE, len_host_key_pub);
- snprintf(host_key_pub, len_host_key_pub, "%s.pub", ssh_config->client_privkey_path);
- }
- vty_out(vty, "%s rpki cache %s %u %s %s %s %s ",
- sep, ssh_config->host,
- ssh_config->port,
- ssh_config->username,
- ssh_config->client_privkey_path,
- host_key_pub ? host_key_pub : "",
- ssh_config->server_hostkey_path != NULL
- ? ssh_config
- ->server_hostkey_path
- : "");
- if (host_key_pub) {
- XFREE(MTYPE_BGP_RPKI_CACHE, host_key_pub);
- host_key_pub = NULL;
- }
- break;
+ case SSH:
+ ssh_config = cache->tr_config.ssh_config;
+ vty_out(vty, " rpki cache %s %u %s %s %s ",
+ ssh_config->host, ssh_config->port,
+ ssh_config->username,
+ ssh_config->client_privkey_path,
+ ssh_config->server_hostkey_path != NULL
+ ? ssh_config
+ ->server_hostkey_path
+ : " ");
+ break;
#endif
- default:
- break;
- }
+ default:
+ break;
+ }
- vty_out(vty, "preference %hhu\n", cache->preference);
+ vty_out(vty, "preference %hhu\n", cache->preference);
+ }
+ vty_out(vty, " exit\n");
+ return 1;
+ } else {
+ return 0;
}
- vty_out(vty, "%s exit\n%s", sep,
- vrf_id == VRF_DEFAULT ? "!\n" : "");
- return 1;
-}
-
-static int config_write(struct vty *vty)
-{
- return bgp_rpki_write_vrf(vty, NULL);
}
DEFUN_NOSH (rpki,
@@ -1435,85 +954,22 @@ DEFUN_NOSH (rpki,
"rpki",
"Enable rpki and enter rpki configuration mode\n")
{
- struct rpki_vrf *rpki_vrf;
- char *vrfname = NULL;
-
- if (vty->node == CONFIG_NODE)
- vty->node = RPKI_NODE;
- else {
- struct vrf *vrf = VTY_GET_CONTEXT(vrf);
-
- vty->node = RPKI_VRF_NODE;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
- /* assume default vrf */
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf) {
- rpki_vrf = bgp_rpki_allocate(vrfname);
-
- rpki_init_sync_socket(rpki_vrf);
- }
- if (vty->node == RPKI_VRF_NODE)
- VTY_PUSH_CONTEXT_SUB(vty->node, rpki_vrf);
- else
- VTY_PUSH_CONTEXT(vty->node, rpki_vrf);
- return CMD_SUCCESS;
-}
-
-DEFUN_NOSH (no_rpki,
- no_rpki_cmd,
- "no rpki",
- NO_STR
- "Enable rpki and enter rpki configuration mode\n")
-{
- struct rpki_vrf *rpki_vrf;
- char *vrfname = NULL;
-
- if (vty->node == VRF_NODE) {
- VTY_DECLVAR_CONTEXT(vrf, vrf);
-
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
-
- rpki_vrf = find_rpki_vrf(vrfname);
-
- if (rpki_vrf)
- bgp_rpki_finish(rpki_vrf);
+ vty->node = RPKI_NODE;
return CMD_SUCCESS;
}
DEFUN (bgp_rpki_start,
bgp_rpki_start_cmd,
- "rpki start [vrf NAME]",
+ "rpki start",
RPKI_OUTPUT_STRING
- "start rpki support\n"
- VRF_CMD_HELP_STR)
-{
- struct list *cache_list = NULL;
- struct rpki_vrf *rpki_vrf;
- int idx_vrf = 3;
- struct vrf *vrf;
- char *vrfname = NULL;
-
- if (argc == 4) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf)
- return CMD_SUCCESS;
- cache_list = rpki_vrf->cache_list;
- if (!cache_list || listcount(cache_list) == 0)
- vty_out(vty, "Could not start rpki"
- " because no caches are configured\n");
+ "start rpki support\n")
+{
+ if (listcount(cache_list) == 0)
+ vty_out(vty,
+ "Could not start rpki because no caches are configured\n");
- if (!is_running(rpki_vrf)) {
- if (start(rpki_vrf) == ERROR) {
+ if (!is_running()) {
+ if (start() == ERROR) {
RPKI_DEBUG("RPKI failed to start");
return CMD_WARNING;
}
@@ -1523,26 +979,12 @@ DEFUN (bgp_rpki_start,
DEFUN (bgp_rpki_stop,
bgp_rpki_stop_cmd,
- "rpki stop [vrf NAME]",
+ "rpki stop",
RPKI_OUTPUT_STRING
- "start rpki support\n"
- VRF_CMD_HELP_STR)
+ "start rpki support\n")
{
- int idx_vrf = 3;
- struct vrf *vrf;
- char *vrfname = NULL;
- struct rpki_vrf *rpki_vrf;
-
- if (argc == 4) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
- rpki_vrf = find_rpki_vrf(vrfname);
- if (rpki_vrf && is_running(rpki_vrf))
- stop(rpki_vrf);
+ if (is_running())
+ stop();
return CMD_SUCCESS;
}
@@ -1554,14 +996,7 @@ DEFPY (rpki_polling_period,
"Set polling period\n"
"Polling period value\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- rpki_vrf->polling_period = pp;
+ polling_period = pp;
return CMD_SUCCESS;
}
@@ -1572,14 +1007,7 @@ DEFUN (no_rpki_polling_period,
RPKI_OUTPUT_STRING
"Set polling period back to default\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- rpki_vrf->polling_period = POLLING_PERIOD_DEFAULT;
+ polling_period = POLLING_PERIOD_DEFAULT;
return CMD_SUCCESS;
}
@@ -1590,15 +1018,8 @@ DEFPY (rpki_expire_interval,
"Set expire interval\n"
"Expire interval value\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- if ((unsigned int)tmp >= rpki_vrf->polling_period) {
- rpki_vrf->expire_interval = tmp;
+ if ((unsigned int)tmp >= polling_period) {
+ expire_interval = tmp;
return CMD_SUCCESS;
}
@@ -1613,14 +1034,7 @@ DEFUN (no_rpki_expire_interval,
RPKI_OUTPUT_STRING
"Set expire interval back to default\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- rpki_vrf->expire_interval = rpki_vrf->polling_period * 2;
+ expire_interval = polling_period * 2;
return CMD_SUCCESS;
}
@@ -1631,14 +1045,7 @@ DEFPY (rpki_retry_interval,
"Set retry interval\n"
"retry interval value\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- rpki_vrf->retry_interval = tmp;
+ retry_interval = tmp;
return CMD_SUCCESS;
}
@@ -1649,14 +1056,7 @@ DEFUN (no_rpki_retry_interval,
RPKI_OUTPUT_STRING
"Set retry interval back to default\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- rpki_vrf->retry_interval = RETRY_INTERVAL_DEFAULT;
+ retry_interval = RETRY_INTERVAL_DEFAULT;
return CMD_SUCCESS;
}
@@ -1723,9 +1123,7 @@ DEFUN_HIDDEN (no_rpki_synchronisation_timeout,
DEFPY (rpki_cache,
rpki_cache_cmd,
- "rpki cache <A.B.C.D|WORD>"
- "<TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> "
- "preference (1-255)",
+ "rpki cache <A.B.C.D|WORD><TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY SSH_PUBKEY [SERVER_PUBKEY]> preference (1-255)",
RPKI_OUTPUT_STRING
"Install a cache server to current group\n"
"IP address of cache server\n Hostname of cache server\n"
@@ -1741,55 +1139,30 @@ DEFPY (rpki_cache,
int return_value;
struct listnode *cache_node;
struct cache *current_cache;
- char *pub = NULL;
- struct rpki_vrf *rpki_vrf;
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- if (!rpki_vrf->cache_list)
- return CMD_WARNING;
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node,
- current_cache)) {
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) {
if (current_cache->preference == preference) {
- vty_out(vty, "Cache with preference %ld "
- "is already configured\n",
+ vty_out(vty,
+ "Cache with preference %ld is already configured\n",
preference);
return CMD_WARNING;
}
}
+
// use ssh connection
if (ssh_uname) {
#if defined(FOUND_SSH)
- if (ssh_privkey && ssh_pubkey) {
- pub = XCALLOC(MTYPE_BGP_RPKI_CACHE,
- strlen(ssh_privkey) + 5);
- snprintf(pub, strlen(ssh_privkey) + 5, "%s.pub",
- ssh_privkey);
- if (!strmatch(pub, ssh_pubkey)) {
- vty_out(vty,
- "ssh public key overriden: %s.pub\n",
- ssh_privkey);
- }
- }
return_value =
- add_ssh_cache(rpki_vrf, cache, sshport, ssh_uname, ssh_privkey,
- pub, server_pubkey, preference);
- if (pub)
- XFREE(MTYPE_BGP_RPKI_CACHE, pub);
+ add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey,
+ ssh_pubkey, server_pubkey, preference);
#else
return_value = SUCCESS;
vty_out(vty,
- "ssh sockets are not supported. "
- "Please recompile rtrlib and frr with ssh support. "
- "If you want to use it\n");
+ "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n");
#endif
} else { // use tcp connection
- return_value = add_tcp_cache(rpki_vrf, cache, tcpport,
- preference);
+ return_value = add_tcp_cache(cache, tcpport, preference);
}
if (return_value == ERROR) {
@@ -1812,27 +1185,17 @@ DEFPY (no_rpki_cache,
"Preference of the cache server\n"
"Preference value\n")
{
- struct cache *cache_p;
- struct list *cache_list = NULL;
- struct rpki_vrf *rpki_vrf;
+ struct cache *cache_p = find_cache(preference);
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
-
- cache_list = rpki_vrf->cache_list;
- cache_p = find_cache(preference, cache_list);
- if (!rpki_vrf || !cache_p) {
+ if (!cache_p) {
vty_out(vty, "Could not find cache %ld\n", preference);
return CMD_WARNING;
}
- if (rpki_vrf->rtr_is_running && listcount(rpki_vrf->cache_list) == 1) {
- stop(rpki_vrf);
- } else if (rpki_vrf->rtr_is_running) {
- if (rtr_mgr_remove_group(rpki_vrf->rtr_config, preference)
- == RTR_ERROR) {
+ if (rtr_is_running && listcount(cache_list) == 1) {
+ stop();
+ } else if (rtr_is_running) {
+ if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {
vty_out(vty, "Could not remove cache %ld", preference);
vty_out(vty, "\n");
@@ -1848,42 +1211,21 @@ DEFPY (no_rpki_cache,
DEFUN (show_rpki_prefix_table,
show_rpki_prefix_table_cmd,
- "show rpki prefix-table [vrf NAME]",
+ "show rpki prefix-table",
SHOW_STR
RPKI_OUTPUT_STRING
- "Show validated prefixes which were received from RPKI Cache\n"
- VRF_CMD_HELP_STR)
+ "Show validated prefixes which were received from RPKI Cache\n")
{
struct listnode *cache_node;
struct cache *cache;
- struct rpki_vrf *rpki_vrf;
- int idx_vrf = 4;
- struct vrf *vrf;
- char *vrfname = NULL;
-
- if (argc == 5) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf)
- return CMD_SUCCESS;
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) {
- if (cache->type == TCP)
- vty_out(vty, "host: %s port: %s\n",
- cache->tr_config.tcp_config->host,
- cache->tr_config.tcp_config->port);
- else
- vty_out(vty, "host: %s port: %u SSH\n",
- cache->tr_config.ssh_config->host,
- cache->tr_config.ssh_config->port);
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ vty_out(vty, "host: %s port: %s\n",
+ cache->tr_config.tcp_config->host,
+ cache->tr_config.tcp_config->port);
}
- if (is_synchronized(rpki_vrf))
- print_prefix_table(vty, rpki_vrf);
+ if (is_synchronized())
+ print_prefix_table(vty);
else
vty_out(vty, "No connection to RPKI cache server.\n");
@@ -1891,58 +1233,32 @@ DEFUN (show_rpki_prefix_table,
}
DEFPY(show_rpki_as_number, show_rpki_as_number_cmd,
- "show rpki as-number (1-4294967295)$by_asn [vrf NAME$vrfname]",
+ "show rpki as-number (1-4294967295)$by_asn",
SHOW_STR RPKI_OUTPUT_STRING
"Lookup by ASN in prefix table\n"
"AS Number\n")
{
- struct rpki_vrf *rpki_vrf;
- char *vrf_name = NULL;
- struct vrf *vrf;
-
- if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) {
- vrf = vrf_lookup_by_name(vrfname);
- if (!vrf)
- return CMD_SUCCESS;
- vrf_name = vrf->name;
- }
- /* assume default vrf */
- rpki_vrf = find_rpki_vrf(vrf_name);
-
- if (!is_synchronized(rpki_vrf)) {
+ if (!is_synchronized()) {
vty_out(vty, "No Connection to RPKI cache server.\n");
return CMD_WARNING;
}
- print_prefix_table_by_asn(vty, by_asn, rpki_vrf);
+ print_prefix_table_by_asn(vty, by_asn);
return CMD_SUCCESS;
}
DEFPY (show_rpki_prefix,
show_rpki_prefix_cmd,
- "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [vrf NAME$vrfname]",
+ "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn]",
SHOW_STR
RPKI_OUTPUT_STRING
"Lookup IP prefix and optionally ASN in prefix table\n"
"IPv4 prefix\n"
"IPv6 prefix\n"
- "AS Number\n"
- VRF_CMD_HELP_STR)
+ "AS Number\n")
{
- struct rpki_vrf *rpki_vrf;
- struct vrf *vrf;
- char *vrf_name = NULL;
- if (vrfname && !strmatch(vrfname, VRF_DEFAULT_NAME)) {
- vrf = vrf_lookup_by_name(vrfname);
- if (!vrf)
- return CMD_SUCCESS;
- vrf_name = vrf->name;
- }
-
- rpki_vrf = find_rpki_vrf(vrf_name);
-
- if (!rpki_vrf || !is_synchronized(rpki_vrf)) {
+ if (!is_synchronized()) {
vty_out(vty, "No Connection to RPKI cache server.\n");
return CMD_WARNING;
}
@@ -1963,9 +1279,8 @@ DEFPY (show_rpki_prefix,
unsigned int match_count = 0;
enum pfxv_state result;
- if (pfx_table_validate_r(rpki_vrf->rtr_config->pfx_table, &matches,
- &match_count, asn, &addr,
- prefix->prefixlen, &result)
+ if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
+ asn, &addr, prefix->prefixlen, &result)
!= PFX_SUCCESS) {
vty_out(vty, "Prefix lookup failed");
return CMD_WARNING;
@@ -1987,32 +1302,15 @@ DEFPY (show_rpki_prefix,
DEFUN (show_rpki_cache_server,
show_rpki_cache_server_cmd,
- "show rpki cache-server [vrf NAME]",
+ "show rpki cache-server",
SHOW_STR
RPKI_OUTPUT_STRING
- "SHOW configured cache server\n"
- VRF_CMD_HELP_STR)
+ "SHOW configured cache server\n")
{
struct listnode *cache_node;
struct cache *cache;
- struct rpki_vrf *rpki_vrf;
- int idx_vrf = 4;
- struct vrf *vrf;
- char *vrfname = NULL;
-
- if (argc == 5) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf)
- return CMD_SUCCESS;
-
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list, cache_node, cache)) {
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
if (cache->type == TCP) {
vty_out(vty, "host: %s port: %s\n",
cache->tr_config.tcp_config->host,
@@ -2021,8 +1319,7 @@ DEFUN (show_rpki_cache_server,
#if defined(FOUND_SSH)
} else if (cache->type == SSH) {
vty_out(vty,
- "host: %s port: %d username: %s "
- "server_hostkey_path: %s client_privkey_path: %s\n",
+ "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s\n",
cache->tr_config.ssh_config->host,
cache->tr_config.ssh_config->port,
cache->tr_config.ssh_config->username,
@@ -2039,41 +1336,22 @@ DEFUN (show_rpki_cache_server,
DEFUN (show_rpki_cache_connection,
show_rpki_cache_connection_cmd,
- "show rpki cache-connection [vrf NAME]",
+ "show rpki cache-connection",
SHOW_STR
RPKI_OUTPUT_STRING
- "Show to which RPKI Cache Servers we have a connection\n"
- VRF_CMD_HELP_STR)
+ "Show to which RPKI Cache Servers we have a connection\n")
{
- struct rpki_vrf *rpki_vrf;
- int idx_vrf = 4;
- struct vrf *vrf;
- char *vrfname = NULL;
-
- if (argc == 5) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
-
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf)
- return CMD_SUCCESS;
-
- if (is_synchronized(rpki_vrf)) {
+ if (is_synchronized()) {
struct listnode *cache_node;
struct cache *cache;
- struct rtr_mgr_group *group = get_connected_group(rpki_vrf);
+ struct rtr_mgr_group *group = get_connected_group();
- if (!group || !rpki_vrf->cache_list) {
+ if (!group) {
vty_out(vty, "Cannot find a connected group.\n");
return CMD_SUCCESS;
}
vty_out(vty, "Connected to group %d\n", group->preference);
- for (ALL_LIST_ELEMENTS_RO(rpki_vrf->cache_list,
- cache_node, cache)) {
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
if (cache->preference == group->preference) {
struct tr_tcp_config *tcp_config;
#if defined(FOUND_SSH)
@@ -2115,52 +1393,9 @@ DEFUN (show_rpki_cache_connection,
return CMD_SUCCESS;
}
-DEFUN (show_rpki_configuration,
- show_rpki_configuration_cmd,
- "show rpki configuration [vrf NAME]",
- SHOW_STR
- RPKI_OUTPUT_STRING
- "Show RPKI configuration\n"
- VRF_CMD_HELP_STR)
-{
- struct rpki_vrf *rpki_vrf;
- int idx_vrf = 4;
- struct vrf *vrf;
- char *vrfname = NULL;
-
- if (argc == 5) {
- vrf = vrf_lookup_by_name(argv[idx_vrf]->arg);
- if (!vrf)
- return CMD_SUCCESS;
- if (vrf->vrf_id != VRF_DEFAULT)
- vrfname = vrf->name;
- }
-
- rpki_vrf = find_rpki_vrf(vrfname);
- if (!rpki_vrf)
- return CMD_SUCCESS;
- vty_out(vty, "rpki is %s",
- listcount(rpki_vrf->cache_list) ? "Enabled" : "Disabled");
- if (!listcount(rpki_vrf->cache_list))
- return CMD_SUCCESS;
- vty_out(vty, " (%d cache servers configured)",
- listcount(rpki_vrf->cache_list));
- vty_out(vty, "\n");
- vty_out(vty, "\tpolling period %d\n", rpki_vrf->polling_period);
- vty_out(vty, "\tretry interval %d\n", rpki_vrf->retry_interval);
- vty_out(vty, "\texpire interval %d\n", rpki_vrf->expire_interval);
- return CMD_SUCCESS;
-}
-
static int config_on_exit(struct vty *vty)
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
- reset(false, rpki_vrf);
+ reset(false);
return 1;
}
@@ -2170,13 +1405,7 @@ DEFUN (rpki_reset,
RPKI_OUTPUT_STRING
"reset rpki\n")
{
- struct rpki_vrf *rpki_vrf;
-
- if (vty->node == RPKI_VRF_NODE)
- rpki_vrf = VTY_GET_CONTEXT_SUB(rpki_vrf);
- else
- rpki_vrf = VTY_GET_CONTEXT(rpki_vrf);
- return reset(true, rpki_vrf) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+ return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
}
DEFUN (debug_rpki,
@@ -2185,10 +1414,7 @@ DEFUN (debug_rpki,
DEBUG_STR
"Enable debugging for rpki\n")
{
- if (vty->node == CONFIG_NODE)
- rpki_debug_conf = 1;
- else
- rpki_debug_term = 1;
+ rpki_debug = 1;
return CMD_SUCCESS;
}
@@ -2199,10 +1425,7 @@ DEFUN (no_debug_rpki,
DEBUG_STR
"Disable debugging for rpki\n")
{
- if (vty->node == CONFIG_NODE)
- rpki_debug_conf = 0;
- else
- rpki_debug_term = 0;
+ rpki_debug = 0;
return CMD_SUCCESS;
}
@@ -2272,10 +1495,8 @@ static void install_cli_commands(void)
// TODO: make config write work
install_node(&rpki_node);
install_default(RPKI_NODE);
- install_node(&rpki_vrf_node);
- install_default(RPKI_VRF_NODE);
install_element(CONFIG_NODE, &rpki_cmd);
- install_element(CONFIG_NODE, &no_rpki_cmd);
+ install_element(ENABLE_NODE, &rpki_cmd);
install_element(ENABLE_NODE, &bgp_rpki_start_cmd);
install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
@@ -2307,43 +1528,12 @@ static void install_cli_commands(void)
install_element(RPKI_NODE, &rpki_cache_cmd);
install_element(RPKI_NODE, &no_rpki_cache_cmd);
- /* RPKI_VRF_NODE commands */
- install_element(VRF_NODE, &rpki_cmd);
- install_element(VRF_NODE, &no_rpki_cmd);
- /* Install rpki reset command */
- install_element(RPKI_VRF_NODE, &rpki_reset_cmd);
-
- /* Install rpki polling period commands */
- install_element(RPKI_VRF_NODE, &rpki_polling_period_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_polling_period_cmd);
-
- /* Install rpki expire interval commands */
- install_element(RPKI_VRF_NODE, &rpki_expire_interval_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_expire_interval_cmd);
-
- /* Install rpki retry interval commands */
- install_element(RPKI_VRF_NODE, &rpki_retry_interval_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_retry_interval_cmd);
-
- /* Install rpki timeout commands */
- install_element(RPKI_VRF_NODE, &rpki_timeout_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_timeout_cmd);
-
- /* Install rpki synchronisation timeout commands */
- install_element(RPKI_VRF_NODE, &rpki_synchronisation_timeout_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_synchronisation_timeout_cmd);
-
- /* Install rpki cache commands */
- install_element(RPKI_VRF_NODE, &rpki_cache_cmd);
- install_element(RPKI_VRF_NODE, &no_rpki_cache_cmd);
-
/* Install show commands */
install_element(VIEW_NODE, &show_rpki_prefix_table_cmd);
install_element(VIEW_NODE, &show_rpki_cache_connection_cmd);
install_element(VIEW_NODE, &show_rpki_cache_server_cmd);
install_element(VIEW_NODE, &show_rpki_prefix_cmd);
install_element(VIEW_NODE, &show_rpki_as_number_cmd);
- install_element(VIEW_NODE, &show_rpki_configuration_cmd);
/* Install debug commands */
install_element(CONFIG_NODE, &debug_rpki_cmd);
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 719ff1452b..303f4ca56e 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -356,17 +356,16 @@ static struct peer *peer_lookup_addr_ipv4(struct in_addr *src)
struct bgp *bgp;
struct peer *peer;
struct listnode *node;
+ struct listnode *bgpnode;
- bgp = bgp_get_default();
- if (!bgp)
- return NULL;
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
- for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- if (sockunion_family(&peer->su) != AF_INET)
- continue;
-
- if (sockunion2ip(&peer->su) == src->s_addr)
- return peer;
+ if (sockunion2ip(&peer->su) == src->s_addr)
+ return peer;
+ }
}
return NULL;
@@ -378,21 +377,20 @@ static struct peer *bgp_peer_lookup_next(struct in_addr *src)
struct peer *peer;
struct peer *next_peer = NULL;
struct listnode *node;
-
- bgp = bgp_get_default();
- if (!bgp)
- return NULL;
-
- for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- if (sockunion_family(&peer->su) != AF_INET)
- continue;
- if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr))
- continue;
-
- if (!next_peer
- || ntohl(sockunion2ip(&next_peer->su))
- > ntohl(sockunion2ip(&peer->su))) {
- next_peer = peer;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+ if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr))
+ continue;
+
+ if (!next_peer
+ || ntohl(sockunion2ip(&next_peer->su))
+ > ntohl(sockunion2ip(&peer->su))) {
+ next_peer = peer;
+ }
}
}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index a9ec36d29b..cf0086b52e 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -51,6 +51,8 @@ enum bgp_path_selection_reason {
bgp_path_selection_first,
bgp_path_selection_evpn_sticky_mac,
bgp_path_selection_evpn_seq,
+ bgp_path_selection_evpn_local_path,
+ bgp_path_selection_evpn_non_proxy,
bgp_path_selection_evpn_lower_ip,
bgp_path_selection_weight,
bgp_path_selection_local_pref,
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index 426b84f91f..ace18000f8 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -1075,10 +1075,7 @@ static void update_subgroup_merge(struct update_subgroup *subgrp,
SUBGRP_INCR_STAT(target, merge_events);
if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " (%d peers) merged into u%" PRIu64 ":s%" PRIu64
- ", "
- "trigger: %s",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" (%d peers) merged into u%" PRIu64 ":s%" PRIu64", trigger: %s",
subgrp->update_group->id, subgrp->id, peer_count,
target->update_group->id, target->id,
reason ? reason : "unknown");
@@ -1368,8 +1365,7 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg)
if (changed) {
if (bgp_debug_update(NULL, NULL, updgrp, 0))
zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " announcing routes upon policy %s (type %d) change",
+ "u%" PRIu64 ":s%" PRIu64" announcing routes upon policy %s (type %d) change",
updgrp->id, subgrp->id,
ctx->policy_name, ctx->policy_type);
subgroup_announce_route(subgrp);
@@ -1377,8 +1373,7 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg)
if (def_changed) {
if (bgp_debug_update(NULL, NULL, updgrp, 0))
zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " announcing default upon default routemap %s change",
+ "u%" PRIu64 ":s%" PRIu64" announcing default upon default routemap %s change",
updgrp->id, subgrp->id,
ctx->policy_name);
subgroup_default_originate(subgrp, 0);
@@ -1484,8 +1479,7 @@ void update_subgroup_split_peer(struct peer_af *paf,
UPDGRP_PEER_DBG_EN(updgrp);
}
if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " peer %s moved to u%" PRIu64 ":s%" PRIu64,
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s moved to u%" PRIu64 ":s%" PRIu64,
old_id, subgrp->id, paf->peer->host,
updgrp->id, subgrp->id);
@@ -1519,9 +1513,7 @@ void update_subgroup_split_peer(struct peer_af *paf,
update_subgroup_copy_packets(subgrp, paf->next_pkt_to_send);
if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " peer %s split and moved into u%" PRIu64
- ":s%" PRIu64,
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s split and moved into u%" PRIu64":s%" PRIu64,
paf->subgroup->update_group->id, paf->subgroup->id,
paf->peer->host, updgrp->id, subgrp->id);
@@ -1837,8 +1829,7 @@ void peer_af_announce_route(struct peer_af *paf, int combine)
assert(subgrp && subgrp->update_group);
if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s announcing routes",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s announcing routes",
subgrp->update_group->id, subgrp->id,
paf->peer->host);
@@ -1859,8 +1850,7 @@ void peer_af_announce_route(struct peer_af *paf, int combine)
}
if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " announcing routes to %s, combined into %d peers",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes to %s, combined into %d peers",
subgrp->update_group->id, subgrp->id,
paf->peer->host, subgrp->peer_count);
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index e1c2865220..ecdcaa38b8 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -246,8 +246,7 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp,
if (adj->subgroup == subgrp) {
if (header1) {
vty_out(vty,
- "BGP table version is %" PRIu64
- ", local router ID is %s\n",
+ "BGP table version is %" PRIu64", local router ID is %s\n",
table->version,
inet_ntoa(bgp->router_id));
vty_out(vty, BGP_SHOW_SCODE_HEADER);
@@ -263,14 +262,15 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp,
route_vty_out_tmp(vty, dest_p,
adj->adv->baa->attr,
SUBGRP_SAFI(subgrp),
- 0, NULL);
+ 0, NULL, false);
output_count++;
}
if ((flags & UPDWALK_FLAGS_ADVERTISED)
&& adj->attr) {
- route_vty_out_tmp(
- vty, dest_p, adj->attr,
- SUBGRP_SAFI(subgrp), 0, NULL);
+ route_vty_out_tmp(vty, dest_p,
+ adj->attr,
+ SUBGRP_SAFI(subgrp),
+ 0, NULL, false);
output_count++;
}
}
@@ -314,8 +314,7 @@ static int subgroup_coalesce_timer(struct thread *thread)
subgrp = THREAD_ARG(thread);
if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " announcing routes upon coalesce timer expiry(%u ms)",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes upon coalesce timer expiry(%u ms)",
(SUBGRP_UPDGRP(subgrp))->id, subgrp->id,
subgrp->v_coalesce);
subgrp->t_coalesce = NULL;
@@ -896,8 +895,7 @@ void subgroup_announce_all(struct update_subgroup *subgrp)
*/
if (!subgrp->v_coalesce) {
if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " announcing all routes",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing all routes",
subgrp->update_group->id, subgrp->id);
subgroup_announce_route(subgrp);
return;
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index f87e372540..f706b834fe 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -488,8 +488,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
stream_put_in_addr_at(s, offset_nh, mod_v4nh);
if (bgp_debug_update(peer, NULL, NULL, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ nexthop %s%s",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ nexthop %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id, peer->host,
inet_ntoa(*mod_v4nh),
@@ -575,6 +574,18 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
gnh_modified = 1;
}
+ if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) {
+ if (peer->nexthop.v4.s_addr) {
+ ipv4_to_ipv4_mapped_ipv6(mod_v6nhg,
+ peer->nexthop.v4);
+ }
+ }
+
+ if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
+
if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
|| nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
stream_get_from(&v6nhlocal, s, offset_nhlocal,
@@ -594,8 +605,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
|| nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ mp_nexthops %s, %s%s",
+ "u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ mp_nexthops %s, %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id, peer->host,
inet_ntop(AF_INET6, mod_v6nhg, buf,
@@ -606,8 +616,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
? " and RD"
: ""));
else
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ mp_nexthop %s%s",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ mp_nexthop %s%s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id, peer->host,
inet_ntop(AF_INET6, mod_v6nhg, buf,
@@ -633,8 +642,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
stream_put_in_addr_at(s, vec->offset + 1, mod_v4nh);
if (bgp_debug_update(peer, NULL, NULL, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " %s send UPDATE w/ nexthop %s",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ nexthop %s",
PAF_SUBGRP(paf)->update_group->id,
PAF_SUBGRP(paf)->id, peer->host,
inet_ntoa(*mod_v4nh));
@@ -750,8 +758,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
if (BGP_DEBUG(update, UPDATE_OUT)
|| BGP_DEBUG(update, UPDATE_PREFIX)) {
zlog_debug(
- "%s reached maximum prefix to be send (%" PRIu32
- ")",
+ "%s reached maximum prefix to be send (%u)",
peer->host, peer->pmax_out[afi][safi]);
}
goto next;
@@ -815,8 +822,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
if (space_remaining < space_needed) {
flog_err(
EC_BGP_UPDGRP_ATTR_LEN,
- "u%" PRIu64 ":s%" PRIu64
- " attributes too long, cannot send UPDATE",
+ "u%" PRIu64 ":s%" PRIu64" attributes too long, cannot send UPDATE",
subgrp->update_group->id, subgrp->id);
/* Flush the FIFO update queue */
@@ -831,7 +837,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
memset(send_attr_str, 0, BUFSIZ);
send_attr_printed = 0;
bgp_dump_attr(adv->baa->attr, send_attr_str,
- BUFSIZ);
+ sizeof(send_attr_str));
}
}
@@ -872,8 +878,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
char pfx_buf[BGP_PRD_PATH_STRLEN];
if (!send_attr_printed) {
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " send UPDATE w/ attr: %s",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE w/ attr: %s",
subgrp->update_group->id, subgrp->id,
send_attr_str);
if (!stream_empty(snlri)) {
@@ -883,8 +888,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
pkt_afi = afi_int2iana(afi);
pkt_safi = safi_int2iana(safi);
zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " send MP_REACH for afi/safi %d/%d",
+ "u%" PRIu64 ":s%" PRIu64" send MP_REACH for afi/safi %d/%d",
subgrp->update_group->id,
subgrp->id, pkt_afi, pkt_safi);
}
@@ -928,8 +932,7 @@ next:
packet = stream_dup(s);
bgp_packet_set_size(packet);
if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " send UPDATE len %zd numpfx %d",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE len %zd numpfx %d",
subgrp->update_group->id, subgrp->id,
(stream_get_endp(packet)
- stream_get_getp(packet)),
@@ -1043,8 +1046,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
if (bgp_debug_update(NULL, NULL,
subgrp->update_group, 0))
zlog_debug(
- "u%" PRIu64 ":s%" PRIu64
- " send MP_UNREACH for afi/safi %d/%d",
+ "u%" PRIu64 ":s%" PRIu64" send MP_UNREACH for afi/safi %d/%d",
subgrp->update_group->id,
subgrp->id, pkt_afi, pkt_safi);
}
@@ -1062,8 +1064,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0,
addpath_encode, addpath_tx_id,
pfx_buf, sizeof(pfx_buf));
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " send UPDATE %s -- unreachable",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable",
subgrp->update_group->id, subgrp->id,
pfx_buf);
}
@@ -1091,8 +1092,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
}
bgp_packet_set_size(s);
if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " send UPDATE (withdraw) len %zd numpfx %d",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE (withdraw) len %zd numpfx %d",
subgrp->update_group->id, subgrp->id,
(stream_get_endp(s) - stream_get_getp(s)),
num_pfx);
@@ -1147,7 +1147,7 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp,
attrstr[0] = '\0';
- bgp_dump_attr(attr, attrstr, BUFSIZ);
+ bgp_dump_attr(attr, attrstr, sizeof(attrstr));
if (addpath_encode)
snprintf(tx_id_buf, sizeof(tx_id_buf),
@@ -1235,8 +1235,7 @@ void subgroup_default_withdraw_packet(struct update_subgroup *subgrp)
" with addpath ID %u",
BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
- zlog_debug("u%" PRIu64 ":s%" PRIu64
- " send UPDATE %s%s -- unreachable",
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s%s -- unreachable",
(SUBGRP_UPDGRP(subgrp))->id, subgrp->id,
prefix2str(&p, buf, sizeof(buf)), tx_id_buf);
}
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
index c8b8e2391d..0b5d156e6d 100644
--- a/bgpd/bgp_vpn.c
+++ b/bgpd/bgp_vpn.c
@@ -226,7 +226,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
rd_header = 0;
}
route_vty_out_tmp(vty, bgp_dest_get_prefix(rm), attr,
- safi, use_json, json_routes);
+ safi, use_json, json_routes, false);
output_count++;
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index fe52b73438..af02e13340 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -65,6 +65,7 @@
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_flowspec.h"
@@ -1093,7 +1094,8 @@ DEFUN_HIDDEN (bgp_local_mac,
return CMD_WARNING;
}
- rv = bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, 0 /* flags */, seq);
+ rv = bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, 0 /* flags */, seq,
+ zero_esi);
if (rv < 0) {
vty_out(vty, "Internal error\n");
return CMD_WARNING;
@@ -4330,8 +4332,7 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str,
*/
if (peer->conf_if && (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)) {
vty_out(vty,
- "%s is directly connected peer, cannot accept disable-"
- "connected-check\n",
+ "%s is directly connected peer, cannot accept disable-connected-check\n",
ip_str);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -4389,7 +4390,7 @@ DEFUN (neighbor_shutdown_msg,
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Administratively shut down this neighbor\n"
- "Add a shutdown message (draft-ietf-idr-shutdown-06)\n"
+ "Add a shutdown message (RFC 8203)\n"
"Shutdown message\n")
{
int idx_peer = 1;
@@ -4421,7 +4422,7 @@ DEFUN (no_neighbor_shutdown_msg,
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Administratively shut down this neighbor\n"
- "Remove a shutdown message (draft-ietf-idr-shutdown-06)\n"
+ "Remove a shutdown message (RFC 8203)\n"
"Shutdown message\n")
{
int idx_peer = 2;
@@ -6703,7 +6704,8 @@ 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)
+ const char *restart_str,
+ const char *force_str)
{
int ret;
struct peer *peer;
@@ -6727,7 +6729,7 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str,
restart = 0;
ret = peer_maximum_prefix_set(peer, afi, safi, max, threshold, warning,
- restart);
+ restart, force_str ? true : false);
return bgp_vty_return(vty, ret);
}
@@ -6798,172 +6800,220 @@ DEFUN(no_neighbor_maximum_prefix_out,
return CMD_SUCCESS;
}
-/* Maximum number of prefix configuration. prefix count is different
- for each peer configuration. So this configuration can be set for
+/* 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 <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
- "maximum no. of prefix limit\n")
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 0, NULL);
+ argv[idx_number]->arg, NULL, 0, NULL, force);
}
ALIAS_HIDDEN(neighbor_maximum_prefix, neighbor_maximum_prefix_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
- "maximum no. of prefix limit\n")
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold,
neighbor_maximum_prefix_threshold_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
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")
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL);
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold,
neighbor_maximum_prefix_threshold_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
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")
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_warning,
neighbor_maximum_prefix_warning_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
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")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 1, NULL);
+ argv[idx_number]->arg, NULL, 1, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_warning,
neighbor_maximum_prefix_warning_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
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")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold_warning,
neighbor_maximum_prefix_threshold_warning_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
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")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL);
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold_warning,
neighbor_maximum_prefix_threshold_warning_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
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")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_restart,
neighbor_maximum_prefix_restart_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
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\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 5;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg);
+ argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_restart,
neighbor_maximum_prefix_restart_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
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\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold_restart,
neighbor_maximum_prefix_threshold_restart_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to 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\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
int idx_number_3 = 6;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
argv[idx_number]->arg, argv[idx_number_2]->arg, 0,
- argv[idx_number_3]->arg);
+ argv[idx_number_3]->arg, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold_restart,
neighbor_maximum_prefix_threshold_restart_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to 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\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (no_neighbor_maximum_prefix,
no_neighbor_maximum_prefix_cmd,
- "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]",
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
@@ -6972,7 +7022,8 @@ DEFUN (no_neighbor_maximum_prefix,
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
"Restart interval in minutes\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 2;
return peer_maximum_prefix_unset_vty(vty, argv[idx_peer]->arg,
@@ -6982,14 +7033,15 @@ DEFUN (no_neighbor_maximum_prefix,
ALIAS_HIDDEN(
no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_hidden_cmd,
- "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]",
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to 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\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
/* "neighbor allowas-in" */
@@ -9352,8 +9404,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
memory_order_relaxed);
vty_out(vty,
- "4 %10u %9u %9u %8" PRIu64
- " %4zu %4zu %8s",
+ "4 %10u %9u %9u %8" PRIu64" %4zu %4zu %8s",
peer->as, PEER_TOTAL_RX(peer),
PEER_TOTAL_TX(peer),
peer->version[afi][safi], inq_count,
@@ -9372,7 +9423,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
"(Policy)");
else
vty_out(vty,
- " %12" PRIu32,
+ " %12u",
peer->pcount
[afi]
[pfx_rcd_safi]);
@@ -9390,7 +9441,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
"(Policy)");
else
vty_out(vty,
- " %8" PRIu32,
+ " %8u",
(PAF_SUBGRP(
paf))
->scount);
@@ -9407,7 +9458,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
lookup_msg(bgp_status_msg,
peer->status, NULL));
- vty_out(vty, " %8" PRIu32, 0);
+ vty_out(vty, " %8u", 0);
}
vty_out(vty, "\n");
}
@@ -10430,8 +10481,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
paf = peer_af_find(p, afi, safi);
if (paf && PAF_SUBGRP(paf)) {
- vty_out(vty, " Update group %" PRIu64
- ", subgroup %" PRIu64 "\n",
+ vty_out(vty, " Update group %" PRIu64", subgroup %" PRIu64 "\n",
PAF_UPDGRP(paf)->id, PAF_SUBGRP(paf)->id);
vty_out(vty, " Packet Queue length %d\n",
bpacket_queue_virtual_length(paf));
@@ -10682,20 +10732,20 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
filter->usmap.name);
/* Receive prefix count */
- vty_out(vty, " %" PRIu32 " accepted prefixes\n",
+ vty_out(vty, " %u accepted prefixes\n",
p->pcount[afi][safi]);
/* maximum-prefix-out */
if (CHECK_FLAG(p->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_OUT))
vty_out(vty,
- " Maximum allowed prefixes sent %" PRIu32 "\n",
+ " Maximum allowed prefixes sent %u\n",
p->pmax_out[afi][safi]);
/* Maximum prefix */
if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
vty_out(vty,
- " Maximum prefixes allowed %" PRIu32 "%s\n",
+ " Maximum prefixes allowed %u%s\n",
p->pmax[afi][safi],
CHECK_FLAG(p->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING)
@@ -10884,10 +10934,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
p->group, &prefix);
if (range) {
- prefix2str(range, buf1, sizeof(buf1));
vty_out(vty,
- " Belongs to the subnet range group: %s\n",
- buf1);
+ " Belongs to the subnet range group: %pFX\n",
+ range);
}
}
}
@@ -12697,11 +12746,13 @@ static void bgp_show_all_instances_neighbors_vty(struct vty *vty,
use_json, json);
}
json_object_free(json);
+ json = NULL;
}
if (use_json) {
vty_out(vty, "}\n");
- json_object_free(json);
+ if (json)
+ json_object_free(json);
}
else if (!nbr_output)
vty_out(vty, "%% BGP instance not found\n");
@@ -13522,10 +13573,10 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group)
conf = group->conf;
if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) {
- vty_out(vty, "\nBGP peer-group %s, remote AS %" PRIu32 "\n",
+ vty_out(vty, "\nBGP peer-group %s, remote AS %u\n",
group->name, conf->as);
} else if (conf->as_type == AS_INTERNAL) {
- vty_out(vty, "\nBGP peer-group %s, remote AS %" PRIu32 "\n",
+ vty_out(vty, "\nBGP peer-group %s, remote AS %u\n",
group->name, group->bgp->as);
} else {
vty_out(vty, "\nBGP peer-group %s\n", group->name);
@@ -15044,7 +15095,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
/* maximum-prefix. */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
- vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr,
+ vty_out(vty, " neighbor %s maximum-prefix %u", addr,
peer->pmax[afi][safi]);
if (peer->pmax_threshold[afi][safi]
@@ -15056,13 +15107,16 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
if (peer->pmax_restart[afi][safi])
vty_out(vty, " restart %u",
peer->pmax_restart[afi][safi]);
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ vty_out(vty, " force");
vty_out(vty, "\n");
}
/* maximum-prefix-out */
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT))
- vty_out(vty, " neighbor %s maximum-prefix-out %" PRIu32 "\n",
+ vty_out(vty, " neighbor %s maximum-prefix-out %u\n",
addr, peer->pmax_out[afi][safi]);
/* Route server client. */
@@ -18097,8 +18151,7 @@ static int community_list_config_write(struct vty *vty)
for (list = cm->str.head; list; list = list->next)
for (entry = list->head; entry; entry = entry->next) {
vty_out(vty,
- "bgp extcommunity-list %s %s seq %" PRId64
- " %s %s\n",
+ "bgp extcommunity-list %s %s seq %" PRId64" %s %s\n",
entry->style == EXTCOMMUNITY_LIST_STANDARD
? "standard"
: "expanded",
@@ -18116,8 +18169,7 @@ static int community_list_config_write(struct vty *vty)
for (list = cm->num.head; list; list = list->next)
for (entry = list->head; entry; entry = entry->next) {
vty_out(vty,
- "bgp large-community-list %s seq %" PRId64
- " %s %s\n",
+ "bgp large-community-list %s seq %" PRId64" %s %s\n",
list->name, entry->seq,
community_direct_str(entry->direct),
community_list_config_str(entry));
@@ -18126,8 +18178,7 @@ static int community_list_config_write(struct vty *vty)
for (list = cm->str.head; list; list = list->next)
for (entry = list->head; entry; entry = entry->next) {
vty_out(vty,
- "bgp large-community-list %s %s seq %" PRId64
- " %s %s\n",
+ "bgp large-community-list %s %s seq %" PRId64" %s %s\n",
entry->style == LARGE_COMMUNITY_LIST_STANDARD
? "standard"
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 518532cb51..87936f1dd6 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -60,6 +60,7 @@
#include "bgpd/bgp_labelpool.h"
#include "bgpd/bgp_pbr.h"
#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_mac.h"
/* All information about zebra. */
@@ -1143,8 +1144,7 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
api_nh->ifindex = 0;
}
}
- if (nexthop)
- api_nh->gate.ipv6 = *nexthop;
+ api_nh->gate.ipv6 = *nexthop;
return true;
}
@@ -1178,7 +1178,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
struct bgp_path_info *info, struct bgp *bgp, afi_t afi,
safi_t safi)
{
- struct zapi_route api;
+ struct zapi_route api = { 0 };
struct zapi_nexthop *api_nh;
int nh_family;
unsigned int valid_nh_count = 0;
@@ -1224,7 +1224,6 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
nh_othervrf = 1;
/* Make Zebra API structure. */
- memset(&api, 0, sizeof(api));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
@@ -2500,17 +2499,66 @@ static void bgp_zebra_connected(struct zclient *zclient)
BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer);
}
-static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS)
+static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS)
+{
+ esi_t esi;
+ struct bgp *bgp = NULL;
+ struct stream *s = NULL;
+ char buf[ESI_STR_LEN];
+ struct in_addr originator_ip;
+ uint8_t active;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+ originator_ip.s_addr = stream_get_ipv4(s);
+ active = stream_getc(s);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx add ESI %s originator-ip %s active %u",
+ esi_to_str(&esi, buf, sizeof(buf)),
+ inet_ntoa(originator_ip),
+ active);
+
+ bgp_evpn_local_es_add(bgp, &esi, originator_ip, active);
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_es_del(ZAPI_CALLBACK_ARGS)
{
esi_t esi;
struct bgp *bgp = NULL;
struct stream *s = NULL;
char buf[ESI_STR_LEN];
- char buf1[INET6_ADDRSTRLEN];
- struct ipaddr originator_ip;
memset(&esi, 0, sizeof(esi_t));
- memset(&originator_ip, 0, sizeof(struct ipaddr));
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx del ESI %s",
+ esi_to_str(&esi, buf, sizeof(buf)));
+
+ bgp_evpn_local_es_del(bgp, &esi);
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_es_evi(ZAPI_CALLBACK_ARGS)
+{
+ esi_t esi;
+ vni_t vni;
+ struct bgp *bgp;
+ struct stream *s;
+ char buf[ESI_STR_LEN];
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp)
@@ -2518,18 +2566,18 @@ static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS)
s = zclient->ibuf;
stream_get(&esi, s, sizeof(esi_t));
- stream_get(&originator_ip, s, sizeof(struct ipaddr));
+ vni = stream_getl(s);
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx %s ESI %s originator-ip %s",
- (cmd == ZEBRA_LOCAL_ES_ADD) ? "add" : "del",
- esi_to_str(&esi, buf, sizeof(buf)),
- ipaddr2str(&originator_ip, buf1, sizeof(buf1)));
+ zlog_debug("Rx %s ESI %s VNI %u",
+ ZEBRA_VNI_ADD ? "add" : "del",
+ esi_to_str(&esi, buf, sizeof(buf)), vni);
- if (cmd == ZEBRA_LOCAL_ES_ADD)
- bgp_evpn_local_es_add(bgp, &esi, &originator_ip);
+ if (cmd == ZEBRA_LOCAL_ES_EVI_ADD)
+ bgp_evpn_local_es_evi_add(bgp, &esi, vni);
else
- bgp_evpn_local_es_del(bgp, &esi, &originator_ip);
+ bgp_evpn_local_es_evi_del(bgp, &esi, vni);
+
return 0;
}
@@ -2629,6 +2677,8 @@ static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
uint8_t flags = 0;
uint32_t seqnum = 0;
int state = 0;
+ char buf2[ESI_STR_LEN];
+ esi_t esi;
memset(&ip, 0, sizeof(ip));
s = zclient->ibuf;
@@ -2652,6 +2702,7 @@ static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
if (cmd == ZEBRA_MACIP_ADD) {
flags = stream_getc(s);
seqnum = stream_getl(s);
+ stream_get(&esi, s, sizeof(esi_t));
} else {
state = stream_getl(s);
}
@@ -2661,15 +2712,15 @@ static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u state %d",
+ zlog_debug("%u:Recv MACIP %s f 0x%x MAC %s IP %s VNI %u seq %u state %d ESI %s",
vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(&mac, buf, sizeof(buf)),
ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum,
- state);
+ state, esi_to_str(&esi, buf2, sizeof(buf2)));
if (cmd == ZEBRA_MACIP_ADD)
return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip,
- flags, seqnum);
+ flags, seqnum, &esi);
else
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state);
}
@@ -2802,9 +2853,11 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance)
zclient->nexthop_update = bgp_read_nexthop_update;
zclient->import_check_update = bgp_read_import_check_update;
zclient->fec_update = bgp_read_fec_update;
- zclient->local_es_add = bgp_zebra_process_local_es;
- zclient->local_es_del = bgp_zebra_process_local_es;
+ zclient->local_es_add = bgp_zebra_process_local_es_add;
+ zclient->local_es_del = bgp_zebra_process_local_es_del;
zclient->local_vni_add = bgp_zebra_process_local_vni;
+ zclient->local_es_evi_add = bgp_zebra_process_local_es_evi;
+ zclient->local_es_evi_del = bgp_zebra_process_local_es_evi;
zclient->local_vni_del = bgp_zebra_process_local_vni;
zclient->local_macip_add = bgp_zebra_process_local_macip;
zclient->local_macip_del = bgp_zebra_process_local_macip;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 14bfc20b22..12817a0837 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -87,6 +87,7 @@
#include "bgpd/bgp_pbr.h"
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
#include "bgpd/bgp_mac.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
@@ -1227,6 +1228,10 @@ struct peer *peer_new(struct bgp *bgp)
peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
}
+ /* set nexthop-unchanged for l2vpn evpn by default */
+ SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
/* Initialize per peer bgp GR FSM */
@@ -2174,14 +2179,6 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi)
return ret;
}
-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);
-}
-
void peer_nsf_stop(struct peer *peer)
{
afi_t afi;
@@ -2707,8 +2704,7 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range)
if (prefix_match(prefix, &prefix2)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
- "Deleting dynamic neighbor %s group %s upon "
- "delete of listen range %s",
+ "Deleting dynamic neighbor %s group %s upon delete of listen range %s",
peer->host, group->name, buf);
peer_delete(peer);
}
@@ -3906,6 +3902,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset},
{PEER_FLAG_MAX_PREFIX, 0, peer_change_none},
{PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none},
{PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out},
{PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out},
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out},
@@ -3978,9 +3975,6 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag)
peer->host);
}
- if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
- peer_nsf_stop(peer);
-
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
char *msg = peer->tx_shutdown_message;
size_t msglen;
@@ -6455,13 +6449,19 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi)
int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
uint32_t max, uint8_t threshold, int warning,
- uint16_t restart)
+ uint16_t restart, bool force)
{
struct peer *member;
struct listnode *node, *nnode;
/* Set flags and configuration on peer. */
peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+
+ if (force)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+
if (warning)
peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
else
@@ -6496,6 +6496,14 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
member->pmax[afi][safi] = max;
member->pmax_threshold[afi][safi] = threshold;
member->pmax_restart[afi][safi] = restart;
+
+ if (force)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+
if (warning)
SET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
@@ -6517,6 +6525,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
if (peer_group_active(peer)) {
peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_inherit(peer, afi, safi,
PEER_FLAG_MAX_PREFIX_WARNING);
PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]);
PEER_ATTR_INHERIT(peer, peer->group, pmax_threshold[afi][safi]);
@@ -6527,6 +6537,7 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
/* Remove flags and configuration from peer. */
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
peer->pmax[afi][safi] = 0;
peer->pmax_threshold[afi][safi] = 0;
@@ -6551,6 +6562,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX);
UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
member->pmax[afi][safi] = 0;
member->pmax_threshold[afi][safi] = 0;
@@ -6944,6 +6957,7 @@ void bgp_master_init(struct thread_master *master, const int buffer_size)
/* mpls label dynamic allocation pool */
bgp_lp_init(bm->master, &bm->labelpool);
+ bgp_evpn_mh_init();
QOBJ_REG(bm, bgp_master);
}
@@ -7143,6 +7157,7 @@ void bgp_terminate(void)
BGP_TIMER_OFF(bm->t_rmap_update);
bgp_mac_finish();
+ bgp_evpn_mh_finish();
}
struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index b71f7c6ce2..e84b0e43b9 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -43,9 +43,6 @@
#include "bgp_labelpool.h"
#include "bgp_addpath_types.h"
-DECLARE_HOOK(bgp_hook_config_write_vrf, (struct vty *vty, struct vrf *vrf),
- (vty, vrf))
-
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@@ -168,6 +165,9 @@ struct bgp_master {
/* How big should we set the socket buffer size */
uint32_t socket_buffer;
+ /* EVPN multihoming */
+ struct bgp_evpn_mh_info *mh_info;
+
bool terminating; /* global flag that sigint terminate seen */
QOBJ_FIELDS
};
@@ -664,9 +664,6 @@ struct bgp {
struct bgp_pbr_config *bgp_pbr_cfg;
- /* local esi hash table */
- struct hash *esihash;
-
/* Count of peers in established state */
uint32_t established_peers;
@@ -681,8 +678,6 @@ DECLARE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp))
DECLARE_HOOK(bgp_inst_config_write,
(struct bgp *bgp, struct vty *vty),
(bgp, vty))
-DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled),
- (vrf, enabled))
/* Thread callback information */
struct afi_safi_info {
@@ -1182,6 +1177,7 @@ struct peer {
#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */
#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */
#define PEER_FLAG_MAX_PREFIX_OUT (1 << 27) /* outgoing maximum prefix */
+#define PEER_FLAG_MAX_PREFIX_FORCE (1 << 28) /* maximum-prefix <num> force */
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
@@ -1353,7 +1349,7 @@ struct peer {
/* Track if we printed the attribute in debugs */
int rcvd_attr_printed;
- /* Prefix count. */
+ /* Accepted prefix count */
uint32_t pcount[AFI_MAX][SAFI_MAX];
/* Max prefix count. */
@@ -1843,7 +1839,6 @@ extern void peer_group_notify_unconfig(struct peer_group *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 *,
struct peer_group *, as_t *);
@@ -1925,7 +1920,7 @@ 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, uint32_t,
- uint8_t, int, uint16_t);
+ uint8_t, int, uint16_t, bool force);
extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t);
extern int peer_clear(struct peer *, struct listnode **);
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
index c935496280..0ff4b2c825 100644
--- a/bgpd/rfapi/rfapi.c
+++ b/bgpd/rfapi/rfapi.c
@@ -3747,8 +3747,7 @@ int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn)
vnc_zlog_debug_verbose("%s: auto-assigning RD", __func__);
if (vn->addr_family != AF_INET && vn->addr_family != AF_INET6) {
vnc_zlog_debug_verbose(
- "%s: can't auto-assign RD, VN addr family is not IPv4"
- "|v6",
+ "%s: can't auto-assign RD, VN addr family is not IPv4|v6",
__func__);
return EAFNOSUPPORT;
}
diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c
index 95b8582b95..e068eb7af6 100644
--- a/bgpd/rfapi/rfapi_rib.c
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -646,9 +646,7 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri,
pEncap->length - 2);
if (hop->length > pEncap->length - 2) {
zlog_warn(
- "%s: VNC subtlv length mismatch: "
- "RFP option says %d, attr says %d "
- "(shrinking)",
+ "%s: VNC subtlv length mismatch: RFP option says %d, attr says %d (shrinking)",
__func__, hop->length,
pEncap->length - 2);
hop->length = pEncap->length - 2;
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index 6b5c0fe719..a5393e25ac 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -15,6 +15,7 @@ vtysh_scan += \
bgpd/bgp_bfd.c \
bgpd/bgp_debug.c \
bgpd/bgp_dump.c \
+ bgpd/bgp_evpn_mh.c \
bgpd/bgp_evpn_vty.c \
bgpd/bgp_filter.c \
bgpd/bgp_mplsvpn.c \
@@ -65,6 +66,7 @@ bgpd_libbgp_a_SOURCES = \
bgpd/bgp_encap_tlv.c \
bgpd/bgp_errors.c \
bgpd/bgp_evpn.c \
+ bgpd/bgp_evpn_mh.c \
bgpd/bgp_evpn_vty.c \
bgpd/bgp_filter.c \
bgpd/bgp_flowspec.c \
@@ -139,6 +141,7 @@ noinst_HEADERS += \
bgpd/bgp_encap_types.h \
bgpd/bgp_errors.h \
bgpd/bgp_evpn.h \
+ bgpd/bgp_evpn_mh.h \
bgpd/bgp_evpn_private.h \
bgpd/bgp_evpn_vty.h \
bgpd/bgp_filter.h \
diff --git a/bgpd/valgrind.supp b/bgpd/valgrind.supp
index ed236a6dc5..31f2477a58 100644
--- a/bgpd/valgrind.supp
+++ b/bgpd/valgrind.supp
@@ -1,17 +1,18 @@
{
- <libyang_0.16.46>
+ <zlog_keep_working_at_exit>
Memcheck:Leak
+ match-leak-kinds: reachable
fun:calloc
- fun:_dlerror_run
- fun:dlopen@@GLIBC_2.2.5
- fun:ly_load_plugins_dir
- fun:ly_load_plugins
+ fun:qcalloc
+ fun:zlog_target_clone
}
{
- <zlog_keep_working_at_exit>
+ <libyang1_1.0.184>
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
- fun:qcalloc
- fun:zlog_target_clone
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ obj:/usr/lib/x86_64-linux-gnu/libyang.so.1.9.2
+ fun:ly_load_plugins
}
diff --git a/doc/developer/building-frr-for-openwrt.rst b/doc/developer/building-frr-for-openwrt.rst
index 5d8f82f27e..9bd1296dad 100644
--- a/doc/developer/building-frr-for-openwrt.rst
+++ b/doc/developer/building-frr-for-openwrt.rst
@@ -1,6 +1,8 @@
-OpenWRT
+OpenWrt
=======
+General info about OpenWrt buildsystem: `link <https://openwrt.org/docs/guide-developer/build-system/start>`_.
+
Prepare build environment
-------------------------
@@ -13,16 +15,16 @@ For Debian based distributions, run:
For other environments, instructions can be found in the
`official documentation
-<https://wiki.openwrt.org/doc/howto/buildroot.exigence#examples_of_package_installations>`_.
+<https://openwrt.org/docs/guide-developer/build-system/install-buildsystem#examples_of_package_installations>`_.
-Get OpenWRT Sources (from Git)
+Get OpenWrt Sources (from Git)
------------------------------
.. note::
- The OpenWRT build will fail if you run it as root. So take care to run it as a nonprivileged user.
+ The OpenWrt build will fail if you run it as root. So take care to run it as a nonprivileged user.
-Clone the OpenWRT sources and retrieve the package feeds
+Clone the OpenWrt sources and retrieve the package feeds
::
@@ -30,21 +32,15 @@ Clone the OpenWRT sources and retrieve the package feeds
cd openwrt
./scripts/feeds update -a
./scripts/feeds install -a
- cd feeds/routing
- git fetch origin pull/319/head
- git read-tree --prefix=frr/ -u FETCH_HEAD:frr
- cd ../../package/feeds/routing/
- ln -sv ../../../feeds/routing/frr .
- cd ../../..
-
-Configure OpenWRT for your target and select the needed FRR packages in Network -> Routing and Redirection -> frr,
+
+Configure OpenWrt for your target and select the needed FRR packages in Network -> Routing and Redirection -> frr,
exit and save
::
make menuconfig
-Then, to compile either a complete OpenWRT image, or the FRR packages, run:
+Then, to compile either a complete OpenWrt image, or the FRR packages, run:
::
@@ -54,10 +50,16 @@ It may be possible that on first build ``make package/frr/compile`` not
to work and it may be needed to run a ``make`` for the entire build
environment. Add ``V=s`` to get more debugging output.
+More information about OpenWrt buildsystem can be found `here
+<https://openwrt.org/docs/guide-developer/build-system/use-buildsystem>`_.
+
Work with sources
-----------------
-To update to a newer version, or change other options, you need to edit the ``feeds/routing/frr/Makefile``.
+To update to a newer version, or change other options, you need to edit the ``feeds/packages/frr/Makefile``.
+
+More information about working with patches in OpenWrt buildsystem can be found `here
+<https://openwrt.org/docs/guide-developer/build-system/use-patches-with-buildsystem>`_.
Usage
-----
diff --git a/doc/developer/grpc.rst b/doc/developer/grpc.rst
new file mode 100644
index 0000000000..8029a08b73
--- /dev/null
+++ b/doc/developer/grpc.rst
@@ -0,0 +1,249 @@
+.. _grpc-dev:
+
+***************
+Northbound gRPC
+***************
+
+.. _grpc-languages-bindings:
+
+Programming Language Bindings
+=============================
+
+The gRPC supported programming language bindings can be found here:
+https://grpc.io/docs/languages/
+
+After picking a programming language that supports gRPC bindings, the
+next step is to generate the FRR northbound bindings. To generate the
+northbound bindings you'll need the programming language binding
+generator tools and those are language specific.
+
+Next sections will use Ruby as an example for writing scripts to use
+the northbound.
+
+
+.. _grpc-ruby-generate:
+
+Generating Ruby FRR Bindings
+----------------------------
+
+Generating FRR northbound bindings for Ruby example:
+
+::
+
+ # Install the required gems:
+ # - grpc: the gem that will talk with FRR's gRPC plugin.
+ # - grpc-tools: the gem that provides the code generator.
+ gem install grpc
+ gem install grpc-tools
+
+ # Create your project/scripts directory:
+ mkdir /tmp/frr-ruby
+
+ # Go to FRR's grpc directory:
+ cd grpc
+
+ # Generate the ruby bindings:
+ grpc_tools_ruby_protoc \
+ --ruby_out=/tmp/frr-ruby \
+ --grpc_out=/tmp/frr-ruby \
+ frr-northbound.proto
+
+
+.. _grpc-ruby-if-sample:
+
+Using Ruby To Get Interfaces State
+----------------------------------
+
+Here is a sample script to print all interfaces FRR discovered:
+
+::
+
+ require 'frr-northbound_services_pb'
+
+ # Create the connection with FRR's gRPC:
+ stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
+
+ # Create a new state request to get interface state:
+ request = Frr::GetRequest.new
+ request.type = :STATE
+ request.path.push('/frr-interface:lib')
+
+ # Ask FRR.
+ response = stub.get(request)
+
+ # Print the response.
+ response.each do |result|
+ result.data.data.each_line do |line|
+ puts line
+ end
+ end
+
+
+.. note::
+
+ The generated files will assume that they are in the search path (e.g.
+ inside gem) so you'll need to either edit it to use ``require_relative`` or
+ tell Ruby where to look for them. For simplicity we'll use ``-I .`` to tell
+ it is in the current directory.
+
+
+The previous script will output something like this:
+
+::
+
+ $ cd /tmp/frr-ruby
+ # Add `-I.` so ruby finds the FRR generated file locally.
+ $ ruby -I. interface.rb
+ {
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth0",
+ "vrf": "default",
+ "state": {
+ "if-index": 2,
+ "mtu": 1500,
+ "mtu6": 1500,
+ "speed": 1000,
+ "metric": 0,
+ "phy-address": "11:22:33:44:55:66"
+ },
+ "frr-zebra:zebra": {
+ "state": {
+ "up-count": 0,
+ "down-count": 0
+ }
+ }
+ },
+ {
+ "name": "lo",
+ "vrf": "default",
+ "state": {
+ "if-index": 1,
+ "mtu": 0,
+ "mtu6": 65536,
+ "speed": 0,
+ "metric": 0,
+ "phy-address": "00:00:00:00:00:00"
+ },
+ "frr-zebra:zebra": {
+ "state": {
+ "up-count": 0,
+ "down-count": 0
+ }
+ }
+ }
+ ]
+ }
+ }
+
+
+.. _grpc-ruby-bfd-profile-sample:
+
+Using Ruby To Create BFD Profiles
+---------------------------------
+
+In this example you'll learn how to edit configuration using JSON
+and programmatic (XPath) format.
+
+::
+
+ require 'frr-northbound_services_pb'
+
+ # Create the connection with FRR's gRPC:
+ stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
+
+ # Create a new candidate configuration change.
+ new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
+
+ # Use JSON to configure.
+ request = Frr::LoadToCandidateRequest.new
+ request.candidate_id = new_candidate.candidate_id
+ request.type = :MERGE
+ request.config = Frr::DataTree.new
+ request.config.encoding = :JSON
+ request.config.data = <<-EOJ
+ {
+ "frr-bfdd:bfdd": {
+ "bfd": {
+ "profile": [
+ {
+ "name": "test-prof",
+ "detection-multiplier": 4,
+ "required-receive-interval": 800000
+ }
+ ]
+ }
+ }
+ }
+ EOJ
+
+ # Load configuration to candidate.
+ stub.load_to_candidate(request)
+
+ # Commit candidate.
+ stub.commit(
+ Frr::CommitRequest.new(
+ candidate_id: new_candidate.candidate_id,
+ phase: :ALL,
+ comment: 'create test-prof'
+ )
+ )
+
+ #
+ # Now lets delete the previous profile and create a new one.
+ #
+
+ # Create a new candidate configuration change.
+ new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
+
+ # Edit the configuration candidate.
+ request = Frr::EditCandidateRequest.new
+ request.candidate_id = new_candidate.candidate_id
+
+ # Delete previously created profile.
+ request.delete.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof']",
+ )
+ )
+
+ # Add new profile with two configurations.
+ request.update.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/detection-multiplier",
+ value: 5.to_s
+ )
+ )
+ request.update.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/desired-transmission-interval",
+ value: 900_000.to_s
+ )
+ )
+
+ # Modify the candidate.
+ stub.edit_candidate(request)
+
+ # Commit the candidate configuration.
+ stub.commit(
+ Frr::CommitRequest.new(
+ candidate_id: new_candidate.candidate_id,
+ phase: :ALL,
+ comment: 'replace test-prof with test-prof-2'
+ )
+ )
+
+
+And here is the new FRR configuration:
+
+::
+
+ $ sudo vtysh -c 'show running-config'
+ ...
+ bfd
+ profile test-prof-2
+ detect-multiplier 5
+ transmit-interval 900
+ !
+ !
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 26b590c876..1f803b3772 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -12,6 +12,7 @@ FRRouting Developer's Guide
testing
bgpd
fpm
+ grpc
ospf
zebra
vtysh
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 6dab244a03..03b4b5a3e2 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -28,6 +28,7 @@ dev_RSTFILES = \
doc/developer/cli.rst \
doc/developer/conf.py \
doc/developer/frr-release-procedure.rst \
+ doc/developer/grpc.rst \
doc/developer/hooks.rst \
doc/developer/include-compile.rst \
doc/developer/index.rst \
diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst
index 6a73803d01..d51cbc9a14 100644
--- a/doc/developer/zebra.rst
+++ b/doc/developer/zebra.rst
@@ -382,3 +382,75 @@ Zebra Protocol Commands
+------------------------------------+-------+
| ZEBRA_OPAQUE_UNREGISTER | 109 |
+------------------------------------+-------+
+| ZEBRA_NEIGH_DISCOVER | 110 |
++------------------------------------+-------+
+
+Dataplane batching
+==================
+
+Dataplane batching is an optimization feature that reduces the processing
+time involved in the user space to kernel space transition for every message we
+want to send.
+
+Design
+-----------
+
+With our dataplane abstraction, we create a queue of dataplane context objects
+for the messages we want to send to the kernel. In a separate pthread, we
+loop over this queue and send the context objects to the appropriate
+dataplane. A batching enhancement tightly integrates with the dataplane
+context objects so they are able to be batch sent to dataplanes that support
+it.
+
+There is one main change in the dataplane code. It does not call
+kernel-dependent functions one-by-one, but instead it hands a list of work down
+to the kernel level for processing.
+
+Netlink
+^^^^^^^
+
+At the moment, this is the only dataplane that allows for batch sending
+messages to it.
+
+When messages must be sent to the kernel, they are consecutively added
+to the batch represented by the `struct nl_batch`. Context objects are firstly
+encoded to their binary representation. All the encoding functions use the same
+interface: take a context object, a buffer and a size of the buffer as an
+argument. It is important that they should handle a situation in which a message
+wouldn't fit in the buffer and return a proper error. To achieve a zero-copy
+(in the user space only) messages are encoded to the same buffer which will
+be passed to the kernel. Hence, we can theoretically hit the boundary of the
+buffer.
+
+Messages stored in the batch are sent if one of the conditions occurs:
+
+- When an encoding function returns the buffer overflow error. The context
+ object that caused this error is re-added to the new, empty batch.
+
+- When the size of the batch hits certain limit.
+
+- When the namespace of a currently being processed context object is
+ different from all the previous ones. They have to be sent through
+ distinct sockets, so the messages cannot share the same buffer.
+
+- After the last message from the list is processed.
+
+As mentioned earlier, there is a special threshold which is smaller than
+the size of the underlying buffer. It prevents the overflow error and thus
+eliminates the case, in which a message is encoded twice.
+
+The buffer used in the batching is global, since allocating that big amount of
+memory every time wouldn't be most effective. However, its size can be changed
+dynamically, using hidden vtysh command:
+``zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)``. This feature is
+only used in tests and shouldn't be utilized in any other place.
+
+For every failed message in the batch, the kernel responds with an error
+message. Error messages are kept in the same order as they were sent, so parsing the
+response is straightforward. We use the two pointer technique to match
+requests with responses and then set appropriate status of dataplane context
+objects. There is also a global receive buffer and it is assumed that whatever
+the kernel sends it will fit in this buffer. The payload of netlink error messages
+consists of a error code and the original netlink message of the request, so
+the batch response won't be bigger than the batch request increased by
+some space for the headers.
diff --git a/doc/figures/fig_dmvpn_topologies.png b/doc/figures/fig_dmvpn_topologies.png
new file mode 100644
index 0000000000..a0dcc3e67d
--- /dev/null
+++ b/doc/figures/fig_dmvpn_topologies.png
Binary files differ
diff --git a/doc/manpages/frr-watchfrr.rst b/doc/manpages/frr-watchfrr.rst
index dceb423f82..d8c82eafa9 100644
--- a/doc/manpages/frr-watchfrr.rst
+++ b/doc/manpages/frr-watchfrr.rst
@@ -35,6 +35,22 @@ OPTIONS
Set the VTY socket directory (the default value is "/var/run/frr").
+.. option:: -N <name>, --pathspace <name>
+
+ Insert the given name into paths used by the FRR daemons. This is appended
+ to the VTY socket directory and passed to the daemons which also add it to
+ their paths in /etc.
+
+.. option:: --netns[=<name>]
+
+ (Linux only.) Switch network namespaces when starting watchfrr. The name
+ defaults to the value passed with -N (which it should be used in conjunction
+ with.) If the name is not specified, the option has no effect.
+
+ If the network namespace does not exist, it is created in a manner
+ compatible with iproute2. Network namespaces are not removed by FRR, this
+ must be done with "ip netns delete".
+
.. option:: -l <level>, --loglevel <level>
Set the logging 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.
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index 618d90a85e..86b0c28002 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -171,6 +171,33 @@ BFD peers and profiles share the same BFD session configuration commands.
'administrative down' message is sent to the remote peer.
+.. index:: [no] passive-mode
+.. clicmd:: [no] passive-mode
+
+ Mark session as passive: a passive session will not attempt to start
+ the connection and will wait for control packets from peer before it
+ begins replying.
+
+ This feature is useful when you have a router that acts as the
+ central node of a star network and you want to avoid sending BFD
+ control packets you don't need to.
+
+ The default is active-mode (or ``no passive-mode``).
+
+.. index:: [no] minimum-ttl (1-254)
+.. clicmd:: [no] minimum-ttl (1-254)
+
+ For multi hop sessions only: configure the minimum expected TTL for
+ an incoming BFD control packet.
+
+ This feature serves the purpose of thightening the packet validation
+ requirements to avoid receiving BFD control packets from other
+ sessions.
+
+ The default value is 254 (which means we only expect one hop between
+ this system and the peer).
+
+
BFD Peer Specific Commands
--------------------------
@@ -254,7 +281,7 @@ IS-IS BFD Configuration
The following commands are available inside the interface configuration node.
.. index:: isis bfd
-.. clicmd:: ip isis bfd
+.. clicmd:: isis bfd
Listen for BFD events on peers created on the interface. Every time
a new neighbor is found a BFD peer is created to monitor the link
@@ -269,6 +296,15 @@ The following commands are available inside the interface configuration node.
IPv4 and IPv6 support are configured then just a IPv6 based session is
created.
+.. index:: isis bfd profile BFDPROF
+.. clicmd:: isis bfd profile BFDPROF
+
+ Use a BFD profile BFDPROF as provided in the BFD configuration.
+
+.. index:: no isis bfd profile BFDPROF
+.. clicmd:: no isis bfd profile BFDPROF
+
+ Removes any BFD profile if present.
.. _bfd-ospf-peer-config:
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index bb968735b9..99dfee60eb 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1191,19 +1191,28 @@ Defining Peers
``net.core.optmem_max`` to allow the kernel to allocate the necessary option
memory.
+.. index:: [no] coalesce-time (0-4294967295)
+.. clicmd:: [no] coalesce-time (0-4294967295)
+
+ The time in milliseconds that BGP will delay before deciding what peers
+ can be put into an update-group together in order to generate a single
+ update for them. The default time is 1000.
+
.. _bgp-configuring-peers:
Configuring Peers
^^^^^^^^^^^^^^^^^
-.. index:: [no] neighbor PEER shutdown
-.. clicmd:: [no] neighbor PEER shutdown
+.. index:: [no] neighbor PEER shutdown [message MSG...]
+.. clicmd:: [no] neighbor PEER shutdown [message MSG...]
Shutdown the peer. We can delete the neighbor's configuration by
``no neighbor PEER remote-as ASN`` 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.
+ Optionally you can specify a shutdown message `MSG`.
+
.. index:: [no] neighbor PEER disable-connected-check
.. clicmd:: [no] neighbor PEER disable-connected-check
@@ -1213,6 +1222,11 @@ Configuring Peers
.. index:: [no] neighbor PEER ebgp-multihop
.. clicmd:: [no] neighbor PEER ebgp-multihop
+ Specifying ``ebgp-multihop`` allows sessions with eBGP neighbors to
+ establish when they are multiple hops away. When the neighbor is not
+ directly connected and this knob is not enabled, the session will not
+ establish.
+
.. index:: [no] neighbor PEER description ...
.. clicmd:: [no] neighbor PEER description ...
@@ -1297,8 +1311,8 @@ Configuring Peers
This command specifies a default `weight` value for the neighbor's routes.
-.. index:: [no] neighbor PEER maximum-prefix NUMBER
-.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER
+.. index:: [no] neighbor PEER maximum-prefix NUMBER [force]
+.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER [force]
Sets a maximum number of prefixes we can receive from a given peer. If this
number is exceeded, the BGP session will be destroyed.
@@ -1310,6 +1324,11 @@ Configuring Peers
granular and offers much smarter matching criterion than number of received
prefixes, making it more suited to implementing policy.
+ If _force_ is set, then ALL prefixes are counted for maximum instead of
+ accepted only. This is useful for cases where an inbound filter is applied,
+ but you want maximum-prefix to act on ALL (including filtered) prefixes. This
+ option requires `soft-reconfiguration inbound` to be enabled for the peer.
+
.. index:: [no] neighbor PEER maximum-prefix-out NUMBER
.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER
@@ -1334,6 +1353,49 @@ Configuring Peers
This command is only allowed for eBGP peers.
+.. index:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> as-override
+.. clicmd:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> as-override
+
+ Override AS number of the originating router with the local AS number.
+
+ Usually this configuration is used in PEs (Provider Edge) to replace
+ the incoming customer AS number so the connected CE (Customer Edge)
+ can use the same AS number as the other customer sites. This allows
+ customers of the provider network to use the same AS number across
+ their sites.
+
+ This command is only allowed for eBGP peers.
+
+.. index:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]
+.. clicmd:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]
+
+ Accept incoming routes with AS path containing AS number with the same value
+ as the current system AS.
+
+ This is used when you want to use the same AS number in your sites, but you
+ can't connect them directly. This is an alternative to
+ `neighbor WORD as-override`.
+
+ The parameter `(1-10)` configures the amount of accepted occurences of the
+ system AS number in AS path.
+
+ The parameter `origin` configures BGP to only accept routes originated with
+ the same AS number as the system.
+
+ This command is only allowed for eBGP peers.
+
+.. index:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths
+.. clicmd:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths
+
+ Configure BGP to send all known paths to neighbor in order to preserve multi
+ path capabilities inside a network.
+
+.. index:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS
+.. clicmd:: [no] neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS
+
+ Configure BGP to send best known paths to neighbor in order to preserve multi
+ path capabilities inside a network.
+
.. index:: [no] neighbor PEER ttl-security hops NUMBER
.. clicmd:: [no] neighbor PEER ttl-security hops NUMBER
@@ -1386,6 +1448,15 @@ Configuring Peers
peer in question. This number is between 0 and 600 seconds,
with the default advertisement interval being 0.
+Displaying Information about Peers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. index:: show bgp <afi> <safi> neighbors WORD bestpath-routes [json] [wide]
+.. clicmd:: show bgp <afi> <safi> neighbors WORD bestpath-routes [json] [wide]
+
+ For the given neighbor, WORD, that is specified list the routes selected
+ by BGP as having the best path.
+
.. _bgp-peer-filtering:
Peer Filtering
@@ -2404,92 +2475,6 @@ the same behavior of using same next-hop and RMAC values.
Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC
parameters.
-Support with VRF network namespace backend
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-It is possible to separate overlay networks contained in VXLAN interfaces from
-underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for
-that. In the latter case, this is necessary to set both bridge and vxlan interface
-on the same network namespace, as below example illustrates:
-
-.. code-block:: shell
-
- # linux shell
- ip netns add vrf1
- ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1
- ip link set dev vxlan101 netns vrf1
- ip netns exec vrf1 ip link set dev lo up
- ip netns exec vrf1 brctl addbr bridge101
- ip netns exec vrf1 brctl addif bridge101 vxlan101
-
-This makes possible to separate not only layer 3 networks like VRF-lite networks.
-Also, VRF netns based make possible to separate layer 2 networks on separate VRF
-instances.
-
-.. _bgp-cisco-compatibility:
-
-Cisco Compatibility
--------------------
-
-FRR has commands that change some configuration syntax and default behavior to
-behave more closely to Cisco conventions. These are deprecated and will be
-removed in a future version of FRR.
-
-.. deprecated:: 5.0
- Please transition to using the FRR specific syntax for your configuration.
-
-.. index:: bgp config-type cisco
-.. clicmd:: bgp config-type cisco
-
- Cisco compatible BGP configuration output.
-
- When this configuration line is specified:
-
- - ``no synchronization`` is displayed. This command does nothing and is for
- display purposes only.
- - ``no auto-summary`` is displayed.
- - The ``network`` and ``aggregate-address`` arguments are displayed as:
-
- ::
-
- A.B.C.D M.M.M.M
-
- FRR: network 10.0.0.0/8
- Cisco: network 10.0.0.0
-
- FRR: 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 no configuration is
- specified community attribute and extended community attribute are sent to
- the neighbor. If a user manually disables the feature, the community
- attribute is not sent to the neighbor. When ``bgp config-type cisco`` is
- specified, the community attribute is not sent to the neighbor by default.
- To send the community attribute user has to specify
- :clicmd:`neighbor A.B.C.D send-community` like so:
-
- .. code-block:: frr
-
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 1
- address-family ipv4 unicast
- no neighbor 10.0.0.1 send-community
- exit-address-family
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 1
- address-family ipv4 unicast
- neighbor 10.0.0.1 send-community
- exit-address-family
- !
-
-.. deprecated:: 5.0
- Please transition to using the FRR specific syntax for your configuration.
-
-.. index:: bgp config-type zebra
-.. clicmd:: bgp config-type zebra
-
- FRR style BGP configuration. This is the default.
.. _bgp-debugging:
@@ -2671,17 +2656,17 @@ daemon project, while :clicmd:`show bgp` command is the new format. The choice
has been done to keep old format with IPv4 routing table, while new format
displays IPv6 routing table.
-.. index:: show ip bgp
-.. clicmd:: show ip bgp
+.. index:: show ip bgp [wide]
+.. clicmd:: show ip bgp [wide]
-.. index:: show ip bgp A.B.C.D
-.. clicmd:: show ip bgp A.B.C.D
+.. index:: show ip bgp A.B.C.D [wide]
+.. clicmd:: show ip bgp A.B.C.D [wide]
-.. index:: show bgp
-.. clicmd:: show bgp
+.. index:: show bgp [wide]
+.. clicmd:: show bgp [wide]
-.. index:: show bgp X:X::X:X
-.. clicmd:: show bgp X:X::X:X
+.. index:: show bgp X:X::X:X [wide]
+.. clicmd:: show bgp X:X::X:X [wide]
These commands display BGP routes. When no route is specified, the default
is to display all BGP routes.
@@ -2697,6 +2682,12 @@ displays IPv6 routing table.
Total number of prefixes 1
+ If _wide_ option is specified, then the prefix table's width is increased
+ to fully display the prefix and the nexthop.
+
+ This is especially handy dealing with IPv6 prefixes and
+ if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled.
+
Some other commands provide additional options for filtering the output.
.. index:: show [ip] bgp regexp LINE
diff --git a/doc/user/grpc.rst b/doc/user/grpc.rst
new file mode 100644
index 0000000000..d6767fc866
--- /dev/null
+++ b/doc/user/grpc.rst
@@ -0,0 +1,66 @@
+.. _grpc:
+
+***************
+Northbound gRPC
+***************
+
+.. program:: configure
+
+*gRPC* provides a combined front end to all FRR daemons using the YANG
+northbound. It is currently disabled by default due its experimental
+stage, but it can be enabled with :option:`--enable-grpc` option in the
+configure script.
+
+
+.. _grpc-features:
+
+Northbound gRPC Features
+========================
+
+* Get/set configuration using JSON/XML/XPath encondings.
+* Execute YANG RPC calls.
+* Lock/unlock configuration.
+* Create/edit/load/update/commit candidate configuration.
+* List/get transactions.
+
+
+.. note::
+
+ There is currently no support for YANG notifications.
+
+
+.. note::
+
+ You can find more information on how to code programs to interact
+ with FRR by reading the gRPC Programming Language Bindings section
+ in the `developer's documentation
+ <http://docs.frrouting.org/projects/dev-guide/en/latest/grpc.html>`_.
+
+
+.. _grpc-config:
+
+Daemon gRPC Configuration
+=========================
+
+The *gRPC* module accepts the following run time option:
+
+- ``port``: the port to listen to (defaults to ``50051``).
+
+
+.. note::
+
+ At the moment only localhost connections with no SSL/TLS are
+ supported.
+
+
+To configure FRR daemons to listen to gRPC you need to append the
+following parameter to the daemon's command line: ``-M grpc``
+(optionally ``-M grpc:PORT`` to specify listening port).
+
+To do that in production you need to edit the ``/etc/frr/daemons`` file
+so the daemons get started with the command line argument. Example:
+
+::
+
+ # other daemons...
+ bfdd_options=" --daemon -A 127.0.0.1 -M grpc"
diff --git a/doc/user/index.rst b/doc/user/index.rst
index 7bb86e563b..8ac997f8dd 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -23,6 +23,7 @@ Basics
basic
vtysh
+ grpc
filter
routemap
ipv6
diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst
index 8d3bea7c94..9caeb0eedb 100644
--- a/doc/user/nhrpd.rst
+++ b/doc/user/nhrpd.rst
@@ -227,5 +227,206 @@ Show NHRP
Configuration Example
=====================
-FIXME
+.. figure:: ../figures/fig_dmvpn_topologies.png
+ :alt: image
+
+ image
+
+IPSec configurration example
+----------------------------
+
+This changes required on all nodes as HUB and Spokes.
+
+ipsec.conf file
+
+.. code-block:: shell
+
+ config setup
+ conn dmvpn
+ authby=secret
+ auto=add
+ keyexchange=ikev2
+ ike=aes256-aes256-sha256-modp2048
+ esp=aes256-aes256-sha256-modp2048
+ dpdaction=clear
+ dpddelay=300s
+ left=%any
+ leftid=%any
+ right=%any
+ rightid=%any
+ leftprotoport=gre
+ rightprotoport=gre
+ type=transport
+ keyingtries=%forever
+
+ipsec.secrets file
+
+.. code-block:: shell
+
+ %any : PSK "some_s3cret!"
+
+
+HUB configuration example
+-------------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.254/32 dev gre1
+ ip link set gre1 up
+
+Adding iptables rules to provide possibility shortcut tunnels and connect spokes directly
+
+.. code-block:: shell
+
+ 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
+
+FRR config on HUB
+
+.. code-block:: frr
+
+ nhrp nflog-group 1
+ !
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.254/32
+ ip nhrp network-id 1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65000
+ bgp router-id 10.0.0.254
+ no bgp ebgp-requires-policy
+ neighbor SPOKES peer-group
+ neighbor SPOKES disable-connected-check
+ neighbor 10.0.0.1 remote-as 65001
+ neighbor 10.0.0.1 peer-group SPOKES
+ neighbor 10.0.0.2 remote-as 65002
+ neighbor 10.0.0.2 peer-group SPOKES
+ neighbor 10.0.0.3 remote-as 65003
+ neighbor 10.0.0.3 peer-group SPOKES
+ !
+ address-family ipv4 unicast
+ network 172.16.0.0/24
+ redistribute nhrp
+ exit-address-family
+
+Spoke1 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.1/32 dev gre1
+ ip link set gre1 up
+
+
+FRR config on Spoke1
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.1/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.1.0/24
+ exit-address-family
+
+
+Spoke2 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.1/32 dev gre1
+ ip link set gre1 up
+
+FRR config on Spoke2
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.2/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.2.0/24
+ exit-address-family
+
+
+Spoke3 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.3/32 dev gre1
+ ip link set gre1 up
+
+FRR config on Spoke3
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.3/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.3.0/24
+ exit-address-family
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index cd095af862..6295ba9293 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -20,8 +20,11 @@ OSPF6 router
Set router's Router-ID.
-.. index:: interface IFNAME area AREA
-.. clicmd:: interface IFNAME area AREA
+.. index:: interface IFNAME area (0-4294967295)
+.. clicmd:: interface IFNAME area (0-4294967295)
+
+.. index:: interface IFNAME area A.B.C.D
+.. clicmd:: interface IFNAME area A.B.C.D
Bind interface to specified area, and start sending OSPF packets. `area` can
be specified as 0.
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index adc20936ed..ac6a1e5a8c 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -290,6 +290,8 @@ BGP
:t:`A Border Gateway Protocol 4 (BGP-4). Updates RFC1771. Y. Rekhter, T. Li & S. Hares. January 2006.`
- :rfc:`4364`
:t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.`
+- :rfc:`4486`
+ :t:`Subcodes for BGP Cease Notification Message. E. Chen, V. Gillet. April 2006.`
- :rfc:`4659`
:t:`BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN. J. De Clercq, D. Ooms, M. Carugi, F. Le Faucheur. September 2006.`
- :rfc:`4893`
@@ -320,6 +322,8 @@ BGP
:t:`BGP Large Communities Attribute. J. Heitz, Ed., J. Snijders, Ed, K. Patel, I. Bagdonas, N. Hilliard. February 2017`
- :rfc:`8195`
:t:`Use of BGP Large Communities. J. Snijders, J. Heasley, M. Schmidt, June 2017`
+- :rfc:`8203`
+ :t:`BGP Administrative Shutdown Communication. J. Snijders, J. Heitz, J. Scudder. July 2017.`
- :rfc:`8212`
:t:`Default External BGP (EBGP) Route Propagation Behavior without Policies. J. Mauch, J. Snijders, G. Hankins. July 2017`
- :rfc:`8277`
diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst
index 149949e863..99ef258cb2 100644
--- a/doc/user/pbr.rst
+++ b/doc/user/pbr.rst
@@ -123,6 +123,22 @@ end destination.
on another platform it will be denied. This mark translates to the
underlying `ip rule .... fwmark XXXX` command.
+.. clicmd:: match dscp (DSCP|0-63)
+
+ Match packets according to the specified differentiated services code point
+ (DSCP) in the IP header; if this value matches then forward the packet
+ according to the nexthop(s) specified. The passed DSCP value may also be a
+ standard name for a differentiated service code point like cs0 or af11.
+
+ You may only specify one dscp per route map sequence; to match on multiple
+ dscp values you will need to create several sequences, one for each value.
+
+.. clicmd:: match ecn (0-3)
+
+ Match packets according to the specified explicit congestion notification
+ (ECN) field in the IP header; if this value matches then forward the packet
+ according to the nexthop(s) specified.
+
.. clicmd:: set nexthop-group NAME
Use the nexthop-group NAME as the place to forward packets when the match
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index 919dc51f69..d30a5ed647 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -252,6 +252,13 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
reports on the interface. Refer to the next `ip igmp` command for IGMP
management.
+.. index:: [no] ip pim use-source A.B.C.D
+.. clicmd:: [no] ip pim use-source A.B.C.D
+
+ If you have multiple addresses configured on a particular interface
+ and would like pim to use a specific source address associated with
+ that interface.
+
.. index:: ip igmp
.. clicmd:: ip igmp
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index 77cadb6ae7..f8ec98c964 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -60,9 +60,8 @@ Enabling RPKI
This command enables the RPKI configuration mode. Most commands that start
with *rpki* can only be used in this mode.
- This command is available either in *configure node* for default *vrf* or
- in *vrf node* for specific *vrf*. When it is used in a telnet session,
- leaving of this mode cause rpki to be initialized.
+ When it is used in a telnet session, leaving of this mode cause rpki to be
+ initialized.
Executing this command alone does not activate prefix validation. You need
to configure at least one reachable cache server. See section
@@ -92,9 +91,6 @@ Examples of the error::
router(config)# rpki
% [BGP] Unknown command: rpki
- router(config-vrf)# rpki
- % [BGP] Unknown command: rpki
-
Note that the RPKI commands will be available in vtysh when running
``find rpki`` regardless of whether the module is loaded.
@@ -103,14 +99,7 @@ Note that the RPKI commands will be available in vtysh when running
Configuring RPKI/RTR Cache Servers
----------------------------------
-RPKI/RTR can be configured independently, either in configure node, or in *vrf*
-sub context. If configured in configure node, the core *bgp* instance of default
-*vrf* is impacted by the configuration.
-
-Each RPKI/RTR context is mapped to a *vrf* and can be made up of a specific list
-of cache-servers, and specific settings.
-
-The following commands are available for independent of a specific cache server.
+The following commands are independent of a specific cache server.
.. index:: rpki polling_period (1-3600)
.. clicmd:: rpki polling_period (1-3600)
@@ -211,27 +200,27 @@ Debugging
Displaying RPKI
---------------
-.. index:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [vrf NAME]
-.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [vrf NAME]
+.. index:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)]
+.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)]
Display validated prefixes received from the cache servers filtered
by the specified prefix.
-.. index:: show rpki as-number ASN [vrf NAME]
-.. clicmd:: show rpki as-number ASN [vrf NAME]
+.. index:: show rpki as-number ASN
+.. clicmd:: show rpki as-number ASN
Display validated prefixes received from the cache servers filtered
by ASN.
-.. index:: show rpki prefix-table [vrf NAME]
-.. clicmd:: show rpki prefix-table [vrf NAME]
+.. index:: show rpki prefix-table
+.. clicmd:: show rpki prefix-table
Display all validated prefix to origin AS mappings/records which have been
received from the cache servers and stored in the router. Based on this data,
the router validates BGP Updates.
-.. index:: show rpki cache-connection [vrf NAME]
-.. clicmd:: show rpki cache-connection [vrf NAME]
+.. index:: show rpki cache-connection
+.. clicmd:: show rpki cache-connection
Display all configured cache servers, whether active or not.
@@ -282,54 +271,5 @@ RPKI Configuration Example
route-map rpki permit 40
!
-RPKI Configuration Example with VRF
------------------------------------
-
-.. code-block:: frr
-
- hostname bgpd1
- password zebra
- ! log stdout
- debug bgp updates
- debug bgp keepalives
- debug rpki
- !
- vrf vrf_connect
- rpki
- rpki polling_period 1000
- rpki timeout 10
- ! SSH Example:
- rpki cache example.com 22 rtr-ssh ./ssh_key/id_rsa ./ssh_key/id_rsa.pub preference 1
- ! TCP Example:
- rpki cache rpki-validator.realmv6.org 8282 preference 2
- exit
- !
- exit-vrf
- router bgp 60001 vrf vrf_connect
- bgp router-id 141.22.28.223
- network 192.168.0.0/16
- neighbor 123.123.123.0 remote-as 60002
- neighbor 123.123.123.0 route-map rpki in
- !
- address-family ipv6
- neighbor 123.123.123.0 activate
- neighbor 123.123.123.0 route-map rpki in
- exit-address-family
- !
- route-map rpki permit 10
- match rpki invalid
- set local-preference 10
- !
- route-map rpki permit 20
- match rpki notfound
- set local-preference 20
- !
- route-map rpki permit 30
- match rpki valid
- set local-preference 30
- !
- route-map rpki permit 40
- !
-
.. [Securing-BGP] Geoff Huston, Randy Bush: Securing BGP, In: The Internet Protocol Journal, Volume 14, No. 2, 2011. <http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_14-2/142_bgp.html>
.. [Resource-Certification] Geoff Huston: Resource Certification, In: The Internet Protocol Journal, Volume 12, No.1, 2009. <http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_12-1/121_resource.html>
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
index 1c474193f2..57ef141c7e 100644
--- a/doc/user/sharp.rst
+++ b/doc/user/sharp.rst
@@ -78,7 +78,8 @@ keyword. At present, no sharp commands will be preserved in the config.
The nexthop or import choice chooses the type of nexthop we are asking
zebra to watch for us. This choice affects zebra's decision on what
matches. Connected tells zebra whether or not that we want the route
- matched against to be a static or connected route. The no form of
+ matched against to be a static or connected route for the nexthop keyword,
+ for the import keyword connected means exact match. The no form of
the command obviously turns this watching off.
.. index:: sharp data nexthop
@@ -88,13 +89,13 @@ keyword. At present, no sharp commands will be preserved in the config.
may have been turned on.
.. index:: sharp lsp
-.. clicmd:: sharp lsp (0-100000) nexthop-group NAME [prefix A.B.C.D/M TYPE [instance (0-255)]]
+.. clicmd:: sharp lsp [update] (0-100000) nexthop-group NAME [prefix A.B.C.D/M TYPE [instance (0-255)]]
Install an LSP using the specified in-label, with nexthops as
- listed in nexthop-group ``NAME``. The LSP is installed as type
- ZEBRA_LSP_SHARP. If ``prefix`` is specified, an existing route with
- type ``TYPE`` (and optional ``instance`` id) will be updated to use
- the LSP.
+ listed in nexthop-group ``NAME``. If ``update`` is included, the
+ update path is used. The LSP is installed as type ZEBRA_LSP_SHARP.
+ If ``prefix`` is specified, an existing route with type ``TYPE``
+ (and optional ``instance`` id) will be updated to use the LSP.
.. index:: sharp remove lsp
.. clicmd:: sharp remove lsp (0-100000) nexthop-group NAME [prefix A.B.C.D/M TYPE [instance (0-255)]]
@@ -137,3 +138,9 @@ keyword. At present, no sharp commands will be preserved in the config.
Remove a test zapi client session that was created with the
specified session id.
+
+.. index:: sharp neigh discover
+.. clicmd:: sharp neigh discover [vrf NAME] <A.B.C.D|X:X::X:X> IFNAME
+
+ Send an ARP/NDP request to trigger the addition of a neighbor in the ARP
+ table.
diff --git a/doc/user/static.rst b/doc/user/static.rst
index 09bdc9cbea..6302d1b148 100644
--- a/doc/user/static.rst
+++ b/doc/user/static.rst
@@ -128,3 +128,15 @@ but this time, the route command will apply to the VRF.
ip route 10.0.0.0/24 10.0.0.2
exit-vrf
+
+SR-TE Route Commands
+====================
+
+It is possible to specify a route using a SR-TE policy configured in Zebra.
+
+e.g. to use the SR-TE policy with endpoint 6.6.6.6 and color 123 to reach the
+network 9.9.9.9/24:
+
+.. code-block:: frr
+
+ ip route 9.9.9.9/24 6.6.6.6 color 123
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index eb7b7ced52..dd7a193e34 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -15,6 +15,7 @@ user_RSTFILES = \
doc/user/filter.rst \
doc/user/frr-reload.rst \
doc/user/glossary.rst \
+ doc/user/grpc.rst \
doc/user/index.rst \
doc/user/installation.rst \
doc/user/ipv6.rst \
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index fcf5c40307..a9ab162b40 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -1050,14 +1050,34 @@ Many routing protocols require a router-id to be configured. To have a
consistent router-id across all daemons, the following commands are available
to configure and display the router-id:
-.. index:: [no] router-id A.B.C.D [vrf NAME]
-.. clicmd:: [no] router-id A.B.C.D [vrf NAME]
+.. index:: [no] [ip] router-id A.B.C.D
+.. clicmd:: [no] [ip] router-id A.B.C.D
- Configure the router-id of this router.
+ Allow entering of the router-id. This command also works under the
+ vrf subnode, to allow router-id's per vrf.
-.. index:: show router-id [vrf NAME]
-.. clicmd:: show router-id [vrf NAME]
+.. index:: [no] [ip] router-id A.B.C.D vrf NAME
+.. clicmd:: [no] [ip] router-id A.B.C.D vrf NAME
+
+ Configure the router-id of this router from the configure NODE.
+ A show run of this command will display the router-id command
+ under the vrf sub node. This command is deprecated and will
+ be removed at some point in time in the future.
+
+.. index:: show [ip] router-id [vrf NAME]
+.. clicmd:: show [ip] router-id [vrf NAME]
Display the user configured router-id.
+For protocols requiring an IPv6 router-id, the following commands are available:
+
+.. index:: [no] ipv6 router-id X:X::X:X
+.. clicmd:: [no] ipv6 router-id X:X::X:X
+
+ Configure the IPv6 router-id of this router. Like its IPv4 counterpart,
+ this command works under the vrf subnode, to allow router-id's per vrf.
+
+.. index:: show ipv6 router-id [vrf NAME]
+.. clicmd:: show ipv6 router-id [vrf NAME]
+ Display the user configured IPv6 router-id.
diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c
index c76e067685..3610b3a869 100644
--- a/eigrpd/eigrp_cli.c
+++ b/eigrpd/eigrp_cli.c
@@ -37,7 +37,7 @@
/*
* XPath: /frr-eigrpd:eigrpd/instance
*/
-DEFPY_NOSH(
+DEFPY_YANG_NOSH(
router_eigrp,
router_eigrp_cmd,
"router eigrp (1-65535)$as [vrf NAME]",
@@ -61,7 +61,7 @@ DEFPY_NOSH(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_router_eigrp,
no_router_eigrp_cmd,
"no router eigrp (1-65535)$as [vrf NAME]",
@@ -101,7 +101,7 @@ void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode)
/*
* XPath: /frr-eigrpd:eigrpd/instance/router-id
*/
-DEFPY(
+DEFPY_YANG(
eigrp_router_id,
eigrp_router_id_cmd,
"eigrp router-id A.B.C.D$addr",
@@ -113,7 +113,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_router_id,
no_eigrp_router_id_cmd,
"no eigrp router-id [A.B.C.D]",
@@ -137,7 +137,7 @@ void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/passive-interface
*/
-DEFPY(
+DEFPY_YANG(
eigrp_passive_interface,
eigrp_passive_interface_cmd,
"[no] passive-interface IFNAME",
@@ -166,7 +166,7 @@ void eigrp_cli_show_passive_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/active-time
*/
-DEFPY(
+DEFPY_YANG(
eigrp_timers_active,
eigrp_timers_active_cmd,
"timers active-time <(1-65535)$timer|disabled$disabled>",
@@ -184,7 +184,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_timers_active,
no_eigrp_timers_active_cmd,
"no timers active-time [<(1-65535)|disabled>]",
@@ -209,7 +209,7 @@ void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/variance
*/
-DEFPY(
+DEFPY_YANG(
eigrp_variance,
eigrp_variance_cmd,
"variance (1-128)$variance",
@@ -220,7 +220,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_variance,
no_eigrp_variance_cmd,
"no variance [(1-128)]",
@@ -243,7 +243,7 @@ void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/maximum-paths
*/
-DEFPY(
+DEFPY_YANG(
eigrp_maximum_paths,
eigrp_maximum_paths_cmd,
"maximum-paths (1-32)$maximum_paths",
@@ -255,7 +255,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_maximum_paths,
no_eigrp_maximum_paths_cmd,
"no maximum-paths [(1-32)]",
@@ -283,7 +283,7 @@ void eigrp_cli_show_maximum_paths(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5
* XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6
*/
-DEFPY(
+DEFPY_YANG(
eigrp_metric_weights,
eigrp_metric_weights_cmd,
"metric weights (0-255)$k1 (0-255)$k2 (0-255)$k3 (0-255)$k4 (0-255)$k5 [(0-255)$k6]",
@@ -308,7 +308,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_metric_weights,
no_eigrp_metric_weights_cmd,
"no metric weights [(0-255) (0-255) (0-255) (0-255) (0-255) (0-255)]",
@@ -359,7 +359,7 @@ void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/network
*/
-DEFPY(
+DEFPY_YANG(
eigrp_network,
eigrp_network_cmd,
"[no] network A.B.C.D/M$prefix",
@@ -388,7 +388,7 @@ void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-eigrpd:eigrpd/instance/neighbor
*/
-DEFPY(
+DEFPY_YANG(
eigrp_neighbor,
eigrp_neighbor_cmd,
"[no] neighbor A.B.C.D$addr",
@@ -423,7 +423,7 @@ void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load
* XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu
*/
-DEFPY(
+DEFPY_YANG(
eigrp_redistribute_source_metric,
eigrp_redistribute_source_metric_cmd,
"[no] redistribute " FRR_REDIST_STR_EIGRPD
@@ -493,7 +493,7 @@ void eigrp_cli_show_redistribute(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay
*/
-DEFPY(
+DEFPY_YANG(
eigrp_if_delay,
eigrp_if_delay_cmd,
"delay (1-16777215)$delay",
@@ -505,7 +505,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_if_delay,
no_eigrp_if_delay_cmd,
"no delay [(1-16777215)]",
@@ -529,7 +529,7 @@ void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth
*/
-DEFPY(
+DEFPY_YANG(
eigrp_if_bandwidth,
eigrp_if_bandwidth_cmd,
"eigrp bandwidth (1-10000000)$bw",
@@ -542,7 +542,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_if_bandwidth,
no_eigrp_if_bandwidth_cmd,
"no eigrp bandwidth [(1-10000000)]",
@@ -567,7 +567,7 @@ void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval
*/
-DEFPY(
+DEFPY_YANG(
eigrp_if_ip_hellointerval,
eigrp_if_ip_hellointerval_cmd,
"ip hello-interval eigrp (1-65535)$hello",
@@ -581,7 +581,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_if_ip_hellointerval,
no_eigrp_if_ip_hellointerval_cmd,
"no ip hello-interval eigrp [(1-65535)]",
@@ -608,7 +608,7 @@ void eigrp_cli_show_hello_interval(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time
*/
-DEFPY(
+DEFPY_YANG(
eigrp_if_ip_holdinterval,
eigrp_if_ip_holdinterval_cmd,
"ip hold-time eigrp (1-65535)$hold",
@@ -622,7 +622,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_if_ip_holdinterval,
no_eigrp_if_ip_holdinterval_cmd,
"no ip hold-time eigrp [(1-65535)]",
@@ -654,7 +654,7 @@ void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses
*/
-DEFPY(
+DEFPY_YANG(
eigrp_ip_summary_address,
eigrp_ip_summary_address_cmd,
"ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix",
@@ -676,7 +676,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_ip_summary_address,
no_eigrp_ip_summary_address_cmd,
"no ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix",
@@ -714,7 +714,7 @@ void eigrp_cli_show_summarize_address(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication
*/
-DEFPY(
+DEFPY_YANG(
eigrp_authentication_mode,
eigrp_authentication_mode_cmd,
"ip authentication mode eigrp (1-65535)$as <md5|hmac-sha-256>$crypt",
@@ -738,7 +738,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_authentication_mode,
no_eigrp_authentication_mode_cmd,
"no ip authentication mode eigrp (1-65535)$as [<md5|hmac-sha-256>]",
@@ -778,7 +778,7 @@ void eigrp_cli_show_authentication(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
* XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain
*/
-DEFPY(
+DEFPY_YANG(
eigrp_authentication_keychain,
eigrp_authentication_keychain_cmd,
"ip authentication key-chain eigrp (1-65535)$as WORD$name",
@@ -801,7 +801,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_eigrp_authentication_keychain,
no_eigrp_authentication_keychain_cmd,
"no ip authentication key-chain eigrp (1-65535)$as [WORD]",
diff --git a/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c
index a3d6eb2e9e..0874ce3f19 100644
--- a/eigrpd/eigrp_dump.c
+++ b/eigrpd/eigrp_dump.c
@@ -234,8 +234,7 @@ void show_ip_eigrp_topology_header(struct vty *vty, struct eigrp *eigrp)
vty_out(vty, "\nEIGRP Topology Table for AS(%d)/ID(%s)\n\n", eigrp->AS,
inet_ntoa(eigrp->router_id));
vty_out(vty,
- "Codes: P - Passive, A - Active, U - Update, Q - Query, "
- "R - Reply\n r - reply Status, s - sia Status\n\n");
+ "Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply\n r - reply Status, s - sia Status\n\n");
}
void show_ip_eigrp_prefix_entry(struct vty *vty, struct eigrp_prefix_entry *tn)
diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c
index 072ff29705..92b5ce3482 100644
--- a/eigrpd/eigrp_network.c
+++ b/eigrpd/eigrp_network.c
@@ -160,8 +160,7 @@ int eigrp_if_ipmulticast(struct eigrp *top, struct prefix *p,
ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex);
if (ret < 0)
zlog_warn(
- "can't setsockopt IP_MULTICAST_IF (fd %d, addr %s, "
- "ifindex %u): %s",
+ "can't setsockopt IP_MULTICAST_IF (fd %d, addr %s, ifindex %u): %s",
top->fd, inet_ntoa(p->u.prefix4), ifindex,
safe_strerror(errno));
@@ -179,9 +178,7 @@ int eigrp_if_add_allspfrouters(struct eigrp *top, struct prefix *p,
htonl(EIGRP_MULTICAST_ADDRESS), 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?",
+ "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
@@ -201,8 +198,7 @@ int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p,
htonl(EIGRP_MULTICAST_ADDRESS), ifindex);
if (ret < 0)
zlog_warn(
- "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, "
- "ifindex %u, AllSPFRouters): %s",
+ "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
diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c
index 6090a1ef13..cfff63f839 100644
--- a/eigrpd/eigrp_packet.c
+++ b/eigrpd/eigrp_packet.c
@@ -449,8 +449,7 @@ int eigrp_write(struct thread *thread)
if (ret < 0)
zlog_warn(
- "*** sendmsg in eigrp_write failed to %s, "
- "id %d, off %d, len %d, interface %s, mtu %u: %s",
+ "*** sendmsg in eigrp_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, ei->ifp->name, ei->ifp->mtu,
safe_strerror(errno));
@@ -578,8 +577,7 @@ int eigrp_read(struct thread *thread)
if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV))
zlog_debug(
- "ignoring packet from router %s sent to %s, "
- "received on a passive interface, %s",
+ "ignoring packet from router %s sent to %s, received on a passive interface, %s",
inet_ntop(AF_INET, &eigrph->vrid, buf[0],
sizeof(buf[0])),
inet_ntop(AF_INET, &iph->ip_dst, buf[1],
@@ -736,8 +734,7 @@ static struct stream *eigrp_recv_packet(struct eigrp *eigrp,
if ((unsigned int)ret < sizeof(*iph)) /* ret must be > 0 now */
{
zlog_warn(
- "eigrp_recv_packet: discarding runt packet of length %d "
- "(ip header size is %u)",
+ "eigrp_recv_packet: discarding runt packet of length %d (ip header size is %u)",
ret, (unsigned int)sizeof(*iph));
return NULL;
}
@@ -782,8 +779,7 @@ static struct stream *eigrp_recv_packet(struct eigrp *eigrp,
if (ret != ip_len) {
zlog_warn(
- "eigrp_recv_packet read length mismatch: ip_len is %d, "
- "but recvmsg returned %d",
+ "eigrp_recv_packet read length mismatch: ip_len is %d, but recvmsg returned %d",
ip_len, ret);
return NULL;
}
diff --git a/grpc/frr-northbound.proto b/grpc/frr-northbound.proto
index d070d715e8..32544ba694 100644
--- a/grpc/frr-northbound.proto
+++ b/grpc/frr-northbound.proto
@@ -1,20 +1,27 @@
//
-// Copyright (C) 2019 NetDEF, Inc.
-// Renato Westphal
+// Copyright 2019 FRRouting
//
-// 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.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
//
-// 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
+// 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.
+//
+// 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 HOLDER 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.
//
syntax = "proto3";
@@ -280,6 +287,9 @@ message CommitResponse {
// ID of the created configuration transaction (when the phase is APPLY
// or ALL).
uint32 transaction_id = 1;
+
+ // Human-readable error or warning message(s).
+ string error_message = 2;
}
//
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index fb79481cb2..50011d55ec 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -293,4 +293,15 @@ struct br_mcast_stats {
__u64 mcast_bytes[BR_MCAST_DIR_SIZE];
__u64 mcast_packets[BR_MCAST_DIR_SIZE];
};
+
+/* FDB notification bits for NDA_NOTIFY:
+ * - BR_FDB_NFY_STATIC - notify on activity/expire even for a static entry
+ * - BR_FDB_NFY_INACTIVE - mark as inactive to avoid double notification,
+ * used with BR_FDB_NFY_STATIC (kernel controlled)
+ */
+enum {
+ BR_FDB_NFY_STATIC,
+ BR_FDB_NFY_INACTIVE,
+ BR_FDB_NFY_MAX
+};
#endif /* _UAPI_LINUX_IF_BRIDGE_H */
diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h
index cd144e3099..33c17af1cc 100644
--- a/include/linux/neighbour.h
+++ b/include/linux/neighbour.h
@@ -29,6 +29,8 @@ enum {
NDA_LINK_NETNSID,
NDA_SRC_VNI,
NDA_PROTOCOL, /* Originator of entry */
+ NDA_NH_ID,
+ NDA_NOTIFY,
__NDA_MAX
};
diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h
index 0ed9dd61d3..0187c74d88 100644
--- a/include/linux/net_namespace.h
+++ b/include/linux/net_namespace.h
@@ -16,7 +16,6 @@ enum {
NETNSA_NSID,
NETNSA_PID,
NETNSA_FD,
- NETNSA_TARGET_NSID,
__NETNSA_MAX,
};
diff --git a/include/linux/nexthop.h b/include/linux/nexthop.h
index e4d6e256ef..ee2a15b9c7 100644
--- a/include/linux/nexthop.h
+++ b/include/linux/nexthop.h
@@ -50,6 +50,7 @@ enum {
*/
NHA_GROUPS, /* flag; only return nexthop groups in dump */
NHA_MASTER, /* u32; only return nexthops with given master dev */
+ NHA_FDB, /* nexthop belongs to a bridge fdb */
__NHA_MAX,
};
diff --git a/isisd/fabricd.c b/isisd/fabricd.c
index e13078a7e8..ebaf14461e 100644
--- a/isisd/fabricd.c
+++ b/isisd/fabricd.c
@@ -372,8 +372,7 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
return ISIS_TIER_UNDEFINED;
}
- zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %"
- PRIu32, rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
+ zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
struct isis_spftree *remote_tree =
isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
@@ -386,8 +385,7 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
isis_spftree_del(remote_tree);
return ISIS_TIER_UNDEFINED;
} else {
- zlog_info("OpenFabric: Found %s as furthest from remote dist == %"
- PRIu32, rawlspid_print(furthest_from_remote->N.id),
+ zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id),
furthest_from_remote->d_N);
}
@@ -423,7 +421,7 @@ static int fabricd_tier_calculation_cb(struct thread *thread)
if (tier == ISIS_TIER_UNDEFINED)
return 0;
- zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.",
+ zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.",
tier);
f->tier_pending = tier;
thread_add_timer(master, fabricd_tier_set_timer, f,
@@ -463,7 +461,7 @@ static void fabricd_set_tier(struct fabricd *f, uint8_t tier)
if (f->tier == tier)
return;
- zlog_info("OpenFabric: Set own tier to %" PRIu8, tier);
+ zlog_info("OpenFabric: Set own tier to %hhu", tier);
f->tier = tier;
fabricd_bump_tier_calculation_timer(f);
@@ -477,7 +475,7 @@ void fabricd_run_spf(struct isis_area *area)
if (!f)
return;
- isis_run_hopcount_spf(area, isis->sysid, f->spftree);
+ isis_run_hopcount_spf(area, area->isis->sysid, f->spftree);
neighbors_neighbors_update(f);
fabricd_bump_tier_calculation_timer(f);
}
@@ -522,7 +520,7 @@ int fabricd_write_settings(struct isis_area *area, struct vty *vty)
return written;
if (f->tier_config != ISIS_TIER_UNDEFINED) {
- vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config);
+ vty_out(vty, " fabric-tier %hhu\n", f->tier_config);
written++;
}
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 425627485a..af5258846a 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -50,8 +50,6 @@
#include "isisd/fabricd.h"
#include "isisd/isis_nb.h"
-extern struct isis *isis;
-
static struct isis_adjacency *adj_alloc(const uint8_t *id)
{
struct isis_adjacency *adj;
@@ -94,6 +92,7 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
}
}
adj->adj_sids = list_new();
+ listnode_add(circuit->area->adjacency_list, adj);
return adj;
}
@@ -123,42 +122,21 @@ struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
return NULL;
}
-bool isis_adj_exists(const struct isis_area *area, int level,
- const uint8_t *sysid)
+struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level,
+ const uint8_t *sysid)
{
- struct isis_circuit *circuit;
+ struct isis_adjacency *adj;
struct listnode *node;
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
- struct isis_adjacency *adj;
- struct listnode *anode;
- struct list *adjdb;
-
- switch (circuit->circ_type) {
- case CIRCUIT_T_BROADCAST:
- adjdb = circuit->u.bc.adjdb[level - 1];
- if (!adjdb)
- continue;
+ for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
+ if (!(adj->level & level))
+ continue;
- for (ALL_LIST_ELEMENTS_RO(adjdb, anode, adj)) {
- if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
- return true;
- }
- break;
- case CIRCUIT_T_P2P:
- adj = circuit->u.p2p.neighbor;
- if (!adj)
- break;
-
- if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
- return true;
- break;
- default:
- break;
- }
+ if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN))
+ return adj;
}
- return false;
+ return NULL;
}
DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj))
@@ -186,6 +164,7 @@ void isis_delete_adj(void *arg)
adj_mt_finish(adj);
list_delete(&adj->adj_sids);
+ listnode_delete(adj->circuit->area->adjacency_list, adj);
XFREE(MTYPE_ISIS_ADJACENCY, adj);
return;
}
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h
index d61fbbd751..3c3a211a52 100644
--- a/isisd/isis_adjacency.h
+++ b/isisd/isis_adjacency.h
@@ -113,8 +113,8 @@ struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid,
struct list *adjdb);
struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
struct list *adjdb);
-bool isis_adj_exists(const struct isis_area *area, int level,
- const uint8_t *sysid);
+struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level,
+ const uint8_t *sysid);
struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
int level, struct isis_circuit *circuit);
void isis_delete_adj(void *adj);
diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c
index 69c971ee2c..f81dd6cf51 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -195,6 +195,14 @@ static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS)
struct listnode *anode;
struct isis_area *area;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(vrf_id);
+
+ if (isis == NULL) {
+ zlog_warn(" %s : ISIS routing instance not found", __func__);
+ return -1;
+ }
if (IS_DEBUG_BFD)
zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors.");
@@ -255,6 +263,43 @@ static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
command_str, dst_str, interface, src_str);
}
+static void bfd_command(int command, struct bfd_info *bfd_info, int family,
+ const void *dst_ip, const void *src_ip,
+ const char *if_name)
+{
+ struct bfd_session_arg args = {};
+ size_t addrlen;
+
+ args.cbit = 1;
+ args.family = family;
+ args.vrf_id = VRF_DEFAULT;
+ args.command = command;
+ args.bfd_info = bfd_info;
+ if (args.bfd_info) {
+ args.min_rx = bfd_info->required_min_rx;
+ args.min_tx = bfd_info->desired_min_tx;
+ args.detection_multiplier = bfd_info->detect_mult;
+ if (bfd_info->profile[0]) {
+ args.profilelen = strlen(bfd_info->profile);
+ strlcpy(args.profile, bfd_info->profile,
+ sizeof(args.profile));
+ }
+ }
+
+ addrlen = family == AF_INET ? sizeof(struct in_addr)
+ : sizeof(struct in6_addr);
+ memcpy(&args.dst, dst_ip, addrlen);
+ if (src_ip)
+ memcpy(&args.src, src_ip, addrlen);
+
+ if (if_name) {
+ strlcpy(args.ifname, if_name, sizeof(args.ifname));
+ args.ifnamelen = strlen(args.ifname);
+ }
+
+ zclient_bfd_command(zclient, &args);
+}
+
static void bfd_handle_adj_down(struct isis_adjacency *adj)
{
if (!adj->bfd_session)
@@ -264,17 +309,11 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj)
&adj->bfd_session->src_ip, adj->circuit->interface->name,
ZEBRA_BFD_DEST_DEREGISTER);
- bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family,
- &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
- (adj->circuit->interface)
- ? adj->circuit->interface->name
- : NULL,
- 0, /* ttl */
- 0, /* multihop */
- 1, /* control plane independent bit is on */
- ZEBRA_BFD_DEST_DEREGISTER,
- 0, /* set_flag */
- VRF_DEFAULT);
+ bfd_command(ZEBRA_BFD_DEST_DEREGISTER, NULL, adj->bfd_session->family,
+ &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
+ (adj->circuit->interface) ? adj->circuit->interface->name
+ : NULL);
+
bfd_session_free(&adj->bfd_session);
}
@@ -324,18 +363,12 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
&adj->bfd_session->src_ip, circuit->interface->name, command);
- bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family,
- &adj->bfd_session->dst_ip,
- &adj->bfd_session->src_ip,
- (adj->circuit->interface)
- ? adj->circuit->interface->name
- : NULL,
- 0, /* ttl */
- 0, /* multihop */
- 1, /* control plane independent bit is on */
- command,
- 0, /* set flag */
- VRF_DEFAULT);
+
+ bfd_command(command, circuit->bfd_info, family,
+ &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
+ (adj->circuit->interface) ? adj->circuit->interface->name
+ : NULL);
+
return;
out:
bfd_handle_adj_down(adj);
@@ -383,14 +416,14 @@ void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
}
}
-void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
- uint32_t min_rx, uint32_t min_tx,
- uint32_t detect_mult, int defaults)
+void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx,
+ uint32_t min_tx, uint32_t detect_mult,
+ const char *profile, int defaults)
{
int command = 0;
- bfd_set_param(&circuit->bfd_info, min_rx,
- min_tx, detect_mult, defaults, &command);
+ bfd_set_param(&circuit->bfd_info, min_rx, min_tx, detect_mult, profile,
+ defaults, &command);
if (command)
isis_bfd_circuit_cmd(circuit, command);
diff --git a/isisd/isis_bfd.h b/isisd/isis_bfd.h
index 3193f16061..6ce630688c 100644
--- a/isisd/isis_bfd.h
+++ b/isisd/isis_bfd.h
@@ -22,9 +22,9 @@
struct isis_circuit;
void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command);
-void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
- uint32_t min_rx, uint32_t min_tx,
- uint32_t detect_mult, int defaults);
+void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx,
+ uint32_t min_tx, uint32_t detect_mult,
+ const char *profile, int defaults);
void isis_bfd_init(void);
#endif
diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c
index 19695e7ab4..9599077771 100644
--- a/isisd/isis_bpf.c
+++ b/isisd/isis_bpf.c
@@ -273,8 +273,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
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",
+ "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;
}
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index e0e82e4725..985e07820f 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -224,10 +224,17 @@ struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp)
struct isis_area *area;
struct listnode *node;
struct isis_circuit *circuit;
+ struct isis *isis = NULL;
if (ifp->info)
return (struct isis_circuit *)ifp->info;
+ isis = isis_lookup_by_vrfid(ifp->vrf_id);
+ if (isis == NULL) {
+ zlog_warn(" %s : ISIS routing instance not found", __func__);
+ return NULL;
+ }
+
if (isis->area_list) {
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
circuit =
@@ -618,7 +625,8 @@ int isis_circuit_up(struct isis_circuit *circuit)
}
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
- circuit->circuit_id = isis_circuit_id_gen(isis, circuit->interface);
+ circuit->circuit_id = isis_circuit_id_gen(circuit->area->isis,
+ circuit->interface);
if (!circuit->circuit_id) {
flog_err(
EC_ISIS_CONFIG,
@@ -802,7 +810,8 @@ void isis_circuit_down(struct isis_circuit *circuit)
circuit->lsp_regenerate_pending[0] = 0;
circuit->lsp_regenerate_pending[1] = 0;
- _ISIS_CLEAR_FLAG(isis->circuit_ids_used, circuit->circuit_id);
+ _ISIS_CLEAR_FLAG(circuit->area->isis->circuit_ids_used,
+ circuit->circuit_id);
circuit->circuit_id = 0;
} else if (circuit->circ_type == CIRCUIT_T_P2P) {
isis_delete_adj(circuit->u.p2p.neighbor);
@@ -914,15 +923,13 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,
vty_out(vty, ", Active neighbors: %u\n",
circuit->upadjcount[0]);
vty_out(vty,
- " Hello interval: %u, "
- "Holddown count: %u %s\n",
+ " Hello interval: %u, Holddown count: %u %s\n",
circuit->hello_interval[0],
circuit->hello_multiplier[0],
(circuit->pad_hellos ? "(pad)"
: "(no-pad)"));
vty_out(vty,
- " CNSP interval: %u, "
- "PSNP interval: %u\n",
+ " CNSP interval: %u, PSNP interval: %u\n",
circuit->csnp_interval[0],
circuit->psnp_interval[0]);
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
@@ -948,15 +955,13 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,
vty_out(vty, ", Active neighbors: %u\n",
circuit->upadjcount[1]);
vty_out(vty,
- " Hello interval: %u, "
- "Holddown count: %u %s\n",
+ " Hello interval: %u, Holddown count: %u %s\n",
circuit->hello_interval[1],
circuit->hello_multiplier[1],
(circuit->pad_hellos ? "(pad)"
: "(no-pad)"));
vty_out(vty,
- " CNSP interval: %u, "
- "PSNP interval: %u\n",
+ " CNSP interval: %u, PSNP interval: %u\n",
circuit->csnp_interval[1],
circuit->psnp_interval[1]);
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
@@ -1015,6 +1020,14 @@ static int isis_interface_config_write(struct vty *vty)
struct isis_area *area;
struct isis_circuit *circuit;
int i;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(vrf->vrf_id);
+
+ if (isis == NULL) {
+ vty_out(vty, "ISIS routing instance not found");
+ return 0;
+ }
FOR_ALL_INTERFACES (vrf, ifp) {
/* IF name */
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index 7c380fb0d9..da358f411b 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -50,16 +50,18 @@ struct metric {
};
struct isis_bcast_info {
- uint8_t 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 ? */
+ uint8_t snpa[ETH_ALEN]; /* SNPA of this circuit */
+ char run_dr_elect[ISIS_LEVELS]; /* Should we run dr election ? */
+ struct thread *t_run_dr[ISIS_LEVELS]; /* DR election thread */
+ struct thread *t_send_lan_hello[ISIS_LEVELS]; /* send LAN IIHs in this
+ thread */
+ struct list *adjdb[ISIS_LEVELS]; /* adjacency dbs */
+ struct list *lan_neighs[ISIS_LEVELS]; /* list of lx neigh snpa */
+ char is_dr[ISIS_LEVELS]; /* Are we level x DR ? */
uint8_t l1_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-1 DR */
uint8_t l2_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-2 DR */
- struct thread *t_refresh_pseudo_lsp[2]; /* refresh pseudo-node LSPs */
+ struct thread *t_refresh_pseudo_lsp[ISIS_LEVELS]; /* refresh pseudo-node
+ LSPs */
};
struct isis_p2p_info {
@@ -86,10 +88,11 @@ struct isis_circuit {
* Threads
*/
struct thread *t_read;
- struct thread *t_send_csnp[2];
- struct thread *t_send_psnp[2];
+ struct thread *t_send_csnp[ISIS_LEVELS];
+ struct thread *t_send_psnp[ISIS_LEVELS];
struct isis_tx_queue *tx_queue;
- struct isis_circuit_arg level_arg[2]; /* used as argument for threads */
+ struct isis_circuit_arg
+ level_arg[ISIS_LEVELS]; /* used as argument for threads */
/* there is no real point in two streams, just for programming kicker */
int (*rx)(struct isis_circuit *circuit, uint8_t *ssnpa);
@@ -107,7 +110,7 @@ struct isis_circuit {
struct isis_bcast_info bc;
struct isis_p2p_info p2p;
} u;
- uint8_t priority[2]; /* l1/2 IS configured priority */
+ uint8_t priority[ISIS_LEVELS]; /* l1/2 IS configured priority */
int pad_hellos; /* add padding to Hello PDUs ? */
char ext_domain; /* externalDomain (boolean) */
int lsp_regenerate_pending[ISIS_LEVELS];
@@ -117,12 +120,12 @@ struct isis_circuit {
struct isis_passwd passwd; /* Circuit rx/tx password */
int is_type; /* circuit is type == level of circuit
* differentiated from circuit type (media) */
- uint32_t hello_interval[2]; /* hello-interval in seconds */
- uint16_t hello_multiplier[2]; /* hello-multiplier */
- uint16_t csnp_interval[2]; /* csnp-interval in seconds */
- uint16_t psnp_interval[2]; /* psnp-interval in seconds */
- uint8_t metric[2];
- uint32_t te_metric[2];
+ uint32_t hello_interval[ISIS_LEVELS]; /* hello-interval in seconds */
+ uint16_t hello_multiplier[ISIS_LEVELS]; /* hello-multiplier */
+ uint16_t csnp_interval[ISIS_LEVELS]; /* csnp-interval in seconds */
+ uint16_t psnp_interval[ISIS_LEVELS]; /* psnp-interval in seconds */
+ uint8_t metric[ISIS_LEVELS];
+ uint32_t te_metric[ISIS_LEVELS];
struct isis_ext_subtlvs *ext; /* Extended parameters (TE + Adj SID */
int ip_router; /* Route IP ? */
int is_passive; /* Is Passive ? */
@@ -131,7 +134,7 @@ struct isis_circuit {
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 */
- uint16_t upadjcount[2];
+ uint16_t upadjcount[ISIS_LEVELS];
#define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01
uint8_t flags;
bool disable_threeway_adj;
@@ -143,8 +146,8 @@ struct isis_circuit {
uint32_t init_failures; /* intialisationFailures */
uint32_t ctrl_pdus_rxed; /* controlPDUsReceived */
uint32_t ctrl_pdus_txed; /* controlPDUsSent */
- uint32_t
- desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */
+ uint32_t desig_changes[ISIS_LEVELS]; /* lanLxDesignatedIntermediateSystemChanges
+ */
uint32_t rej_adjacencies; /* rejectedAdjacencies */
/*
* Counters as in ietf-isis@2019-09-09.yang
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index df69b1c7be..4d02758003 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -46,7 +46,7 @@
/*
* XPath: /frr-isisd:isis/instance
*/
-DEFPY_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag",
+DEFPY_YANG_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag",
ROUTER_STR
"ISO IS-IS\n"
"ISO Routing area tag\n")
@@ -62,7 +62,12 @@ DEFPY_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag",
* need to make sure to set it in the yang model so that it
* is consistent with what FRR sees.
*/
- if (listcount(isis->area_list) == 0)
+
+ if (!im) {
+ return CMD_SUCCESS;
+ }
+
+ if (listcount(im->isis) == 0)
nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY,
"level-1-2");
ret = nb_cli_apply_changes(vty, base_xpath);
@@ -72,7 +77,7 @@ DEFPY_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag",
return ret;
}
-DEFPY(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag",
+DEFPY_YANG(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag",
NO_STR ROUTER_STR
"ISO IS-IS\n"
"ISO Routing area tag\n")
@@ -90,7 +95,7 @@ DEFPY(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag",
}
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
- area = isis_area_lookup(tag);
+ area = isis_area_lookup(tag, VRF_DEFAULT);
if (area && area->circuit_list && listcount(area->circuit_list)) {
for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode,
circuit)) {
@@ -126,7 +131,7 @@ void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing
* XPath: /frr-isisd:isis/instance
*/
-DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
+DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
"Interface Internet Protocol config commands\n"
"IP router interface commands\n"
"IS-IS routing protocol\n"
@@ -134,13 +139,20 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
{
char temp_xpath[XPATH_MAXLEN];
const char *circ_type;
- struct isis_area *area;
+ struct isis_area *area = NULL;
struct interface *ifp;
/* area will be created if it is not present. make sure the yang model
* is synced with FRR and call the appropriate NB cb.
*/
- area = isis_area_lookup(tag);
+
+ if (!im) {
+ return CMD_SUCCESS;
+ }
+ ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ if (ifp)
+ area = isis_area_lookup(tag, ifp->vrf_id);
+
if (!area) {
snprintf(temp_xpath, XPATH_MAXLEN,
"/frr-isisd:isis/instance[area-tag='%s']", tag);
@@ -148,9 +160,9 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
snprintf(temp_xpath, XPATH_MAXLEN,
"/frr-isisd:isis/instance[area-tag='%s']/is-type",
tag);
- nb_cli_enqueue_change(
- vty, temp_xpath, NB_OP_MODIFY,
- listcount(isis->area_list) == 0 ? "level-1-2" : NULL);
+ nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY,
+ listcount(im->isis) == 0 ? "level-1-2"
+ : NULL);
nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE,
NULL);
nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag",
@@ -159,8 +171,7 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
NB_OP_MODIFY, "true");
nb_cli_enqueue_change(
vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY,
- listcount(isis->area_list) == 0 ? "level-1-2"
- : "level-1");
+ listcount(im->isis) == 0 ? "level-1-2" : "level-1");
} else {
/* area exists, circuit type defaults to its area's is_type */
switch (area->is_type) {
@@ -188,7 +199,6 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
}
/* check if the interface is a loopback and if so set it as passive */
- ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
if (ifp && if_is_loopback(ifp))
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
NB_OP_MODIFY, "true");
@@ -196,7 +206,7 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
+DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
"Interface Internet Protocol config commands\n"
"IP router interface commands\n"
"IS-IS routing protocol\n"
@@ -204,13 +214,20 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
{
char temp_xpath[XPATH_MAXLEN];
const char *circ_type;
- struct isis_area *area;
+ struct isis_area *area = NULL;
struct interface *ifp;
/* area will be created if it is not present. make sure the yang model
* is synced with FRR and call the appropriate NB cb.
*/
- area = isis_area_lookup(tag);
+
+ if (!im)
+ return CMD_SUCCESS;
+
+ ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
+ if (ifp)
+ area = isis_area_lookup(tag, ifp->vrf_id);
+
if (!area) {
snprintf(temp_xpath, XPATH_MAXLEN,
"/frr-isisd:isis/instance[area-tag='%s']", tag);
@@ -218,9 +235,9 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
snprintf(temp_xpath, XPATH_MAXLEN,
"/frr-isisd:isis/instance[area-tag='%s']/is-type",
tag);
- nb_cli_enqueue_change(
- vty, temp_xpath, NB_OP_MODIFY,
- listcount(isis->area_list) == 0 ? "level-1-2" : NULL);
+ nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY,
+ listcount(im->isis) == 0 ? "level-1-2"
+ : NULL);
nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE,
NULL);
nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag",
@@ -229,8 +246,7 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
NB_OP_MODIFY, "true");
nb_cli_enqueue_change(
vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY,
- listcount(isis->area_list) == 0 ? "level-1-2"
- : "level-1");
+ listcount(im->isis) == 0 ? "level-1-2" : "level-1");
} else {
/* area exists, circuit type defaults to its area's is_type */
switch (area->is_type) {
@@ -258,7 +274,6 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
}
/* check if the interface is a loopback and if so set it as passive */
- ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
if (ifp && if_is_loopback(ifp))
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
NB_OP_MODIFY, "true");
@@ -266,7 +281,7 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_ip_router_isis, no_ip_router_isis_cmd,
+DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd,
"no <ip|ipv6>$ip router isis [WORD]$tag",
NO_STR
"Interface Internet Protocol config commands\n"
@@ -327,11 +342,10 @@ void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring
*/
-DEFPY(isis_bfd,
+DEFPY_YANG(isis_bfd,
isis_bfd_cmd,
"[no] isis bfd",
- NO_STR
- PROTO_HELP
+ NO_STR PROTO_HELP
"Enable BFD support\n")
{
const struct lyd_node *dnode;
@@ -343,25 +357,59 @@ DEFPY(isis_bfd,
return CMD_SUCCESS;
}
- nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring",
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/enabled",
NB_OP_MODIFY, no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile
+ */
+DEFPY_YANG(isis_bfd_profile,
+ isis_bfd_profile_cmd,
+ "[no] isis bfd profile WORD",
+ NO_STR PROTO_HELP
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n"
+ "Profile name\n")
+{
+ const struct lyd_node *dnode;
+
+ dnode = yang_dnode_get(vty->candidate_config->dnode,
+ "%s/frr-isisd:isis", VTY_CURR_XPATH);
+ if (dnode == NULL) {
+ vty_out(vty, "ISIS is not enabled on this circuit\n");
+ return CMD_SUCCESS;
+ }
+
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/profile",
+ NB_OP_MODIFY, no ? NULL : profile);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
- if (!yang_dnode_get_bool(dnode, NULL))
+ const char *profile;
+
+ if (!yang_dnode_get_bool(dnode, "./enabled"))
vty_out(vty, " no");
vty_out(vty, " isis bfd\n");
+
+ if (yang_dnode_exists(dnode, "./profile")) {
+ profile = yang_dnode_get_string(dnode, "./profile");
+ if (profile[0] != '\0')
+ vty_out(vty, " isis bfd profile %s\n", profile);
+ }
}
/*
* XPath: /frr-isisd:isis/instance/area-address
*/
-DEFPY(net, net_cmd, "[no] net WORD",
+DEFPY_YANG(net, net_cmd, "[no] net WORD",
"Remove an existing Network Entity Title for this process\n"
"A Network Entity Title for this process (OSI only)\n"
"XX.XXXX. ... .XXX.XX Network entity title (NET)\n")
@@ -381,7 +429,7 @@ void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/is-type
*/
-DEFPY(is_type, is_type_cmd, "is-type <level-1|level-1-2|level-2-only>$level",
+DEFPY_YANG(is_type, is_type_cmd, "is-type <level-1|level-1-2|level-2-only>$level",
"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"
@@ -394,7 +442,7 @@ DEFPY(is_type, is_type_cmd, "is-type <level-1|level-1-2|level-2-only>$level",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_is_type, no_is_type_cmd,
+DEFPY_YANG(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"
@@ -428,7 +476,7 @@ void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/dynamic-hostname
*/
-DEFPY(dynamic_hostname, dynamic_hostname_cmd, "[no] hostname dynamic",
+DEFPY_YANG(dynamic_hostname, dynamic_hostname_cmd, "[no] hostname dynamic",
NO_STR
"Dynamic hostname for IS-IS\n"
"Dynamic hostname\n")
@@ -451,7 +499,7 @@ void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/overload
*/
-DEFPY(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit",
+DEFPY_YANG(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit",
"Reset overload bit to accept transit traffic\n"
"Set overload bit to avoid any transit traffic\n")
{
@@ -472,7 +520,7 @@ void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/attached
*/
-DEFPY(set_attached_bit, set_attached_bit_cmd, "[no] set-attached-bit",
+DEFPY_YANG(set_attached_bit, set_attached_bit_cmd, "[no] set-attached-bit",
"Reset attached bit\n"
"Set attached bit to identify as L1/L2 router for inter-area traffic\n")
{
@@ -493,7 +541,7 @@ void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/metric-style
*/
-DEFPY(metric_style, metric_style_cmd,
+DEFPY_YANG(metric_style, metric_style_cmd,
"metric-style <narrow|transition|wide>$style",
"Use old-style (ISO 10589) or new-style packet formats\n"
"Use old style of TLVs with narrow metric\n"
@@ -505,7 +553,7 @@ DEFPY(metric_style, metric_style_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_metric_style, no_metric_style_cmd,
+DEFPY_YANG(no_metric_style, no_metric_style_cmd,
"no metric-style [narrow|transition|wide]",
NO_STR
"Use old-style (ISO 10589) or new-style packet formats\n"
@@ -539,7 +587,7 @@ void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/area-password
*/
-DEFPY(area_passwd, area_passwd_cmd,
+DEFPY_YANG(area_passwd, area_passwd_cmd,
"area-password <clear|md5>$pwd_type WORD$pwd [authenticate snp <send-only|validate>$snp]",
"Configure the authentication password for an area\n"
"Clear-text authentication type\n"
@@ -578,7 +626,7 @@ void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/domain-password
*/
-DEFPY(domain_passwd, domain_passwd_cmd,
+DEFPY_YANG(domain_passwd, domain_passwd_cmd,
"domain-password <clear|md5>$pwd_type WORD$pwd [authenticate snp <send-only|validate>$snp]",
"Set the authentication password for a routing domain\n"
"Clear-text authentication type\n"
@@ -600,7 +648,7 @@ DEFPY(domain_passwd, domain_passwd_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_area_passwd, no_area_passwd_cmd,
+DEFPY_YANG(no_area_passwd, no_area_passwd_cmd,
"no <area-password|domain-password>$cmd",
NO_STR
"Configure the authentication password for an area\n"
@@ -629,7 +677,7 @@ void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-isisd:isis/instance/lsp/timers/level-1/generation-interval
* XPath: /frr-isisd:isis/instance/lsp/timers/level-2/generation-interval
*/
-DEFPY(lsp_gen_interval, lsp_gen_interval_cmd,
+DEFPY_YANG(lsp_gen_interval, lsp_gen_interval_cmd,
"lsp-gen-interval [level-1|level-2]$level (1-120)$val",
"Minimum interval between regenerating same LSP\n"
"Set interval for level 1 only\n"
@@ -648,7 +696,7 @@ DEFPY(lsp_gen_interval, lsp_gen_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_lsp_gen_interval, no_lsp_gen_interval_cmd,
+DEFPY_YANG(no_lsp_gen_interval, no_lsp_gen_interval_cmd,
"no lsp-gen-interval [level-1|level-2]$level [(1-120)]",
NO_STR
"Minimum interval between regenerating same LSP\n"
@@ -672,7 +720,7 @@ DEFPY(no_lsp_gen_interval, no_lsp_gen_interval_cmd,
* XPath: /frr-isisd:isis/instance/lsp/timers/level-1/refresh-interval
* XPath: /frr-isisd:isis/instance/lsp/timers/level-2/refresh-interval
*/
-DEFPY(lsp_refresh_interval, lsp_refresh_interval_cmd,
+DEFPY_YANG(lsp_refresh_interval, lsp_refresh_interval_cmd,
"lsp-refresh-interval [level-1|level-2]$level (1-65235)$val",
"LSP refresh interval\n"
"LSP refresh interval for Level 1 only\n"
@@ -691,7 +739,7 @@ DEFPY(lsp_refresh_interval, lsp_refresh_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd,
+DEFPY_YANG(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd,
"no lsp-refresh-interval [level-1|level-2]$level [(1-65235)]",
NO_STR
"LSP refresh interval\n"
@@ -716,7 +764,7 @@ DEFPY(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd,
* XPath: /frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime
*/
-DEFPY(max_lsp_lifetime, max_lsp_lifetime_cmd,
+DEFPY_YANG(max_lsp_lifetime, max_lsp_lifetime_cmd,
"max-lsp-lifetime [level-1|level-2]$level (350-65535)$val",
"Maximum LSP lifetime\n"
"Maximum LSP lifetime for Level 1 only\n"
@@ -735,7 +783,7 @@ DEFPY(max_lsp_lifetime, max_lsp_lifetime_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd,
+DEFPY_YANG(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd,
"no max-lsp-lifetime [level-1|level-2]$level [(350-65535)]",
NO_STR
"Maximum LSP lifetime\n"
@@ -759,7 +807,7 @@ DEFPY(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd,
* XPath: /frr-isisd:isis/instance/lsp/timers
*/
-DEFPY(lsp_timers, lsp_timers_cmd,
+DEFPY_YANG(lsp_timers, lsp_timers_cmd,
"lsp-timers [level-1|level-2]$level gen-interval (1-120)$gen refresh-interval (1-65235)$refresh max-lifetime (350-65535)$lifetime",
"LSP-related timers\n"
"LSP-related timers for Level 1 only\n"
@@ -797,7 +845,7 @@ DEFPY(lsp_timers, lsp_timers_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_lsp_timers, no_lsp_timers_cmd,
+DEFPY_YANG(no_lsp_timers, no_lsp_timers_cmd,
"no lsp-timers [level-1|level-2]$level [gen-interval (1-120) refresh-interval (1-65235) max-lifetime (350-65535)]",
NO_STR
"LSP-related timers\n"
@@ -869,7 +917,7 @@ void cli_show_isis_lsp_timers(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/lsp/mtu
*/
-DEFPY(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val",
+DEFPY_YANG(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val",
"Configure the maximum size of generated LSPs\n"
"Maximum size of generated LSPs\n")
{
@@ -878,7 +926,7 @@ DEFPY(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_area_lsp_mtu, no_area_lsp_mtu_cmd, "no lsp-mtu [(128-4352)]",
+DEFPY_YANG(no_area_lsp_mtu, no_area_lsp_mtu_cmd, "no lsp-mtu [(128-4352)]",
NO_STR
"Configure the maximum size of generated LSPs\n"
"Maximum size of generated LSPs\n")
@@ -897,7 +945,7 @@ void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/spf/minimum-interval
*/
-DEFPY(spf_interval, spf_interval_cmd,
+DEFPY_YANG(spf_interval, spf_interval_cmd,
"spf-interval [level-1|level-2]$level (1-120)$val",
"Minimum interval between SPF calculations\n"
"Set interval for level 1 only\n"
@@ -914,7 +962,7 @@ DEFPY(spf_interval, spf_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_spf_interval, no_spf_interval_cmd,
+DEFPY_YANG(no_spf_interval, no_spf_interval_cmd,
"no spf-interval [level-1|level-2]$level [(1-120)]",
NO_STR
"Minimum interval between SPF calculations\n"
@@ -949,7 +997,7 @@ void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay
*/
-DEFPY(spf_delay_ietf, spf_delay_ietf_cmd,
+DEFPY_YANG(spf_delay_ietf, spf_delay_ietf_cmd,
"spf-delay-ietf init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)",
"IETF SPF delay algorithm\n"
"Delay used while in QUIET state\n"
@@ -979,7 +1027,7 @@ DEFPY(spf_delay_ietf, spf_delay_ietf_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_spf_delay_ietf, no_spf_delay_ietf_cmd,
+DEFPY_YANG(no_spf_delay_ietf, no_spf_delay_ietf_cmd,
"no spf-delay-ietf [init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)]",
NO_STR
"IETF SPF delay algorithm\n"
@@ -1015,7 +1063,7 @@ void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/purge-originator
*/
-DEFPY(area_purge_originator, area_purge_originator_cmd, "[no] purge-originator",
+DEFPY_YANG(area_purge_originator, area_purge_originator_cmd, "[no] purge-originator",
NO_STR "Use the RFC 6232 purge-originator\n")
{
nb_cli_enqueue_change(vty, "./purge-originator", NB_OP_MODIFY,
@@ -1035,7 +1083,7 @@ void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/mpls-te
*/
-DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on",
+DEFPY_YANG(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on",
MPLS_TE_STR "Enable the MPLS-TE functionality\n")
{
nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_CREATE,
@@ -1044,7 +1092,7 @@ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]",
+DEFPY_YANG(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]",
NO_STR
"Disable the MPLS-TE functionality\n"
"Disable the MPLS-TE functionality\n")
@@ -1064,7 +1112,7 @@ void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/mpls-te/router-address
*/
-DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd,
+DEFPY_YANG(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"
@@ -1076,7 +1124,7 @@ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd,
+DEFPY_YANG(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd,
"no mpls-te router-address [A.B.C.D]",
NO_STR MPLS_TE_STR
"Delete IP address of the advertising router\n"
@@ -1095,7 +1143,7 @@ void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(dnode, NULL));
}
-DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd,
+DEFPY_YANG(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd,
"[no] mpls-te inter-as [level-1|level-1-2|level-2-only]",
NO_STR MPLS_TE_STR
"Configure MPLS-TE Inter-AS support\n"
@@ -1110,10 +1158,8 @@ DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd,
/*
* XPath: /frr-isisd:isis/instance/default-information-originate
*/
-DEFPY(isis_default_originate, isis_default_originate_cmd,
- "[no] default-information originate <ipv4|ipv6>$ip"
- " <level-1|level-2>$level [always]$always"
- " [{metric (0-16777215)$metric|route-map WORD$rmap}]",
+DEFPY_YANG(isis_default_originate, isis_default_originate_cmd,
+ "[no] default-information originate <ipv4|ipv6>$ip <level-1|level-2>$level [always]$always [{metric (0-16777215)$metric|route-map WORD$rmap}]",
NO_STR
"Control distribution of default information\n"
"Distribute a default route\n"
@@ -1123,7 +1169,7 @@ DEFPY(isis_default_originate, isis_default_originate_cmd,
"Distribute default route into level-2\n"
"Always advertise default route\n"
"Metric for default route\n"
- "ISIS default metric\n"
+ "IS-IS default metric\n"
"Route map reference\n"
"Pointer to route-map entries\n")
{
@@ -1188,18 +1234,16 @@ void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/redistribute
*/
-DEFPY(isis_redistribute, isis_redistribute_cmd,
+DEFPY_YANG(isis_redistribute, isis_redistribute_cmd,
"[no] redistribute <ipv4|ipv6>$ip " PROTO_REDIST_STR
- "$proto"
- " <level-1|level-2>$level"
- " [{metric (0-16777215)|route-map WORD}]",
+ "$proto <level-1|level-2>$level [{metric (0-16777215)|route-map WORD}]",
NO_STR REDIST_STR
"Redistribute IPv4 routes\n"
"Redistribute IPv6 routes\n" PROTO_REDIST_HELP
"Redistribute into level-1\n"
"Redistribute into level-2\n"
"Metric for redistributed routes\n"
- "ISIS default metric\n"
+ "IS-IS default metric\n"
"Route map reference\n"
"Pointer to route-map entries\n")
{
@@ -1249,16 +1293,8 @@ void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/multi-topology
*/
-DEFPY(isis_topology, isis_topology_cmd,
- "[no] topology "
- "<ipv4-unicast"
- "|ipv4-mgmt"
- "|ipv6-unicast"
- "|ipv4-multicast"
- "|ipv6-multicast"
- "|ipv6-mgmt"
- "|ipv6-dstsrc>$topology "
- "[overload]$overload",
+DEFPY_YANG(isis_topology, isis_topology_cmd,
+ "[no] topology <ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology [overload]$overload",
NO_STR
"Configure IS-IS topologies\n"
"IPv4 unicast topology\n"
@@ -1358,7 +1394,7 @@ void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/segment-routing/enabled
*/
-DEFPY (isis_sr_enable,
+DEFPY_YANG (isis_sr_enable,
isis_sr_enable_cmd,
"segment-routing on",
SR_STR
@@ -1370,7 +1406,7 @@ DEFPY (isis_sr_enable,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_isis_sr_enable,
+DEFPY_YANG (no_isis_sr_enable,
no_isis_sr_enable_cmd,
"no segment-routing [on]",
NO_STR
@@ -1395,7 +1431,7 @@ void cli_show_isis_sr_enabled(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/segment-routing/srgb
*/
-DEFPY (isis_sr_global_block_label_range,
+DEFPY_YANG (isis_sr_global_block_label_range,
isis_sr_global_block_label_range_cmd,
"segment-routing global-block (16-1048575)$lower_bound (16-1048575)$upper_bound",
SR_STR
@@ -1411,7 +1447,7 @@ DEFPY (isis_sr_global_block_label_range,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_isis_sr_global_block_label_range,
+DEFPY_YANG (no_isis_sr_global_block_label_range,
no_isis_sr_global_block_label_range_cmd,
"no segment-routing global-block [(16-1048575) (16-1048575)]",
NO_STR
@@ -1439,7 +1475,7 @@ void cli_show_isis_srgb(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/segment-routing/srlb
*/
-DEFPY (isis_sr_local_block_label_range,
+DEFPY_YANG (isis_sr_local_block_label_range,
isis_sr_local_block_label_range_cmd,
"segment-routing local-block (16-1048575)$lower_bound (16-1048575)$upper_bound",
SR_STR
@@ -1455,7 +1491,7 @@ DEFPY (isis_sr_local_block_label_range,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_isis_sr_local_block_label_range,
+DEFPY_YANG (no_isis_sr_local_block_label_range,
no_isis_sr_local_block_label_range_cmd,
"no segment-routing local-block [(16-1048575) (16-1048575)]",
NO_STR
@@ -1483,7 +1519,7 @@ void cli_show_isis_srlb(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/segment-routing/msd/node-msd
*/
-DEFPY (isis_sr_node_msd,
+DEFPY_YANG (isis_sr_node_msd,
isis_sr_node_msd_cmd,
"segment-routing node-msd (1-16)$msd",
SR_STR
@@ -1496,7 +1532,7 @@ DEFPY (isis_sr_node_msd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_isis_sr_node_msd,
+DEFPY_YANG (no_isis_sr_node_msd,
no_isis_sr_node_msd_cmd,
"no segment-routing node-msd [(1-16)]",
NO_STR
@@ -1520,7 +1556,7 @@ void cli_show_isis_node_msd(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid
*/
-DEFPY (isis_sr_prefix_sid,
+DEFPY_YANG (isis_sr_prefix_sid,
isis_sr_prefix_sid_cmd,
"segment-routing prefix\
<A.B.C.D/M|X:X::X:X/M>$prefix\
@@ -1559,7 +1595,7 @@ DEFPY (isis_sr_prefix_sid,
prefix_str);
}
-DEFPY (no_isis_sr_prefix_sid,
+DEFPY_YANG (no_isis_sr_prefix_sid,
no_isis_sr_prefix_sid_cmd,
"no segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\
[<absolute$sid_type (16-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]",
@@ -1611,7 +1647,7 @@ void cli_show_isis_prefix_sid(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
*/
-DEFPY(isis_passive, isis_passive_cmd, "[no] isis passive",
+DEFPY_YANG(isis_passive, isis_passive_cmd, "[no] isis passive",
NO_STR
"IS-IS routing protocol\n"
"Configure the passive mode for interface\n")
@@ -1634,7 +1670,7 @@ void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-interface:lib/interface/frr-isisd:isis/password
*/
-DEFPY(isis_passwd, isis_passwd_cmd, "isis password <md5|clear>$type WORD$pwd",
+DEFPY_YANG(isis_passwd, isis_passwd_cmd, "isis password <md5|clear>$type WORD$pwd",
"IS-IS routing protocol\n"
"Configure the authentication password for a circuit\n"
"HMAC-MD5 authentication\n"
@@ -1651,7 +1687,7 @@ DEFPY(isis_passwd, isis_passwd_cmd, "isis password <md5|clear>$type WORD$pwd",
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_passwd, no_isis_passwd_cmd, "no isis password [<md5|clear> WORD]",
+DEFPY_YANG(no_isis_passwd, no_isis_passwd_cmd, "no isis password [<md5|clear> WORD]",
NO_STR
"IS-IS routing protocol\n"
"Configure the authentication password for a circuit\n"
@@ -1676,7 +1712,7 @@ void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/metric
*/
-DEFPY(isis_metric, isis_metric_cmd,
+DEFPY_YANG(isis_metric, isis_metric_cmd,
"isis metric [level-1|level-2]$level (0-16777215)$met",
"IS-IS routing protocol\n"
"Set default metric for circuit\n"
@@ -1694,7 +1730,7 @@ DEFPY(isis_metric, isis_metric_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_metric, no_isis_metric_cmd,
+DEFPY_YANG(no_isis_metric, no_isis_metric_cmd,
"no isis metric [level-1|level-2]$level [(0-16777215)]",
NO_STR
"IS-IS routing protocol\n"
@@ -1730,7 +1766,7 @@ void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval
*/
-DEFPY(isis_hello_interval, isis_hello_interval_cmd,
+DEFPY_YANG(isis_hello_interval, isis_hello_interval_cmd,
"isis hello-interval [level-1|level-2]$level (1-600)$intv",
"IS-IS routing protocol\n"
"Set Hello interval\n"
@@ -1750,7 +1786,7 @@ DEFPY(isis_hello_interval, isis_hello_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_hello_interval, no_isis_hello_interval_cmd,
+DEFPY_YANG(no_isis_hello_interval, no_isis_hello_interval_cmd,
"no isis hello-interval [level-1|level-2]$level [(1-600)]",
NO_STR
"IS-IS routing protocol\n"
@@ -1788,7 +1824,7 @@ void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier
*/
-DEFPY(isis_hello_multiplier, isis_hello_multiplier_cmd,
+DEFPY_YANG(isis_hello_multiplier, isis_hello_multiplier_cmd,
"isis hello-multiplier [level-1|level-2]$level (2-100)$mult",
"IS-IS routing protocol\n"
"Set multiplier for Hello holding time\n"
@@ -1808,7 +1844,7 @@ DEFPY(isis_hello_multiplier, isis_hello_multiplier_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_hello_multiplier, no_isis_hello_multiplier_cmd,
+DEFPY_YANG(no_isis_hello_multiplier, no_isis_hello_multiplier_cmd,
"no isis hello-multiplier [level-1|level-2]$level [(2-100)]",
NO_STR
"IS-IS routing protocol\n"
@@ -1847,7 +1883,7 @@ void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode,
* XPath:
* /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake
*/
-DEFPY(isis_threeway_adj, isis_threeway_adj_cmd, "[no] isis three-way-handshake",
+DEFPY_YANG(isis_threeway_adj, isis_threeway_adj_cmd, "[no] isis three-way-handshake",
NO_STR
"IS-IS commands\n"
"Enable/Disable three-way handshake\n")
@@ -1870,7 +1906,7 @@ void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding
*/
-DEFPY(isis_hello_padding, isis_hello_padding_cmd, "[no] isis hello padding",
+DEFPY_YANG(isis_hello_padding, isis_hello_padding_cmd, "[no] isis hello padding",
NO_STR
"IS-IS routing protocol\n"
"Add padding to IS-IS hello packets\n"
@@ -1894,7 +1930,7 @@ void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval
*/
-DEFPY(csnp_interval, csnp_interval_cmd,
+DEFPY_YANG(csnp_interval, csnp_interval_cmd,
"isis csnp-interval (1-600)$intv [level-1|level-2]$level",
"IS-IS routing protocol\n"
"Set CSNP interval in seconds\n"
@@ -1914,7 +1950,7 @@ DEFPY(csnp_interval, csnp_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_csnp_interval, no_csnp_interval_cmd,
+DEFPY_YANG(no_csnp_interval, no_csnp_interval_cmd,
"no isis csnp-interval [(1-600)] [level-1|level-2]$level",
NO_STR
"IS-IS routing protocol\n"
@@ -1952,7 +1988,7 @@ void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval
*/
-DEFPY(psnp_interval, psnp_interval_cmd,
+DEFPY_YANG(psnp_interval, psnp_interval_cmd,
"isis psnp-interval (1-120)$intv [level-1|level-2]$level",
"IS-IS routing protocol\n"
"Set PSNP interval in seconds\n"
@@ -1972,7 +2008,7 @@ DEFPY(psnp_interval, psnp_interval_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_psnp_interval, no_psnp_interval_cmd,
+DEFPY_YANG(no_psnp_interval, no_psnp_interval_cmd,
"no isis psnp-interval [(1-120)] [level-1|level-2]$level",
NO_STR
"IS-IS routing protocol\n"
@@ -2010,16 +2046,8 @@ void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology
*/
-DEFPY(circuit_topology, circuit_topology_cmd,
- "[no] isis topology"
- "<ipv4-unicast"
- "|ipv4-mgmt"
- "|ipv6-unicast"
- "|ipv4-multicast"
- "|ipv6-multicast"
- "|ipv6-mgmt"
- "|ipv6-dstsrc"
- ">$topology",
+DEFPY_YANG(circuit_topology, circuit_topology_cmd,
+ "[no] isis topology<ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology",
NO_STR
"IS-IS routing protocol\n"
"Configure interface IS-IS topologies\n"
@@ -2103,7 +2131,7 @@ void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type
*/
-DEFPY(isis_circuit_type, isis_circuit_type_cmd,
+DEFPY_YANG(isis_circuit_type, isis_circuit_type_cmd,
"isis circuit-type <level-1|level-1-2|level-2-only>$type",
"IS-IS routing protocol\n"
"Configure circuit type for interface\n"
@@ -2118,7 +2146,7 @@ DEFPY(isis_circuit_type, isis_circuit_type_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd,
+DEFPY_YANG(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 routing protocol\n"
@@ -2196,7 +2224,7 @@ void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type
*/
-DEFPY(isis_network, isis_network_cmd, "[no] isis network point-to-point",
+DEFPY_YANG(isis_network, isis_network_cmd, "[no] isis network point-to-point",
NO_STR
"IS-IS routing protocol\n"
"Set network type\n"
@@ -2221,7 +2249,7 @@ void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/priority
*/
-DEFPY(isis_priority, isis_priority_cmd,
+DEFPY_YANG(isis_priority, isis_priority_cmd,
"isis priority (0-127)$prio [level-1|level-2]$level",
"IS-IS routing protocol\n"
"Set priority for Designated Router election\n"
@@ -2239,7 +2267,7 @@ DEFPY(isis_priority, isis_priority_cmd,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(no_isis_priority, no_isis_priority_cmd,
+DEFPY_YANG(no_isis_priority, no_isis_priority_cmd,
"no isis priority [(0-127)] [level-1|level-2]$level",
NO_STR
"IS-IS routing protocol\n"
@@ -2275,7 +2303,7 @@ void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-isisd:isis/instance/log-adjacency-changes
*/
-DEFPY(log_adj_changes, log_adj_changes_cmd, "[no] log-adjacency-changes",
+DEFPY_YANG(log_adj_changes, log_adj_changes_cmd, "[no] log-adjacency-changes",
NO_STR "Log changes in adjacency state\n")
{
nb_cli_enqueue_change(vty, "./log-adjacency-changes", NB_OP_MODIFY,
@@ -2301,6 +2329,7 @@ void isis_cli_init(void)
install_element(INTERFACE_NODE, &ip6_router_isis_cmd);
install_element(INTERFACE_NODE, &no_ip_router_isis_cmd);
install_element(INTERFACE_NODE, &isis_bfd_cmd);
+ install_element(INTERFACE_NODE, &isis_bfd_profile_cmd);
install_element(ISIS_NODE, &net_cmd);
diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c
index 7aae326a4f..929b4c26e8 100644
--- a/isisd/isis_csm.c
+++ b/isisd/isis_csm.c
@@ -48,8 +48,6 @@
#include "isisd/isis_events.h"
#include "isisd/isis_errors.h"
-extern struct isis *isis;
-
static const char *const csm_statestr[] = {"C_STATE_NA", "C_STATE_INIT",
"C_STATE_CONF", "C_STATE_UP"};
@@ -66,6 +64,7 @@ struct isis_circuit *
isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
{
int old_state;
+ struct isis *isis = NULL;
old_state = circuit ? circuit->state : C_STATE_NA;
if (IS_DEBUG_EVENTS)
@@ -86,6 +85,13 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
case IF_UP_FROM_Z:
circuit = isis_circuit_new();
isis_circuit_if_add(circuit, (struct interface *)arg);
+ isis = isis_lookup_by_vrfid(circuit->interface->vrf_id);
+ if (isis == NULL) {
+ zlog_warn(
+ " %s : ISIS routing instance not found",
+ __func__);
+ break;
+ }
listnode_add(isis->init_circ_list, circuit);
circuit->state = C_STATE_INIT;
break;
@@ -111,7 +117,8 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
circuit->state = C_STATE_UP;
isis_event_circuit_state_change(circuit, circuit->area,
1);
- listnode_delete(isis->init_circ_list, circuit);
+ listnode_delete(circuit->area->isis->init_circ_list,
+ circuit);
break;
case IF_UP_FROM_Z:
assert(circuit);
@@ -122,6 +129,14 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
break;
case IF_DOWN_FROM_Z:
isis_circuit_if_del(circuit, (struct interface *)arg);
+ isis = isis_lookup_by_vrfid(circuit->interface->vrf_id);
+ if (isis == NULL) {
+ zlog_warn(
+ "%s : ISIS routing instance not found",
+ __func__);
+ break;
+ }
+
listnode_delete(isis->init_circ_list, circuit);
isis_circuit_del(circuit);
circuit = NULL;
@@ -174,6 +189,15 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
circuit->state = C_STATE_INIT;
isis_event_circuit_state_change(
circuit, (struct isis_area *)arg, 0);
+
+ isis = isis_lookup_by_vrfid(circuit->interface->vrf_id);
+ if (isis == NULL) {
+ zlog_warn(
+ "%s : ISIS routing instance not found",
+ __func__);
+ break;
+ }
+
listnode_add(isis->init_circ_list, circuit);
break;
case IF_DOWN_FROM_Z:
diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c
index 5c15d1d29d..06fb41430c 100644
--- a/isisd/isis_dlpi.c
+++ b/isisd/isis_dlpi.c
@@ -566,8 +566,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level)
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",
+ "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;
}
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c
index 8df1304866..318fb9fab8 100644
--- a/isisd/isis_dr.c
+++ b/isisd/isis_dr.c
@@ -225,7 +225,7 @@ int isis_dr_resign(struct isis_circuit *circuit, int level)
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);
+ memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(id) = circuit->circuit_id;
LSP_FRAGMENT(id) = 0;
lsp_purge_pseudo(id, circuit, level);
@@ -278,7 +278,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level)
/* 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);
+ memcpy(circuit->u.bc.l1_desig_is, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN);
*(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
circuit->circuit_id;
@@ -299,7 +300,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level)
/* 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);
+ memcpy(circuit->u.bc.l2_desig_is, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN);
*(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
circuit->circuit_id;
diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c
index 921e23d33a..e34c59be11 100644
--- a/isisd/isis_dynhn.c
+++ b/isisd/isis_dynhn.c
@@ -45,11 +45,11 @@ extern struct host host;
struct list *dyn_cache = NULL;
static int dyn_cache_cleanup(struct thread *);
-void dyn_cache_init(void)
+void dyn_cache_init(struct isis *isis)
{
if (dyn_cache == NULL)
dyn_cache = list_new();
- thread_add_timer(master, dyn_cache_cleanup, NULL, 120,
+ thread_add_timer(master, dyn_cache_cleanup, isis, 120,
&isis->t_dync_clean);
return;
}
@@ -59,19 +59,22 @@ static int dyn_cache_cleanup(struct thread *thread)
struct listnode *node, *nnode;
struct isis_dynhn *dyn;
time_t now = time(NULL);
+ struct isis *isis = NULL;
+
+ isis = THREAD_ARG(thread);
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_add_timer(master, dyn_cache_cleanup, NULL, 120,
- &isis->t_dync_clean);
+ thread_add_timer(master, dyn_cache_cleanup, isis, 120,
+ &isis->t_dync_clean);
+
return ISIS_OK;
}
@@ -132,11 +135,14 @@ void isis_dynhn_remove(const uint8_t *id)
* 2 0000.0000.0002 bar-gw
* * 0000.0000.0004 this-gw
*/
-void dynhn_print_all(struct vty *vty)
+void dynhn_print_all(struct vty *vty, struct isis *isis)
{
struct listnode *node;
struct isis_dynhn *dyn;
+ vty_out(vty, "vrf : %s\n", isis->name);
+ if (!isis->sysid_set)
+ return;
vty_out(vty, "Level System ID Dynamic Hostname\n");
for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) {
vty_out(vty, "%-7d", dyn->level);
diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h
index 27133bd3fd..2cfc43fc17 100644
--- a/isisd/isis_dynhn.h
+++ b/isisd/isis_dynhn.h
@@ -30,11 +30,11 @@ struct isis_dynhn {
int level;
};
-void dyn_cache_init(void);
+void dyn_cache_init(struct isis *isis);
void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level);
void isis_dynhn_remove(const uint8_t *id);
struct isis_dynhn *dynhn_find_by_id(const uint8_t *id);
struct isis_dynhn *dynhn_find_by_name(const char *hostname);
-void dynhn_print_all(struct vty *vty);
+void dynhn_print_all(struct vty *vty, struct isis *isis);
#endif /* _ZEBRA_ISIS_DYNHN_H */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 63303e2308..90c7c0efb5 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -200,16 +200,12 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
|| (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) {
if (IS_DEBUG_SNP_PACKETS) {
zlog_debug(
- "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s",
+ "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, rawlspid_print(lsp->hdr.lsp_id),
lsp->hdr.seqno, lsp->hdr.checksum,
lsp->hdr.rem_lifetime);
zlog_debug(
- "ISIS-Snp (%s): is equal to ours seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s",
+ "ISIS-Snp (%s): is equal to ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, seqno, checksum, rem_lifetime);
}
return LSP_EQUAL;
@@ -234,28 +230,22 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
&& lsp->hdr.rem_lifetime)))) {
if (IS_DEBUG_SNP_PACKETS) {
zlog_debug(
- "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s",
+ "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
checksum, rem_lifetime);
zlog_debug(
- "ISIS-Snp (%s): is newer than ours seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s",
+ "ISIS-Snp (%s): is newer than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, lsp->hdr.seqno, lsp->hdr.checksum,
lsp->hdr.rem_lifetime);
}
return LSP_NEWER;
}
if (IS_DEBUG_SNP_PACKETS) {
- zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
+ zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
checksum, rem_lifetime);
zlog_debug(
- "ISIS-Snp (%s): is older than ours seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
+ "ISIS-Snp (%s): is older than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
areatag, lsp->hdr.seqno, lsp->hdr.checksum,
lsp->hdr.rem_lifetime);
}
@@ -348,13 +338,17 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno)
static void lsp_purge_add_poi(struct isis_lsp *lsp,
const uint8_t *sender)
{
+ if (lsp->area == NULL)
+ return;
+
if (!lsp->area->purge_originator)
return;
/* add purge originator identification */
if (!lsp->tlvs)
lsp->tlvs = isis_alloc_tlvs();
- isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender);
+ isis_tlvs_set_purge_originator(lsp->tlvs, lsp->area->isis->sysid,
+ sender);
isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get());
}
@@ -601,7 +595,8 @@ static void lsp_set_time(struct isis_lsp *lsp)
stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime);
}
-void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag)
+void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag,
+ struct isis *isis)
{
struct isis_dynhn *dyn = NULL;
char id[SYSID_STRLEN];
@@ -617,6 +612,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag)
snprintf(id, sizeof(id), "%.14s", cmd_hostname_get());
else
memcpy(id, sysid_print(lsp_id), 15);
+
if (frag)
sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id),
LSP_FRAGMENT(lsp_id));
@@ -648,29 +644,31 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size)
}
/* this function prints the lsp on show isis database */
-void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost)
+void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost,
+ struct isis *isis)
{
char LSPid[255];
char age_out[8];
char b[200];
- lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1);
+ lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1, isis);
vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' ');
- vty_out(vty, "%5" PRIu16 " ", lsp->hdr.pdu_len);
- vty_out(vty, "0x%08" PRIx32 " ", lsp->hdr.seqno);
- vty_out(vty, "0x%04" PRIx16 " ", lsp->hdr.checksum);
+ vty_out(vty, "%5hu ", lsp->hdr.pdu_len);
+ vty_out(vty, "0x%08x ", lsp->hdr.seqno);
+ vty_out(vty, "0x%04hx ", lsp->hdr.checksum);
if (lsp->hdr.rem_lifetime == 0) {
snprintf(age_out, sizeof(age_out), "(%d)", lsp->age_out);
age_out[7] = '\0';
vty_out(vty, "%7s ", age_out);
} else
- vty_out(vty, " %5" PRIu16 " ", lsp->hdr.rem_lifetime);
+ vty_out(vty, " %5hu ", lsp->hdr.rem_lifetime);
vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b)));
}
-void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost)
+void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost,
+ struct isis *isis)
{
- lsp_print(lsp, vty, dynhost);
+ lsp_print(lsp, vty, dynhost, isis);
if (lsp->tlvs)
vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs));
vty_out(vty, "\n");
@@ -678,19 +676,19 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost)
/* print all the lsps info in the local lspdb */
int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail,
- char dynhost)
+ char dynhost, struct isis *isis)
{
struct isis_lsp *lsp;
int lsp_count = 0;
if (detail == ISIS_UI_LEVEL_BRIEF) {
frr_each (lspdb, head, lsp) {
- lsp_print(lsp, vty, dynhost);
+ lsp_print(lsp, vty, dynhost, isis);
lsp_count++;
}
} else if (detail == ISIS_UI_LEVEL_DETAIL) {
frr_each (lspdb, head, lsp) {
- lsp_print_detail(lsp, vty, dynhost);
+ lsp_print_detail(lsp, vty, dynhost, isis);
lsp_count++;
}
}
@@ -923,10 +921,10 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
}
/* Add Router Capability TLV. */
- if (isis->router_id != 0) {
+ if (area->isis->router_id != 0) {
struct isis_router_cap cap = {};
- cap.router_id.s_addr = isis->router_id;
+ cap.router_id.s_addr = area->isis->router_id;
/* Add SR Sub-TLVs if SR is enabled. */
if (area->srdb.enabled) {
@@ -964,8 +962,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
* into LSP. TE router ID will be the same if MPLS-TE
* is not activate or MPLS-TE router-id not specified
*/
- if (isis->router_id != 0) {
- struct in_addr id = {.s_addr = isis->router_id};
+ if (area->isis->router_id != 0) {
+ struct in_addr id = {.s_addr = area->isis->router_id};
inet_ntop(AF_INET, &id, buf, sizeof(buf));
lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.",
area->area_tag, buf);
@@ -994,7 +992,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
if (fabricd) {
lsp_debug(
- "ISIS (%s): Adding tier %" PRIu8 " spine-leaf-extension tlv.",
+ "ISIS (%s): Adding tier %hhu spine-leaf-extension tlv.",
area->area_tag, fabricd_tier(area));
isis_tlvs_add_spine_leaf(lsp->tlvs, fabricd_tier(area), true,
false, false, false);
@@ -1220,7 +1218,8 @@ int lsp_generate(struct isis_area *area, int level)
return ISIS_ERROR;
memset(&lspid, 0, ISIS_SYS_ID_LEN + 2);
- memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN);
+
+ memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN);
/* only builds the lsp if the area shares the level */
oldlsp = lsp_search(&area->lspdb[level - 1], lspid);
@@ -1256,9 +1255,7 @@ int lsp_generate(struct isis_area *area, int level)
&area->t_lsp_refresh[level - 1]);
if (IS_DEBUG_UPDATE_PACKETS) {
- zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16
- ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
- ", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
+ zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
area->area_tag, level,
rawlspid_print(newlsp->hdr.lsp_id),
newlsp->hdr.pdu_len, newlsp->hdr.seqno,
@@ -1293,9 +1290,8 @@ static int lsp_regenerate(struct isis_area *area, int level)
return ISIS_ERROR;
head = &area->lspdb[level - 1];
-
memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
- memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(lspid, area->isis->sysid, ISIS_SYS_ID_LEN);
lsp = lsp_search(head, lspid);
@@ -1341,9 +1337,7 @@ static int lsp_regenerate(struct isis_area *area, int level)
if (IS_DEBUG_UPDATE_PACKETS) {
zlog_debug(
- "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %" PRIu16
- ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
- ", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
+ "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id),
lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
lsp->hdr.rem_lifetime, refresh_time);
@@ -1413,13 +1407,12 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level,
return ISIS_ERROR;
sched_debug(
- "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs"
- " Caller: %s %s:%d",
+ "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs Caller: %s %s:%d",
area->area_tag, circuit_t2string(level),
all_pseudo ? "" : "not ",
func, file, line);
- memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(id, area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0;
now = time(NULL);
@@ -1444,8 +1437,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level,
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)",
+ "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;
@@ -1541,7 +1533,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
*/
uint8_t ne_id[ISIS_SYS_ID_LEN + 1];
- memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(ne_id, area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(ne_id) = 0;
if (circuit->area->oldmetric) {
@@ -1553,7 +1545,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
}
if (circuit->area->newmetric) {
isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST,
- ne_id, 0, circuit->ext);
+ ne_id, 0, NULL);
lsp_debug(
"ISIS (%s): Adding %s.%02x as te-style neighbor (self)",
area->area_tag, sysid_print(ne_id),
@@ -1595,7 +1587,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
if (circuit->area->newmetric) {
isis_tlvs_add_extended_reach(lsp->tlvs,
ISIS_MT_IPV4_UNICAST,
- ne_id, 0, circuit->ext);
+ ne_id, 0, NULL);
lsp_debug(
"ISIS (%s): Adding %s.%02x as te-style neighbor (peer)",
area->area_tag, sysid_print(ne_id),
@@ -1619,7 +1611,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
|| (circuit->u.bc.is_dr[level - 1] == 0))
return ISIS_ERROR;
- memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_FRAGMENT(lsp_id) = 0;
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
@@ -1656,9 +1648,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
if (IS_DEBUG_UPDATE_PACKETS) {
zlog_debug(
- "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %" PRIu16
- ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
- ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
+ "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
circuit->area->area_tag, level,
rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
lsp->hdr.seqno, lsp->hdr.checksum,
@@ -1681,7 +1671,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
|| (circuit->u.bc.is_dr[level - 1] == 0))
return ISIS_ERROR;
- memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
LSP_FRAGMENT(lsp_id) = 0;
@@ -1713,9 +1703,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
if (IS_DEBUG_UPDATE_PACKETS) {
zlog_debug(
- "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %" PRIu16
- ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
- ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
+ "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
circuit->area->area_tag, level,
rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
lsp->hdr.seqno, lsp->hdr.checksum,
@@ -1740,7 +1728,7 @@ static int lsp_l1_refresh_pseudo(struct thread *thread)
if ((circuit->u.bc.is_dr[0] == 0)
|| (circuit->is_type & IS_LEVEL_1) == 0) {
- memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(id, circuit->area->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);
@@ -1762,7 +1750,7 @@ static int lsp_l2_refresh_pseudo(struct thread *thread)
if ((circuit->u.bc.is_dr[1] == 0)
|| (circuit->is_type & IS_LEVEL_2) == 0) {
- memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(id, circuit->area->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);
@@ -1790,7 +1778,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
area->area_tag, circuit_t2string(level),
circuit->interface->name);
- memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
+ memcpy(lsp_id, area->isis->sysid, ISIS_SYS_ID_LEN);
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
LSP_FRAGMENT(lsp_id) = 0;
now = time(NULL);
@@ -1817,8 +1805,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
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)",
+ "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;
@@ -1851,8 +1838,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
} else {
timeout = 100;
sched_debug(
- "ISIS (%s): Last generation was more than lsp_gen_interval ago."
- " Scheduling for execution in %ld ms.",
+ "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution in %ld ms.",
area->area_tag, timeout);
}
@@ -1930,8 +1916,7 @@ int lsp_tick(struct thread *thread)
if (lsp->age_out == 0) {
zlog_debug(
- "ISIS-Upd (%s): L%u LSP %s seq "
- "0x%08" PRIx32 " aged out",
+ "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out",
area->area_tag, lsp->level,
rawlspid_print(lsp->hdr.lsp_id),
lsp->hdr.seqno);
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
index 4cbca5d517..6cbea47496 100644
--- a/isisd/isis_lsp.h
+++ b/isisd/isis_lsp.h
@@ -29,6 +29,7 @@
PREDECL_RBTREE_UNIQ(lspdb)
+struct isis;
/* 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
@@ -115,11 +116,14 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
struct isis_tlvs *tlvs, struct stream *stream,
struct isis_area *area, int level, bool confusion);
void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno);
-void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag);
-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);
+void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag,
+ struct isis *isis);
+void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost,
+ struct isis *isis);
+void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost,
+ struct isis *isis);
int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail,
- char dynhost);
+ char dynhost, struct isis *isis);
/* sets SRMflags for all active circuits of an lsp */
void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set);
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index cb5b47bbd6..ed4b206851 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -101,6 +101,7 @@ void sigusr1(void);
static __attribute__((__noreturn__)) void terminate(int i)
{
+ isis_terminate();
isis_sr_term();
isis_zebra_stop();
exit(i);
@@ -185,8 +186,7 @@ FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT,
.proghelp = "Implementation of the IS-IS routing protocol.",
#endif
.copyright =
- "Copyright (c) 2001-2002 Sampo Saaristo,"
- " Ofer Wald and Hannes Gredler",
+ "Copyright (c) 2001-2002 Sampo Saaristo, Ofer Wald and Hannes Gredler",
.signals = isisd_signals,
.n_signals = array_size(isisd_signals),
@@ -234,7 +234,8 @@ int main(int argc, char **argv, char **envp)
}
/* thread master */
- master = frr_init();
+ isis_master_init(frr_init());
+ master = im->master;
/*
* initializations
@@ -260,7 +261,7 @@ int main(int argc, char **argv, char **envp)
mt_init();
/* create the global 'isis' instance */
- isis_new(1, VRF_DEFAULT);
+ isis_global_instance_create();
isis_zebra_init(master, instance);
isis_bfd_init();
diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c
index 27d06e8da7..3aedd8ba1f 100644
--- a/isisd/isis_misc.c
+++ b/isisd/isis_misc.c
@@ -190,7 +190,7 @@ const char *nlpid2str(uint8_t nlpid)
case NLPID_ESIS:
return "ES-IS";
default:
- snprintf(buf, sizeof(buf), "%" PRIu8, nlpid);
+ snprintf(buf, sizeof(buf), "%hhu", nlpid);
return buf;
}
}
@@ -437,15 +437,18 @@ struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen)
* 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 uint8_t *sysid)
+const char *print_sys_hostname(uint8_t *sysid)
{
struct isis_dynhn *dyn;
+ struct isis *isis = NULL;
if (!sysid)
return "nullsysid";
/* For our system ID return our host name */
- if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0)
+ isis = isis_lookup_by_sysid(sysid);
+
+ if (isis != NULL)
return cmd_hostname_get();
dyn = dynhn_find_by_id(sysid);
diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h
index 5cdbbfb058..c6a5832f33 100644
--- a/isisd/isis_misc.h
+++ b/isisd/isis_misc.h
@@ -49,7 +49,7 @@ const char *time2string(uint32_t);
const char *nlpid2str(uint8_t nlpid);
/* typedef struct nlpids nlpids; */
char *nlpid2string(struct nlpids *);
-const char *print_sys_hostname(const uint8_t *sysid);
+const char *print_sys_hostname(uint8_t *sysid);
void zlog_dump_data(void *data, int len);
/*
diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c
index 5c262049a9..9465c5e75c 100644
--- a/isisd/isis_mt.c
+++ b/isisd/isis_mt.c
@@ -72,7 +72,7 @@ const char *isis_mtid2str(uint16_t mtid)
case ISIS_MT_IPV6_DSTSRC:
return "ipv6-dstsrc";
default:
- snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
+ snprintf(buf, sizeof(buf), "%hu", mtid);
return buf;
}
}
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 1d842eb13b..2b8b02e3f1 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -574,11 +574,24 @@ const struct frr_yang_module_info frr_isisd_info = {
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring",
.cbs = {
- .modify = lib_interface_isis_bfd_monitoring_modify,
+ .apply_finish = lib_interface_isis_bfd_monitoring_apply_finish,
.cli_show = cli_show_ip_isis_bfd_monitoring,
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled",
+ .cbs = {
+ .modify = lib_interface_isis_bfd_monitoring_enabled_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile",
+ .cbs = {
+ .modify = lib_interface_isis_bfd_monitoring_profile_modify,
+ .destroy = lib_interface_isis_bfd_monitoring_profile_destroy,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval",
.cbs = {
.cli_show = cli_show_ip_isis_csnp_interval,
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index e887b1a388..a9401bc86a 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -171,7 +171,14 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_ipv4_routing_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args);
-int lib_interface_isis_bfd_monitoring_modify(struct nb_cb_modify_args *args);
+void lib_interface_isis_bfd_monitoring_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+int lib_interface_isis_bfd_monitoring_enabled_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_bfd_monitoring_profile_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_bfd_monitoring_profile_destroy(
+ struct nb_cb_destroy_args *args);
int isis_instance_segment_routing_enabled_modify(
struct nb_cb_modify_args *args);
int isis_instance_segment_routing_enabled_modify(
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index c17433cb27..ffc3d5b2e0 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -58,11 +58,11 @@ int isis_instance_create(struct nb_cb_create_args *args)
return NB_OK;
area_tag = yang_dnode_get_string(args->dnode, "./area-tag");
- area = isis_area_lookup(area_tag);
+ area = isis_area_lookup(area_tag, VRF_DEFAULT);
if (area)
return NB_ERR_INCONSISTENCY;
- area = isis_area_create(area_tag);
+ area = isis_area_create(area_tag, VRF_DEFAULT_NAME);
/* save area in dnode to avoid looking it up all the time */
nb_running_set_entry(args->dnode, area);
@@ -77,7 +77,7 @@ int isis_instance_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
area = nb_running_unset_entry(args->dnode);
- isis_area_destroy(area->area_tag);
+ isis_area_destroy(area);
return NB_OK;
}
@@ -113,6 +113,10 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
switch (args->event) {
case NB_EV_VALIDATE:
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ if (area == NULL)
+ return NB_ERR_VALIDATION;
+
addr.addr_len = dotformat2buff(buff, net_title);
memcpy(addr.area_addr, buff, addr.addr_len);
if (addr.area_addr[addr.addr_len - 1] != 0) {
@@ -121,9 +125,9 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
"nsel byte (last byte) in area address must be 0");
return NB_ERR_VALIDATION;
}
- if (isis->sysid_set) {
+ if (area->isis->sysid_set) {
/* Check that the SystemID portions match */
- if (memcmp(isis->sysid, GETSYSID((&addr)),
+ if (memcmp(area->isis->sysid, GETSYSID((&addr)),
ISIS_SYS_ID_LEN)) {
snprintf(
args->errmsg, args->errmsg_len,
@@ -145,12 +149,13 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
area = nb_running_get_entry(args->dnode, NULL, true);
addrr = args->resource->ptr;
- if (isis->sysid_set == 0) {
+ if (area->isis->sysid_set == 0) {
/*
* First area address - get the SystemID for this router
*/
- memcpy(isis->sysid, GETSYSID(addrr), ISIS_SYS_ID_LEN);
- isis->sysid_set = 1;
+ memcpy(area->isis->sysid, GETSYSID(addrr),
+ ISIS_SYS_ID_LEN);
+ area->isis->sysid_set = 1;
} else {
/* check that we don't already have this address */
for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node,
@@ -200,6 +205,7 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args)
addr.addr_len = dotformat2buff(buff, net_title);
memcpy(addr.area_addr, buff, (int)addr.addr_len);
area = nb_running_get_entry(args->dnode, NULL, true);
+
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))
@@ -214,8 +220,8 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args)
* 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;
+ memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN);
+ area->isis->sysid_set = 0;
if (IS_DEBUG_EVENTS)
zlog_debug("Router has no SystemID");
}
@@ -1503,7 +1509,7 @@ int isis_instance_segment_routing_srgb_lower_bound_modify(
switch (args->event) {
case NB_EV_VALIDATE:
if (!IS_MPLS_UNRESERVED_LABEL(lower_bound)) {
- zlog_warn("Invalid SRGB lower bound: %" PRIu32,
+ zlog_warn("Invalid SRGB lower bound: %u",
lower_bound);
return NB_ERR_VALIDATION;
}
@@ -1528,7 +1534,7 @@ int isis_instance_segment_routing_srgb_upper_bound_modify(
switch (args->event) {
case NB_EV_VALIDATE:
if (!IS_MPLS_UNRESERVED_LABEL(upper_bound)) {
- zlog_warn("Invalid SRGB upper bound: %" PRIu32,
+ zlog_warn("Invalid SRGB upper bound: %u",
upper_bound);
return NB_ERR_VALIDATION;
}
@@ -1601,7 +1607,7 @@ int isis_instance_segment_routing_srlb_lower_bound_modify(
switch (args->event) {
case NB_EV_VALIDATE:
if (!IS_MPLS_UNRESERVED_LABEL(lower_bound)) {
- zlog_warn("Invalid SRLB lower bound: %" PRIu32,
+ zlog_warn("Invalid SRLB lower bound: %u",
lower_bound);
return NB_ERR_VALIDATION;
}
@@ -1626,7 +1632,7 @@ int isis_instance_segment_routing_srlb_upper_bound_modify(
switch (args->event) {
case NB_EV_VALIDATE:
if (!IS_MPLS_UNRESERVED_LABEL(upper_bound)) {
- zlog_warn("Invalid SRLB upper bound: %" PRIu32,
+ zlog_warn("Invalid SRLB upper bound: %u",
upper_bound);
return NB_ERR_VALIDATION;
}
@@ -1822,7 +1828,7 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo
*/
int lib_interface_isis_create(struct nb_cb_create_args *args)
{
- struct isis_area *area;
+ struct isis_area *area = NULL;
struct interface *ifp;
struct isis_circuit *circuit;
const char *area_tag = yang_dnode_get_string(args->dnode, "./area-tag");
@@ -1842,7 +1848,7 @@ int lib_interface_isis_create(struct nb_cb_create_args *args)
break;
actual_mtu =
if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu;
- area = isis_area_lookup(area_tag);
+ area = isis_area_lookup(area_tag, ifp->vrf_id);
if (area)
min_mtu = area->lsp_mtu;
else
@@ -1854,14 +1860,15 @@ int lib_interface_isis_create(struct nb_cb_create_args *args)
#endif /* ifndef FABRICD */
if (actual_mtu < min_mtu) {
snprintf(args->errmsg, args->errmsg_len,
- "Interface %s has MTU %" PRIu32
- ", minimum MTU for the area is %" PRIu32 "",
+ "Interface %s has MTU %u, minimum MTU for the area is %u",
ifp->name, actual_mtu, min_mtu);
return NB_ERR_VALIDATION;
}
break;
case NB_EV_APPLY:
- area = isis_area_lookup(area_tag);
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ if (ifp)
+ area = isis_area_lookup(area_tag, ifp->vrf_id);
/* The area should have already be created. We are
* setting the priority of the global isis area creation
* slightly lower, so it should be executed first, but I
@@ -1875,7 +1882,6 @@ int lib_interface_isis_create(struct nb_cb_create_args *args)
abort();
}
- ifp = nb_running_get_entry(args->dnode, NULL, true);
circuit = isis_circuit_create(area, ifp);
assert(circuit
&& (circuit->state == C_STATE_CONF
@@ -1916,6 +1922,7 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args)
struct interface *ifp;
struct vrf *vrf;
const char *area_tag, *ifname, *vrfname;
+ struct isis *isis = NULL;
if (args->event == NB_EV_VALIDATE) {
/* libyang doesn't like relative paths across module boundaries
@@ -1927,8 +1934,14 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args)
vrf = vrf_lookup_by_name(vrfname);
assert(vrf);
ifp = if_lookup_by_name(ifname, vrf->vrf_id);
+
if (!ifp)
return NB_OK;
+
+ isis = isis_lookup_by_vrfid(ifp->vrf_id);
+ if (isis == NULL)
+ return NB_ERR_VALIDATION;
+
circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
area_tag = yang_dnode_get_string(args->dnode, NULL);
if (circuit && circuit->area && circuit->area->area_tag
@@ -1953,6 +1966,7 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args)
struct interface *ifp;
struct vrf *vrf;
const char *ifname, *vrfname;
+ struct isis *isis = NULL;
switch (args->event) {
case NB_EV_VALIDATE:
@@ -1967,6 +1981,11 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args)
ifp = if_lookup_by_name(ifname, vrf->vrf_id);
if (!ifp)
break;
+
+ isis = isis_lookup_by_vrfid(ifp->vrf_id);
+ if (isis == NULL)
+ return NB_ERR_VALIDATION;
+
circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list);
if (circuit && circuit->state == C_STATE_UP
&& circuit->area->is_type != IS_LEVEL_1_AND_2
@@ -2030,26 +2049,53 @@ int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args)
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring
*/
-int lib_interface_isis_bfd_monitoring_modify(struct nb_cb_modify_args *args)
+void lib_interface_isis_bfd_monitoring_apply_finish(
+ struct nb_cb_apply_finish_args *args)
{
struct isis_circuit *circuit;
- bool bfd_monitoring;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
+ bool enabled;
+ const char *profile = NULL;
circuit = nb_running_get_entry(args->dnode, NULL, true);
- bfd_monitoring = yang_dnode_get_bool(args->dnode, NULL);
+ enabled = yang_dnode_get_bool(args->dnode, "./enabled");
- if (bfd_monitoring) {
+ if (yang_dnode_exists(args->dnode, "./profile"))
+ profile = yang_dnode_get_string(args->dnode, "./profile");
+
+ if (enabled) {
isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT,
- true);
+ profile, true);
} else {
isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER);
bfd_info_free(&circuit->bfd_info);
}
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled
+ */
+int lib_interface_isis_bfd_monitoring_enabled_modify(
+ struct nb_cb_modify_args *args)
+{
+ /* Everything done in apply_finish */
+ return NB_OK;
+}
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile
+ */
+int lib_interface_isis_bfd_monitoring_profile_modify(
+ struct nb_cb_modify_args *args)
+{
+ /* Everything done in apply_finish */
+ return NB_OK;
+}
+
+int lib_interface_isis_bfd_monitoring_profile_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* Everything done in apply_finish */
return NB_OK;
}
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index b5377142b6..43b9f6685e 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -74,8 +74,10 @@ static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
fill_fixed_hdr(pdu_type, 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_put(circuit->snd_stream, circuit->area->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 */
@@ -128,6 +130,7 @@ struct iih_info {
static int process_p2p_hello(struct iih_info *iih)
{
struct isis_threeway_adj *tw_adj = iih->tlvs->threeway_adj;
+
if (tw_adj) {
if (tw_adj->state > ISIS_THREEWAY_DOWN) {
if (IS_DEBUG_ADJ_PACKETS) {
@@ -140,8 +143,10 @@ static int process_p2p_hello(struct iih_info *iih)
}
if (tw_adj->neighbor_set
- && (memcmp(tw_adj->neighbor_id, isis->sysid, ISIS_SYS_ID_LEN)
- || tw_adj->neighbor_circuit_id != (uint32_t) iih->circuit->idx)) {
+ && (memcmp(tw_adj->neighbor_id,
+ iih->circuit->area->isis->sysid, ISIS_SYS_ID_LEN)
+ || tw_adj->neighbor_circuit_id
+ != (uint32_t)iih->circuit->idx)) {
if (IS_DEBUG_ADJ_PACKETS) {
zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) which lists IS/Circuit different from us as neighbor.",
@@ -427,8 +432,7 @@ static int process_p2p_hello(struct iih_info *iih)
if (IS_DEBUG_ADJ_PACKETS) {
zlog_debug(
- "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s,"
- " cir id %hhu, length %" PRIu16,
+ "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s, cir id %hhu, length %hu",
iih->circuit->area->area_tag,
iih->circuit->interface->name,
circuit_t2string(iih->circuit->is_type),
@@ -562,7 +566,6 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
? "P2P IIH"
: (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH";
-
stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
pdu_end - pdu_start);
if (IS_DEBUG_ADJ_PACKETS) {
@@ -647,7 +650,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
if (pdu_len_validate(iih.pdu_len, circuit)) {
zlog_warn(
- "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %" PRIu16,
+ "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %hu",
circuit->area->area_tag, pdu_name,
circuit->interface->name, iih.pdu_len);
circuit->rej_adjacencies++;
@@ -725,7 +728,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
goto out;
}
- if (!memcmp(iih.sys_id, isis->sysid, ISIS_SYS_ID_LEN)) {
+ if (!memcmp(iih.sys_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN)) {
zlog_warn(
"ISIS-Adj (%s): Received IIH with own sysid - discard",
circuit->area->area_tag);
@@ -860,16 +863,14 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
#endif /* ifndef FABRICD */
if (pdu_len_validate(hdr.pdu_len, circuit)) {
- zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %" PRIu16,
+ zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu",
circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
hdr.pdu_len);
return ISIS_WARNING;
}
if (IS_DEBUG_UPDATE_PACKETS) {
- zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s, len %" PRIu16 ", on %s",
+ zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s",
circuit->area->area_tag, level,
rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum,
hdr.rem_lifetime, hdr.pdu_len,
@@ -879,7 +880,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
/* lsp is_type check */
if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) {
zlog_debug(
- "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%" PRIx8,
+ "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%x",
circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
hdr.lsp_bits & IS_LEVEL_1_AND_2);
/* continue as per RFC1122 Be liberal in what you accept, and
@@ -891,7 +892,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12,
hdr.pdu_len - 12, hdr.checksum, 12)) {
zlog_debug(
- "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04" PRIx16,
+ "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx",
circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
hdr.checksum);
return ISIS_WARNING;
@@ -900,8 +901,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
/* 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",
+ "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;
@@ -910,8 +910,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
/* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
if (!(circuit->is_type & level)) {
zlog_debug(
- "ISIS-Upd (%s): LSP %s received at level %d over circuit of"
- " type %s",
+ "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;
@@ -986,9 +985,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
if (!isis_adj_lookup_snpa(ssnpa,
circuit->u.bc.adjdb[level - 1])) {
- zlog_debug("(%s): DS ======= LSP %s, seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s on %s",
+ zlog_debug("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
circuit->area->area_tag,
rawlspid_print(hdr.lsp_id), hdr.seqno,
hdr.checksum, hdr.rem_lifetime,
@@ -1029,8 +1026,7 @@ dontcheckadj:
if (lsp && (lsp->hdr.seqno == hdr.seqno)
&& (lsp->hdr.checksum != hdr.checksum)
&& hdr.rem_lifetime) {
- zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08" PRIx32
- " with confused checksum received.",
+ zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
hdr.seqno);
hdr.rem_lifetime = 0;
@@ -1048,7 +1044,8 @@ dontcheckadj:
ack_lsp(&hdr, circuit, level);
goto out; /* FIXME: do we need a purge? */
} else {
- if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) {
+ if (memcmp(hdr.lsp_id, circuit->area->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) {
@@ -1125,9 +1122,7 @@ dontcheckadj:
}
if (IS_DEBUG_UPDATE_PACKETS)
zlog_debug(
- "ISIS-Upd (%s): (1) "
- "re-originating LSP %s new seq "
- "0x%08" PRIx32,
+ "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x",
circuit->area->area_tag,
rawlspid_print(hdr.lsp_id),
lsp->hdr.seqno);
@@ -1144,7 +1139,8 @@ dontcheckadj:
}
/* 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 (memcmp(hdr.lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN)
+ == 0) {
if (!lsp) {
/* 7.3.16.4: initiate a purge */
lsp_purge_non_exist(level, &hdr, circuit->area);
@@ -1169,7 +1165,7 @@ dontcheckadj:
#endif /* ifndef FABRICD */
if (IS_DEBUG_UPDATE_PACKETS) {
zlog_debug(
- "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08" PRIx32,
+ "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x",
circuit->area->area_tag,
rawlspid_print(hdr.lsp_id),
lsp->hdr.seqno);
@@ -1314,8 +1310,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
if (circuit->ext_domain) {
zlog_debug(
- "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
- "skipping: circuit externalDomain = true",
+ "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, skipping: circuit externalDomain = true",
circuit->area->area_tag, level, typechar,
circuit->interface->name);
@@ -1325,8 +1320,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
/* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
if (!(circuit->is_type & level)) {
zlog_debug(
- "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
- "skipping: circuit type %s does not match level %d",
+ "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);
@@ -1338,8 +1332,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
if (!is_csnp && (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",
+ "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);
@@ -1383,6 +1376,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
struct isis_passwd *passwd = (level == IS_LEVEL_1)
? &circuit->area->area_passwd
: &circuit->area->domain_passwd;
+
if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) {
int auth_code = isis_tlvs_auth_is_valid(
tlvs, passwd, circuit->rcv_stream, false);
@@ -1419,8 +1413,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
for (struct isis_lsp_entry *entry = entry_head; entry;
entry = entry->next) {
zlog_debug(
- "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
+ "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus",
circuit->area->area_tag, typechar,
rawlspid_print(entry->id), entry->seqno,
entry->checksum, entry->rem_lifetime);
@@ -1434,7 +1427,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
entry = entry->next) {
struct isis_lsp *lsp =
lsp_search(&circuit->area->lspdb[level - 1], entry->id);
- bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN);
+ bool own_lsp = !memcmp(entry->id, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN);
if (lsp) {
/* 7.3.15.2 b) 1) is this LSP newer */
int cmp = lsp_compare(circuit->area->area_tag, lsp,
@@ -1473,8 +1467,9 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
* are not 0,
* insert it and set SSN on it */
if (entry->rem_lifetime && entry->checksum
- && entry->seqno && memcmp(entry->id, isis->sysid,
- ISIS_SYS_ID_LEN)) {
+ && entry->seqno
+ && memcmp(entry->id, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN)) {
struct isis_lsp *lsp0 = NULL;
if (LSP_FRAGMENT(entry->id)) {
@@ -1613,18 +1608,18 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
if (idrp == ISO9542_ESIS) {
flog_err(EC_LIB_DEVELOPMENT,
- "No support for ES-IS packet IDRP=%" PRIx8, idrp);
+ "No support for ES-IS packet IDRP=%hhx", idrp);
return ISIS_ERROR;
}
if (idrp != ISO10589_ISIS) {
- flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%" PRIx8,
+ flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx",
idrp);
return ISIS_ERROR;
}
if (version1 != 1) {
- zlog_warn("Unsupported ISIS version %" PRIu8, version1);
+ zlog_warn("Unsupported ISIS version %hhu", version1);
#ifndef FABRICD
/* send northbound notification */
isis_notif_version_skew(circuit, version1, raw_pdu);
@@ -1635,8 +1630,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) {
flog_err(
EC_ISIS_PACKET,
- "IDFieldLengthMismatch: ID Length field in a received PDU %" PRIu8
- ", while the parameter for this IS is %u",
+ "IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u",
id_len, ISIS_SYS_ID_LEN);
circuit->id_len_mismatches++;
#ifndef FABRICD
@@ -1648,14 +1642,13 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
uint8_t expected_length;
if (pdu_size(pdu_type, &expected_length)) {
- zlog_warn("Unsupported ISIS PDU %" PRIu8, pdu_type);
+ zlog_warn("Unsupported ISIS PDU %hhu", pdu_type);
return ISIS_WARNING;
}
if (length != expected_length) {
flog_err(EC_ISIS_PACKET,
- "Expected fixed header length = %" PRIu8
- " but got %" PRIu8,
+ "Expected fixed header length = %hhu but got %hhu",
expected_length, length);
return ISIS_ERROR;
}
@@ -1668,7 +1661,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
}
if (version2 != 1) {
- zlog_warn("Unsupported ISIS PDU version %" PRIu8, version2);
+ zlog_warn("Unsupported ISIS PDU version %hhu", version2);
#ifndef FABRICD
/* send northbound notification */
isis_notif_version_skew(circuit, version2, raw_pdu);
@@ -1683,14 +1676,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
}
/* either 3 or 0 */
- if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr field */
+ if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr
+ field */
&& max_area_addrs != 0
- && max_area_addrs != isis->max_area_addrs) {
+ && max_area_addrs != circuit->area->isis->max_area_addrs) {
flog_err(
EC_ISIS_PACKET,
- "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8
- " while the parameter for this IS is %u",
- max_area_addrs, isis->max_area_addrs);
+ "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %hhu while the parameter for this IS is %u",
+ max_area_addrs, circuit->area->isis->max_area_addrs);
circuit->max_area_addr_mismatches++;
#ifndef FABRICD
/* send northbound notification */
@@ -2069,8 +2062,10 @@ int send_csnp(struct isis_circuit *circuit, int level)
fill_fixed_hdr(pdu_type, circuit->snd_stream);
size_t len_pointer = stream_get_endp(circuit->snd_stream);
+
stream_putw(circuit->snd_stream, 0);
- stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
+ stream_put(circuit->snd_stream, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN);
/* with zero circuit id - ref 9.10, 9.11 */
stream_putc(circuit->snd_stream, 0);
@@ -2247,7 +2242,8 @@ static int send_psnp(int level, struct isis_circuit *circuit)
size_t len_pointer = stream_get_endp(circuit->snd_stream);
stream_putw(circuit->snd_stream, 0); /* length is filled in later */
- stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
+ stream_put(circuit->snd_stream, circuit->area->isis->sysid,
+ ISIS_SYS_ID_LEN);
stream_putc(circuit->snd_stream, circuit->idx);
struct isis_passwd *passwd = (level == ISIS_LEVEL1)
@@ -2409,9 +2405,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
flog_err(
EC_ISIS_PACKET,
- "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s on %s. LSP Size is %zu while interface stream size is %zu.",
+ "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.",
circuit->area->area_tag, lsp->level,
rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
lsp->hdr.checksum, lsp->hdr.rem_lifetime,
@@ -2439,9 +2433,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
}
if (IS_DEBUG_UPDATE_PACKETS) {
- zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08" PRIx32
- ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
- "s on %s",
+ zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
circuit->area->area_tag,
(tx_type == TX_LSP_CIRCUIT_SCOPED)
? "Circuit scoped " : "",
diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c
index 82f42a86d9..4a884877f0 100644
--- a/isisd/isis_pfpacket.c
+++ b/isisd/isis_pfpacket.c
@@ -102,8 +102,7 @@ static int isis_multicast_join(int fd, int registerto, int if_num)
}
#ifdef EXTREME_DEBUG
zlog_debug(
- "isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, "
- "address = %02x:%02x:%02x:%02x:%02x:%02x",
+ "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]);
@@ -247,16 +246,13 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa)
|| (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",
+ "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",
+ "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);
}
diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c
index dc23e8ea49..44422ff664 100644
--- a/isisd/isis_redist.c
+++ b/isisd/isis_redist.c
@@ -100,8 +100,7 @@ static void isis_redist_install(struct isis_area *area, int level,
if (!er_table) {
zlog_warn(
- "%s: External reachability table of area %s"
- " is not initialized.",
+ "%s: External reachability table of area %s is not initialized.",
__func__, area->area_tag);
return;
}
@@ -134,8 +133,7 @@ static void isis_redist_uninstall(struct isis_area *area, int level,
if (!er_table) {
zlog_warn(
- "%s: External reachability table of area %s"
- " is not initialized.",
+ "%s: External reachability table of area %s is not initialized.",
__func__, area->area_tag);
return;
}
@@ -220,8 +218,9 @@ static void isis_redist_ensure_default(struct isis *isis, int family)
}
/* Handle notification about route being added */
-void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
- uint8_t distance, uint32_t metric)
+void isis_redist_add(struct isis *isis, int type, struct prefix *p,
+ struct prefix_ipv6 *src_p, uint8_t distance,
+ uint32_t metric)
{
int family = p->family;
struct route_table *ei_table = get_ext_info(isis, family);
@@ -272,7 +271,8 @@ void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
}
}
-void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
+void isis_redist_delete(struct isis *isis, int type, struct prefix *p,
+ struct prefix_ipv6 *src_p)
{
int family = p->family;
struct route_table *ei_table = get_ext_info(isis, family);
@@ -294,8 +294,8 @@ void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
* by "default-information originate always". Areas without the
* "always" setting will ignore routes with origin
* DEFAULT_ROUTE. */
- isis_redist_add(DEFAULT_ROUTE, p, NULL,
- 254, MAX_WIDE_PATH_METRIC);
+ isis_redist_add(isis, DEFAULT_ROUTE, p, NULL, 254,
+ MAX_WIDE_PATH_METRIC);
return;
}
@@ -310,8 +310,7 @@ void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
char buf[BUFSIZ];
prefix2str(p, buf, sizeof(buf));
zlog_warn(
- "%s: Got a delete for %s route %s, but that route"
- " was never added.",
+ "%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);
@@ -605,8 +604,7 @@ DEFUN (no_isis_redistribute,
DEFUN (isis_default_originate,
isis_default_originate_cmd,
- "default-information originate <ipv4|ipv6>"
- " [always] [{metric (0-16777215)|route-map WORD}]",
+ "default-information originate <ipv4|ipv6> [always] [{metric (0-16777215)|route-map WORD}]",
"Control distribution of default information\n"
"Distribute a default route\n"
"Distribute default route for IPv4\n"
diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h
index 9c37c310ea..0d2dc6a803 100644
--- a/isisd/isis_redist.h
+++ b/isisd/isis_redist.h
@@ -40,6 +40,7 @@ struct isis_redist {
struct route_map *map;
};
+struct isis;
struct isis_area;
struct prefix;
struct prefix_ipv6;
@@ -47,9 +48,11 @@ 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, struct prefix_ipv6 *src_p,
- uint8_t distance, uint32_t metric);
-void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p);
+void isis_redist_add(struct isis *isis, int type, struct prefix *p,
+ struct prefix_ipv6 *src_p, uint8_t distance,
+ uint32_t metric);
+void isis_redist_delete(struct isis *isis, int type, struct prefix *p,
+ struct prefix_ipv6 *src_p);
int isis_redist_config_write(struct vty *vty, struct isis_area *area,
int family);
void isis_redist_init(void);
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index fb1aad8c49..ebce86bed9 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -35,6 +35,7 @@
#include "table.h"
#include "spf_backoff.h"
#include "srcdest_table.h"
+#include "vrf.h"
#include "isis_constants.h"
#include "isis_common.h"
@@ -877,8 +878,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
if (lsp == NULL
|| lsp->hdr.rem_lifetime == 0)
zlog_warn(
- "ISIS-Spf: No LSP %s found for IS adjacency "
- "L%d on %s (ID %u)",
+ "ISIS-Spf: No LSP %s found for IS adjacency L%d on %s (ID %u)",
rawlspid_print(lsp_id),
spftree->level,
circuit->interface->name,
@@ -915,8 +915,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
/* if no adj, we are the dis or error */
if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) {
zlog_warn(
- "ISIS-Spf: No adjacency found from root "
- "to L%d DR %s on %s (ID %d)",
+ "ISIS-Spf: No adjacency found from root to L%d DR %s on %s (ID %d)",
spftree->level, rawlspid_print(lsp_id),
circuit->interface->name,
circuit->circuit_id);
@@ -927,8 +926,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree,
lsp_id);
if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
zlog_warn(
- "ISIS-Spf: No lsp (%p) found from root "
- "to L%d DR %s on %s (ID %d)",
+ "ISIS-Spf: No lsp (%p) found from root to L%d DR %s on %s (ID %d)",
(void *)lsp, spftree->level,
rawlspid_print(lsp_id),
circuit->interface->name,
@@ -1022,8 +1020,7 @@ static void add_to_paths(struct isis_spftree *spftree,
spftree->route_table);
else if (IS_DEBUG_SPF_EVENTS)
zlog_debug(
- "ISIS-Spf: no adjacencies do not install route for "
- "%s depth %d dist %d",
+ "ISIS-Spf: no adjacencies do not install route for %s depth %d dist %d",
vid2string(vertex, buff, sizeof(buff)),
vertex->depth, vertex->d_N);
}
@@ -1087,7 +1084,8 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2,
AF_INET, SPFTREE_IPV4, true);
- if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) {
+
+ if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) {
/* If we are running locally, initialize with information from adjacencies */
struct isis_vertex *root = isis_spf_add_root(spftree, sysid);
isis_spf_preload_tent(spftree, sysid, root);
@@ -1103,19 +1101,17 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
}
static int isis_run_spf(struct isis_area *area, int level,
- enum spf_tree_id tree_id,
- uint8_t *sysid, struct timeval *nowtv)
+ enum spf_tree_id tree_id, uint8_t *sysid)
{
int retval = ISIS_OK;
struct isis_vertex *root_vertex;
struct isis_spftree *spftree = area->spftree[tree_id][level - 1];
- struct timeval time_now;
- unsigned long long start_time, end_time;
+ struct timeval time_start;
+ struct timeval time_end;
uint16_t mtid = 0;
/* Get time that can't roll backwards. */
- start_time = nowtv->tv_sec;
- start_time = (start_time * 1000000) + nowtv->tv_usec;
+ monotime(&time_start);
int family = -1;
switch (tree_id) {
@@ -1166,10 +1162,10 @@ static int isis_run_spf(struct isis_area *area, int level,
out:
spftree->runcount++;
spftree->last_run_timestamp = time(NULL);
- spftree->last_run_monotime = monotime(&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;
+ spftree->last_run_monotime = monotime(&time_end);
+ spftree->last_run_duration =
+ ((time_end.tv_sec - time_start.tv_sec) * 1000000)
+ + (time_end.tv_usec - time_start.tv_usec);
return retval;
}
@@ -1215,15 +1211,14 @@ static int isis_run_spf_cb(struct thread *thread)
area->area_tag, level);
if (area->ip_circuits)
- retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid,
- &thread->real);
+ retval = isis_run_spf(area, level, SPFTREE_IPV4,
+ area->isis->sysid);
if (area->ipv6_circuits)
- retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid,
- &thread->real);
- if (area->ipv6_circuits
- && isis_area_ipv6_dstsrc_enabled(area))
- retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid,
- &thread->real);
+ retval = isis_run_spf(area, level, SPFTREE_IPV6,
+ area->isis->sysid);
+ if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area))
+ retval = isis_run_spf(area, level, SPFTREE_DSTSRC,
+ area->isis->sysid);
isis_area_verify_routes(area);
@@ -1262,8 +1257,7 @@ int _isis_spf_schedule(struct isis_area *area, int level,
if (IS_DEBUG_SPF_EVENTS) {
zlog_debug(
- "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago"
- " Caller: %s %s:%d",
+ "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago Caller: %s %s:%d",
area->area_tag, level, diff, func, file, line);
}
@@ -1416,38 +1410,19 @@ static void isis_print_spftree(struct vty *vty, int level,
vty_out(vty, "IS-IS paths to level-%d routers %s\n",
level, tree_id_text);
isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths,
- isis->sysid);
+ area->isis->sysid);
+
vty_out(vty, "\n");
}
-DEFUN (show_isis_topology,
- show_isis_topology_cmd,
- "show " PROTO_NAME " topology"
-#ifndef FABRICD
- " [<level-1|level-2>]"
-#endif
- , SHOW_STR
- PROTO_HELP
- "IS-IS paths to Intermediate Systems\n"
-#ifndef FABRICD
- "Paths to all level-1 routers in the area\n"
- "Paths to all level-2 routers in the domain\n"
-#endif
- )
+static void show_isis_topology_common(struct vty *vty, int levels,
+ struct isis *isis)
{
- int levels;
struct listnode *node;
struct isis_area *area;
- if (argc < 4)
- levels = ISIS_LEVEL1 | ISIS_LEVEL2;
- else if (strmatch(argv[3]->text, "level-1"))
- levels = ISIS_LEVEL1;
- else
- levels = ISIS_LEVEL2;
-
if (!isis->area_list || isis->area_list->count == 0)
- return CMD_SUCCESS;
+ return;
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
vty_out(vty, "Area %s:\n",
@@ -1480,6 +1455,58 @@ DEFUN (show_isis_topology,
vty_out(vty, "\n");
}
+}
+
+DEFUN(show_isis_topology, show_isis_topology_cmd,
+ "show " PROTO_NAME
+ " [vrf <NAME|all>] topology"
+#ifndef FABRICD
+ " [<level-1|level-2>]"
+#endif
+ ,
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "IS-IS paths to Intermediate Systems\n"
+#ifndef FABRICD
+ "Paths to all level-1 routers in the area\n"
+ "Paths to all level-2 routers in the domain\n"
+#endif
+)
+{
+ int levels = ISIS_LEVELS;
+ struct listnode *inode, *nnode;
+ struct isis *isis = NULL;
+ int idx = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ if (argv_find(argv, argc, "topology", &idx)) {
+ if (argc < idx + 2)
+ levels = ISIS_LEVEL1 | ISIS_LEVEL2;
+ else if (strmatch(argv[idx + 1]->arg, "level-1"))
+ levels = ISIS_LEVEL1;
+ else
+ levels = ISIS_LEVEL2;
+ }
+
+ if (!im) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ show_isis_topology_common(vty, levels, isis);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ show_isis_topology_common(vty, levels, isis);
+ }
return CMD_SUCCESS;
}
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
index 59f00a73f5..bdbdc30b4a 100644
--- a/isisd/isis_sr.c
+++ b/isisd/isis_sr.c
@@ -1055,7 +1055,7 @@ static void parse_prefix_sid_subtlvs(struct sr_node *srn,
|| srp->sid.value != psid->value) {
srp->sid = *psid;
srp->state = SRDB_STATE_MODIFIED;
- } else
+ } else if (srp->state == SRDB_STATE_VALIDATED)
srp->state = SRDB_STATE_UNCHANGED;
sr_debug(" |- Found %s Prefix-SID %pFX",
srp->state == SRDB_STATE_MODIFIED
@@ -1223,7 +1223,7 @@ static void process_node_changes(struct isis_area *area, int level,
* If an neighbor router's SRGB was changed or created, then reinstall
* all Prefix-SIDs from all nodes that use this neighbor as nexthop.
*/
- adjacent = isis_adj_exists(area, level, sysid);
+ adjacent = !!isis_adj_find(area, level, sysid);
switch (srn->state) {
case SRDB_STATE_NEW:
case SRDB_STATE_MODIFIED:
@@ -1980,20 +1980,48 @@ static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level)
* Declaration of new show commands.
*/
DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd,
- "show isis segment-routing prefix-sids",
- SHOW_STR PROTO_HELP
+ "show isis [vrf <NAME|all>] segment-routing prefix-sids",
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
"Segment-Routing\n"
"Segment-Routing Prefix-SIDs\n")
{
- struct listnode *node;
+ struct listnode *node, *inode, *nnode;
struct isis_area *area;
-
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- vty_out(vty, "Area %s:\n",
- area->area_tag ? area->area_tag : "null");
-
- for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
- show_prefix_sids(vty, area, level);
+ struct isis *isis = NULL;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
+ area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag
+ : "null");
+ for (int level = ISIS_LEVEL1;
+ level <= ISIS_LEVELS; level++)
+ show_prefix_sids(vty, area,
+ level);
+ }
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node,
+ area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag
+ : "null");
+ for (int level = ISIS_LEVEL1;
+ level <= ISIS_LEVELS; level++)
+ show_prefix_sids(vty, area, level);
+ }
+ }
}
return CMD_SUCCESS;
@@ -2056,15 +2084,19 @@ DEFUN(show_sr_node, show_sr_node_cmd,
"Segment-Routing\n"
"Segment-Routing node\n")
{
- struct listnode *node;
+ struct listnode *node, *inode, *nnode;
struct isis_area *area;
+ struct isis *isis = NULL;
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
- vty_out(vty, "Area %s:\n",
- area->area_tag ? area->area_tag : "null");
+ for (ALL_LIST_ELEMENTS(im->isis, inode, nnode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag : "null");
- for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++)
- show_node(vty, area, level);
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++)
+ show_node(vty, area, level);
+ }
}
return CMD_SUCCESS;
@@ -2103,7 +2135,7 @@ static int sr_start_label_manager(struct thread *start)
int isis_sr_start(struct isis_area *area)
{
struct isis_sr_db *srdb = &area->srdb;
- struct isis_circuit *circuit;
+ struct isis_adjacency *adj;
struct listnode *node;
/* First start Label Manager if not ready */
@@ -2140,34 +2172,11 @@ int isis_sr_start(struct isis_area *area)
area->area_tag);
/* Create Adjacency-SIDs from existing IS-IS Adjacencies. */
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
- struct isis_adjacency *adj;
- struct listnode *anode;
-
- switch (circuit->circ_type) {
- case CIRCUIT_T_BROADCAST:
- for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
- level++) {
- for (ALL_LIST_ELEMENTS_RO(
- circuit->u.bc.adjdb[level - 1],
- anode, adj)) {
- if (adj->ipv4_address_count > 0)
- sr_adj_sid_add(adj, AF_INET);
- if (adj->ipv6_address_count > 0)
- sr_adj_sid_add(adj, AF_INET6);
- }
- }
- break;
- case CIRCUIT_T_P2P:
- adj = circuit->u.p2p.neighbor;
- if (adj && adj->ipv4_address_count > 0)
- sr_adj_sid_add(adj, AF_INET);
- if (adj && adj->ipv6_address_count > 0)
- sr_adj_sid_add(adj, AF_INET6);
- break;
- default:
- break;
- }
+ for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) {
+ if (adj->ipv4_address_count > 0)
+ sr_adj_sid_add(adj, AF_INET);
+ if (adj->ipv6_address_count > 0)
+ sr_adj_sid_add(adj, AF_INET6);
}
area->srdb.enabled = true;
diff --git a/isisd/isis_te.c b/isisd/isis_te.c
index 133707d61d..016f811a72 100644
--- a/isisd/isis_te.c
+++ b/isisd/isis_te.c
@@ -302,34 +302,68 @@ int isis_mpls_te_update(struct interface *ifp)
/* Followings are vty command functions */
#ifndef FABRICD
-DEFUN (show_isis_mpls_te_router,
- show_isis_mpls_te_router_cmd,
- "show " PROTO_NAME " mpls-te router",
- SHOW_STR
- PROTO_HELP
- MPLS_TE_STR
- "Router information\n")
+DEFUN(show_isis_mpls_te_router,
+ show_isis_mpls_te_router_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] mpls-te router",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR "All VRFs\n"
+ MPLS_TE_STR "Router information\n")
{
- struct listnode *anode;
+ struct listnode *anode, *nnode, *inode;
struct isis_area *area;
+ struct isis *isis = NULL;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
- if (!isis) {
+ if (!im) {
vty_out(vty, "IS-IS Routing Process not enabled\n");
return CMD_SUCCESS;
}
-
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
-
- if (!IS_MPLS_TE(area->mta))
- continue;
-
- vty_out(vty, "Area %s:\n", area->area_tag);
- if (ntohs(area->mta->router_id.s_addr) != 0)
- vty_out(vty, " MPLS-TE Router-Address: %s\n",
- inet_ntoa(area->mta->router_id));
- else
- vty_out(vty, " N/A\n");
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list,
+ anode, area)) {
+ if (!IS_MPLS_TE(area->mta))
+ continue;
+
+ vty_out(vty, "Area %s:\n",
+ area->area_tag);
+ if (ntohs(area->mta->router_id.s_addr)
+ != 0)
+ vty_out(vty,
+ " MPLS-TE Router-Address: %s\n",
+ inet_ntoa(
+ area->mta
+ ->router_id));
+ else
+ vty_out(vty, " N/A\n");
+ }
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
+ area)) {
+
+ if (!IS_MPLS_TE(area->mta))
+ continue;
+
+ vty_out(vty, "Area %s:\n", area->area_tag);
+ if (ntohs(area->mta->router_id.s_addr) != 0)
+ vty_out(vty,
+ " MPLS-TE Router-Address: %s\n",
+ inet_ntoa(
+ area->mta->router_id));
+ else
+ vty_out(vty, " N/A\n");
+ }
+ }
}
return CMD_SUCCESS;
@@ -351,12 +385,12 @@ static void show_ext_sub(struct vty *vty, char *name,
sbuf_reset(&buf);
if (IS_SUBTLV(ext, EXT_ADM_GRP))
- sbuf_push(&buf, 4, "Administrative Group: 0x%" PRIx32 "\n",
+ sbuf_push(&buf, 4, "Administrative Group: 0x%x\n",
ext->adm_group);
if (IS_SUBTLV(ext, EXT_LLRI)) {
- sbuf_push(&buf, 4, "Link Local ID: %" PRIu32 "\n",
+ sbuf_push(&buf, 4, "Link Local ID: %u\n",
ext->local_llri);
- sbuf_push(&buf, 4, "Link Remote ID: %" PRIu32 "\n",
+ sbuf_push(&buf, 4, "Link Remote ID: %u\n",
ext->remote_llri);
}
if (IS_SUBTLV(ext, EXT_LOCAL_ADDR))
@@ -394,7 +428,7 @@ static void show_ext_sub(struct vty *vty, char *name,
ext->te_metric);
if (IS_SUBTLV(ext, EXT_RMT_AS))
sbuf_push(&buf, 4,
- "Inter-AS TE Remote AS number: %" PRIu32 "\n",
+ "Inter-AS TE Remote AS number: %u\n",
ext->remote_as);
if (IS_SUBTLV(ext, EXT_RMT_IP))
sbuf_push(&buf, 4,
@@ -402,19 +436,18 @@ static void show_ext_sub(struct vty *vty, char *name,
inet_ntoa(ext->remote_ip));
if (IS_SUBTLV(ext, EXT_DELAY))
sbuf_push(&buf, 4,
- "%s Average Link Delay: %" PRIu32 " (micro-sec)\n",
+ "%s Average Link Delay: %u (micro-sec)\n",
IS_ANORMAL(ext->delay) ? "Anomalous" : "Normal",
ext->delay);
if (IS_SUBTLV(ext, EXT_MM_DELAY)) {
- sbuf_push(&buf, 4, "%s Min/Max Link Delay: %" PRIu32 " / %"
- PRIu32 " (micro-sec)\n",
+ sbuf_push(&buf, 4, "%s Min/Max Link Delay: %u / %u (micro-sec)\n",
IS_ANORMAL(ext->min_delay) ? "Anomalous" : "Normal",
ext->min_delay & TE_EXT_MASK,
ext->max_delay & TE_EXT_MASK);
}
if (IS_SUBTLV(ext, EXT_DELAY_VAR))
sbuf_push(&buf, 4,
- "Delay Variation: %" PRIu32 " (micro-sec)\n",
+ "Delay Variation: %u (micro-sec)\n",
ext->delay_var & TE_EXT_MASK);
if (IS_SUBTLV(ext, EXT_PKT_LOSS))
sbuf_push(&buf, 4, "%s Link Packet Loss: %g (%%)\n",
@@ -450,30 +483,35 @@ DEFUN (show_isis_mpls_te_interface,
"Interface information\n"
"Interface name\n")
{
- struct listnode *anode, *cnode;
+ struct listnode *anode, *cnode, *nnode, *inode;
struct isis_area *area;
struct isis_circuit *circuit;
struct interface *ifp;
int idx_interface = 4;
+ struct isis *isis = NULL;
- if (!isis) {
+ if (!im) {
vty_out(vty, "IS-IS Routing Process not enabled\n");
return CMD_SUCCESS;
}
if (argc == idx_interface) {
/* Show All Interfaces. */
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
+ area)) {
- if (!IS_MPLS_TE(area->mta))
- continue;
+ if (!IS_MPLS_TE(area->mta))
+ continue;
- vty_out(vty, "Area %s:\n", area->area_tag);
+ vty_out(vty, "Area %s:\n", area->area_tag);
- for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode,
- circuit))
- show_ext_sub(vty, circuit->interface->name,
- circuit->ext);
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list,
+ cnode, circuit))
+ show_ext_sub(vty,
+ circuit->interface->name,
+ circuit->ext);
+ }
}
} else {
/* Interface name is specified. */
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index f3c9c47691..a1f9cc236f 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -200,12 +200,12 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
/* Standard metrics */
if (IS_SUBTLV(exts, EXT_ADM_GRP))
- sbuf_push(buf, indent, "Administrative Group: 0x%" PRIx32 "\n",
+ sbuf_push(buf, indent, "Administrative Group: 0x%x\n",
exts->adm_group);
if (IS_SUBTLV(exts, EXT_LLRI)) {
- sbuf_push(buf, indent, "Link Local ID: %" PRIu32 "\n",
+ sbuf_push(buf, indent, "Link Local ID: %u\n",
exts->local_llri);
- sbuf_push(buf, indent, "Link Remote ID: %" PRIu32 "\n",
+ sbuf_push(buf, indent, "Link Remote ID: %u\n",
exts->remote_llri);
}
if (IS_SUBTLV(exts, EXT_LOCAL_ADDR))
@@ -243,7 +243,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
exts->te_metric);
if (IS_SUBTLV(exts, EXT_RMT_AS))
sbuf_push(buf, indent,
- "Inter-AS TE Remote AS number: %" PRIu32 "\n",
+ "Inter-AS TE Remote AS number: %u\n",
exts->remote_as);
if (IS_SUBTLV(exts, EXT_RMT_IP))
sbuf_push(buf, indent,
@@ -252,19 +252,18 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
/* Extended metrics */
if (IS_SUBTLV(exts, EXT_DELAY))
sbuf_push(buf, indent,
- "%s Average Link Delay: %" PRIu32 " (micro-sec)\n",
+ "%s Average Link Delay: %u (micro-sec)\n",
IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal",
exts->delay);
if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
- sbuf_push(buf, indent, "%s Min/Max Link Delay: %" PRIu32 " / %"
- PRIu32 " (micro-sec)\n",
+ sbuf_push(buf, indent, "%s Min/Max Link Delay: %u / %u (micro-sec)\n",
IS_ANORMAL(exts->min_delay) ? "Anomalous" : "Normal",
exts->min_delay & TE_EXT_MASK,
exts->max_delay & TE_EXT_MASK);
}
if (IS_SUBTLV(exts, EXT_DELAY_VAR)) {
sbuf_push(buf, indent,
- "Delay Variation: %" PRIu32 " (micro-sec)\n",
+ "Delay Variation: %u (micro-sec)\n",
exts->delay_var & TE_EXT_MASK);
}
if (IS_SUBTLV(exts, EXT_PKT_LOSS))
@@ -297,8 +296,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
continue;
sbuf_push(
buf, indent,
- "Adjacency-SID: %" PRIu32 ", Weight: %" PRIu8
- ", Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n",
+ "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n",
adj->sid, adj->weight,
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1'
: '0',
@@ -327,9 +325,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
&& (lan->family != AF_INET6)))
continue;
sbuf_push(buf, indent,
- "Lan-Adjacency-SID: %" PRIu32
- ", Weight: %" PRIu8
- ", Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
+ "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
" Neighbor-ID: %s\n",
lan->sid, lan->weight,
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
@@ -542,7 +538,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
subtlv_type = stream_getc(s);
subtlv_len = stream_getc(s);
if (subtlv_len > len - sum) {
- sbuf_push(log, indent, "TLV %" PRIu8 ": Available data %" PRIu8 " is less than TLV size %u !\n",
+ sbuf_push(log, indent, "TLV %hhu: Available data %u is less than TLV size %u !\n",
subtlv_type, len - sum, subtlv_len);
return 1;
}
@@ -812,11 +808,11 @@ static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i,
sbuf_push(buf, indent, "SR Prefix-SID ");
if (sid->flags & ISIS_PREFIX_SID_VALUE) {
- sbuf_push(buf, 0, "Label: %" PRIu32 ", ", sid->value);
+ sbuf_push(buf, 0, "Label: %u, ", sid->value);
} else {
- sbuf_push(buf, 0, "Index: %" PRIu32 ", ", sid->value);
+ sbuf_push(buf, 0, "Index: %u, ", sid->value);
}
- sbuf_push(buf, 0, "Algorithm: %" PRIu8 ", ", sid->algorithm);
+ sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm);
sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n",
sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED"
: "",
@@ -865,7 +861,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
if (len < 5) {
sbuf_push(log, indent,
- "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
+ "Not enough data left. (expected 5 or more bytes, got %hhu)\n",
len);
return 1;
}
@@ -884,8 +880,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
: ISIS_SUBTLV_PREFIX_SID_SIZE + 1;
if (len != expected_size) {
sbuf_push(log, indent,
- "TLV size differs from expected size. "
- "(expected %u but got %" PRIu8 ")\n",
+ "TLV size differs from expected size. (expected %u but got %hhu)\n",
expected_size, len);
return 1;
}
@@ -961,7 +956,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
if (tlv_len < 1) {
sbuf_push(log, indent,
- "Not enough data left. (expected 1 or more bytes, got %" PRIu8 ")\n",
+ "Not enough data left. (expected 1 or more bytes, got %hhu)\n",
tlv_len);
return 1;
}
@@ -976,8 +971,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
if (tlv_len != 1 + PSIZE(p.prefixlen)) {
sbuf_push(
log, indent,
- "TLV size differs from expected size for the prefixlen. "
- "(expected %u but got %" PRIu8 ")\n",
+ "TLV size differs from expected size for the prefixlen. (expected %u but got %hhu)\n",
1 + PSIZE(p.prefixlen), tlv_len);
return 1;
}
@@ -1149,8 +1143,7 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len,
if (len < 1) {
sbuf_push(
log, indent,
- "Not enough data left. (Expected 1 byte of address length, got %" PRIu8
- ")\n",
+ "Not enough data left. (Expected 1 byte of address length, got %hhu)\n",
len);
goto out;
}
@@ -1159,15 +1152,14 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len,
rv->len = stream_getc(s);
if (len < 1 + rv->len) {
- sbuf_push(log, indent, "Not enough data left. (Expected %" PRIu8
- " bytes of address, got %" PRIu8 ")\n",
+ sbuf_push(log, indent, "Not enough data left. (Expected %hhu bytes of address, got %u)\n",
rv->len, len - 1);
goto out;
}
if (rv->len < 1 || rv->len > 20) {
sbuf_push(log, indent,
- "Implausible area address length %" PRIu8 "\n",
+ "Implausible area address length %hhu\n",
rv->len);
goto out;
}
@@ -1199,7 +1191,7 @@ static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
{
struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
- sbuf_push(buf, indent, "IS Reachability: %s (Metric: %" PRIu8 ")\n",
+ sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n",
isis_format_id(r->id, 7), r->metric);
}
@@ -1234,8 +1226,7 @@ static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
if (len < 11) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 11 bytes of reach information, got %hhu)\n",
len);
return 1;
}
@@ -1300,8 +1291,7 @@ static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
if (len < 6) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 6 bytes of mac, got %hhu)\n",
len);
return 1;
}
@@ -1334,8 +1324,7 @@ static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
sbuf_push(buf, indent,
- "LSP Entry: %s, seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
- ", lifetime %" PRIu16 "s\n",
+ "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n",
isis_format_id(e->id, 8), e->seqno, e->checksum,
e->rem_lifetime);
}
@@ -1369,7 +1358,7 @@ static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s,
if (len < 16) {
sbuf_push(
log, indent,
- "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8,
+ "Not enough data left. (Expected 16 bytes of LSP info, got %hhu",
len);
return 1;
}
@@ -1467,8 +1456,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
if (len < 11) {
sbuf_push(log, indent,
- "Not enough data left. (expected 11 or more bytes, got %"
- PRIu8 ")\n",
+ "Not enough data left. (expected 11 or more bytes, got %hhu)\n",
len);
goto out;
}
@@ -1480,13 +1468,12 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
if ((size_t)len < ((size_t)11) + subtlv_len) {
sbuf_push(log, indent,
- "Not enough data left for subtlv size %" PRIu8
- ", there are only %" PRIu8 " bytes left.\n",
+ "Not enough data left for subtlv size %hhu, there are only %u bytes left.\n",
subtlv_len, len - 11);
goto out;
}
- sbuf_push(log, indent, "Storing %" PRIu8 " bytes of subtlvs\n",
+ sbuf_push(log, indent, "Storing %hhu bytes of subtlvs\n",
subtlv_len);
if (subtlv_len) {
@@ -1525,7 +1512,7 @@ static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i,
struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
char prefixbuf[PREFIX2STR_BUFFER];
- sbuf_push(buf, indent, "IP Reachability: %s (Metric: %" PRIu8 ")\n",
+ sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
r->metric);
}
@@ -1563,8 +1550,7 @@ static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len,
if (len < 12) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 12 bytes of reach information, got %hhu)\n",
len);
return 1;
}
@@ -1713,8 +1699,7 @@ static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len,
if (len < 4) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 4 bytes of IPv4 address, got %hhu)\n",
len);
return 1;
}
@@ -1775,8 +1760,7 @@ static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len,
if (len < 16) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 16 bytes of IPv6 address, got %hhu)\n",
len);
return 1;
}
@@ -1847,8 +1831,7 @@ static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len,
if (len < 2) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 2 bytes of MT info, got %hhu)\n",
len);
return 1;
}
@@ -2025,7 +2008,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
consume = 5;
if (len < consume) {
sbuf_push(log, indent,
- "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
+ "Not enough data left. (expected 5 or more bytes, got %hhu)\n",
len);
goto out;
}
@@ -2075,8 +2058,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
consume += subtlv_len;
if (len < consume) {
sbuf_push(log, indent,
- "Expected %" PRIu8
- " bytes of subtlvs, but only %u bytes available.\n",
+ "Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
subtlv_len,
len - 6 - PSIZE(rv->prefix.prefixlen));
goto out;
@@ -2209,7 +2191,7 @@ static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
if (spine_leaf->tier == ISIS_TIER_UNDEFINED) {
sbuf_push(buf, indent, " Tier: undefined\n");
} else {
- sbuf_push(buf, indent, " Tier: %" PRIu8 "\n",
+ sbuf_push(buf, indent, " Tier: %hhu\n",
spine_leaf->tier);
}
}
@@ -2342,14 +2324,14 @@ static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj
sbuf_push(buf, indent, " State: %s (%d)\n",
isis_threeway_state_name(threeway_adj->state),
threeway_adj->state);
- sbuf_push(buf, indent, " Extended Local Circuit ID: %" PRIu32 "\n",
+ sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n",
threeway_adj->local_circuit_id);
if (!threeway_adj->neighbor_set)
return;
sbuf_push(buf, indent, " Neighbor System ID: %s\n",
isis_format_id(threeway_adj->neighbor_id, 6));
- sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %" PRIu32 "\n",
+ sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n",
threeway_adj->neighbor_circuit_id);
}
@@ -2508,8 +2490,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
consume = 6;
if (len < consume) {
sbuf_push(log, indent,
- "Not enough data left. (expected 6 or more bytes, got %"
- PRIu8 ")\n",
+ "Not enough data left. (expected 6 or more bytes, got %hhu)\n",
len);
goto out;
}
@@ -2561,8 +2542,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
consume += subtlv_len;
if (len < consume) {
sbuf_push(log, indent,
- "Expected %" PRIu8
- " bytes of subtlvs, but only %u bytes available.\n",
+ "Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
subtlv_len,
len - 6 - PSIZE(rv->prefix.prefixlen));
goto out;
@@ -2912,12 +2892,12 @@ static void format_item_auth(uint16_t mtid, struct isis_item *i,
case ISIS_PASSWD_TYPE_HMAC_MD5:
for (unsigned int j = 0; j < 16; j++) {
snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j,
- "%02" PRIx8, auth->value[j]);
+ "%02hhx", auth->value[j]);
}
sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
break;
default:
- sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type);
+ sbuf_push(buf, indent, " Unknown (%hhu)\n", auth->type);
break;
}
}
@@ -2963,8 +2943,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
if (len < 1) {
sbuf_push(
log, indent,
- "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8
- ")\n",
+ "Not enough data left.(Expected 1 bytes of auth type, got %hhu)\n",
len);
return 1;
}
@@ -2977,8 +2956,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
sbuf_push(
log, indent,
- "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8
- ")\n",
+ "Unexpected auth length for HMAC-MD5 (expected 16, got %hhu)\n",
rv->length);
XFREE(MTYPE_ISIS_TLV, rv);
return 1;
@@ -3062,8 +3040,7 @@ static int unpack_tlv_purge_originator(enum isis_tlv_context context,
sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n");
if (tlv_len < 7) {
- sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %"
- PRIu8 ")\n", tlv_len);
+ sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %hhu)\n", tlv_len);
return 1;
}
@@ -3074,8 +3051,7 @@ static int unpack_tlv_purge_originator(enum isis_tlv_context context,
} else if (number_of_ids == 2) {
poi.sender_set = true;
} else {
- sbuf_push(log, indent, "Got invalid value for number of system IDs: %"
- PRIu8 ")\n", number_of_ids);
+ sbuf_push(log, indent, "Got invalid value for number of system IDs: %hhu)\n", number_of_ids);
return 1;
}
@@ -4019,7 +3995,7 @@ static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
{
stream_forward_getp(s, tlv_len);
sbuf_push(log, indent,
- "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n",
+ "Skipping unknown TLV %hhu (%hhu bytes)\n",
tlv_type, tlv_len);
return 0;
}
@@ -4045,12 +4021,12 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
tlv_len = stream_getc(stream);
sbuf_push(log, indent + 2,
- "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n",
+ "Found TLV of type %hhu and len %hhu.\n",
tlv_type, tlv_len);
if (avail_len < ((size_t)tlv_len) + 2) {
sbuf_push(log, indent + 2,
- "Available data %zu too short for claimed TLV len %" PRIu8 ".\n",
+ "Available data %zu too short for claimed TLV len %hhu.\n",
avail_len - 2, tlv_len);
return 1;
}
@@ -4106,8 +4082,7 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
sbuf_reset(&logbuf);
if (avail_len > STREAM_READABLE(stream)) {
sbuf_push(&logbuf, indent,
- "Stream doesn't contain sufficient data. "
- "Claimed %zu, available %zu\n",
+ "Stream doesn't contain sufficient data. Claimed %zu, available %zu\n",
avail_len, STREAM_READABLE(stream));
return 1;
}
diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c
index 09b8d28258..d0a411a8db 100644
--- a/isisd/isis_vty_fabricd.c
+++ b/isisd/isis_vty_fabricd.c
@@ -112,12 +112,13 @@ DEFUN (no_triggered_csnp,
return CMD_SUCCESS;
}
-static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp)
+static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp,
+ struct isis *isis)
{
char lspid[255];
char buf[MONOTIME_STRLEN];
- lspid_print(lsp->hdr.lsp_id, lspid, true, true);
+ lspid_print(lsp->hdr.lsp_id, lspid, true, true, isis);
vty_out(vty, "Flooding information for %s\n", lspid);
if (!lsp->flooding_neighbors[TX_LSP_NORMAL]) {
@@ -136,8 +137,7 @@ static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp)
vty_out(vty, "%s ago)\n", buf);
if (lsp->flooding_circuit_scoped) {
- vty_out(vty, " Received as circuit-scoped LSP, so not "
- "flooded.\n");
+ vty_out(vty, " Received as circuit-scoped LSP, so not flooded.\n");
return;
}
@@ -171,25 +171,29 @@ DEFUN (show_lsp_flooding,
struct listnode *node;
struct isis_area *area;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1];
struct isis_lsp *lsp;
- vty_out(vty, "Area %s:\n", area->area_tag ?
- area->area_tag : "null");
-
+ vty_out(vty, "Area %s:\n",
+ area->area_tag ? area->area_tag : "null");
if (lspid) {
- lsp = lsp_for_arg(head, lspid);
-
+ lsp = lsp_for_arg(head, lspid, isis);
if (lsp)
- lsp_print_flooding(vty, lsp);
-
+ lsp_print_flooding(vty, lsp, isis);
continue;
}
-
frr_each (lspdb, head, lsp) {
- lsp_print_flooding(vty, lsp);
+ lsp_print_flooding(vty, lsp, isis);
vty_out(vty, "\n");
}
}
@@ -223,9 +227,9 @@ DEFUN (ip_router_isis,
}
}
- area = isis_area_lookup(area_tag);
+ area = isis_area_lookup(area_tag, VRF_DEFAULT);
if (!area)
- area = isis_area_create(area_tag);
+ area = isis_area_create(area_tag, VRF_DEFAULT_NAME);
if (!circuit || !circuit->area) {
circuit = isis_circuit_create(area, ifp);
@@ -277,7 +281,7 @@ DEFUN (no_ip_router_isis,
const char *af = argv[idx_afi]->arg;
const char *area_tag = argv[idx_word]->arg;
- area = isis_area_lookup(area_tag);
+ area = isis_area_lookup(area_tag, VRF_DEFAULT);
if (!area) {
vty_out(vty, "Can't find ISIS instance %s\n",
area_tag);
@@ -316,8 +320,8 @@ DEFUN (isis_bfd,
return CMD_SUCCESS;
}
- isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
- BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, true);
+ isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
+ BFD_DEF_DETECT_MULT, NULL, true);
return CMD_SUCCESS;
}
@@ -437,8 +441,7 @@ isis_vty_lsp_gen_interval_set(struct vty *vty, int level, uint16_t interval)
if (interval >= area->lsp_refresh[lvl - 1]) {
vty_out(vty,
- "LSP gen interval %us must be less than "
- "the LSP refresh interval %us\n",
+ "LSP gen interval %us must be less than the LSP refresh interval %us\n",
interval, area->lsp_refresh[lvl - 1]);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -488,15 +491,13 @@ isis_vty_lsp_refresh_set(struct vty *vty, int level, uint16_t interval)
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\n",
+ "LSP refresh interval %us must be greater than the configured LSP gen interval %us\n",
interval, area->lsp_gen_interval[lvl - 1]);
return CMD_WARNING_CONFIG_FAILED;
}
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\n",
+ "LSP refresh interval %us must be less than the configured LSP lifetime %us less 300\n",
interval, area->max_lsp_lifetime[lvl - 1]);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -546,20 +547,17 @@ isis_vty_max_lsp_lifetime_set(struct vty *vty, int level, uint16_t interval)
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\n",
+ "Level %d Max LSP lifetime %us must be 300s greater than the configured LSP refresh interval %us\n",
lvl, interval, area->lsp_refresh[lvl - 1]);
vty_out(vty,
- "Automatically reducing level %d LSP refresh interval "
- "to %us\n",
+ "Automatically reducing level %d LSP refresh interval to %us\n",
lvl, refresh_interval);
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\n",
+ "LSP refresh interval %us must be greater than the configured LSP gen interval %us\n",
refresh_interval,
area->lsp_gen_interval[lvl - 1]);
return CMD_WARNING_CONFIG_FAILED;
@@ -845,8 +843,7 @@ DEFUN (isis_metric,
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\n",
+ "Invalid metric %d - should be <0-63> when narrow metric type enabled\n",
met);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -855,8 +852,7 @@ DEFUN (isis_metric,
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\n",
+ "Invalid metric %d - should be <0-16777215> when wide metric type enabled\n",
met);
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index a80a18d887..3aa21a9aed 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -62,6 +62,13 @@ static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
struct isis_area *area;
struct listnode *node;
struct prefix router_id;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(vrf_id);
+
+ if (isis == NULL) {
+ return -1;
+ }
zebra_router_id_update_read(zclient->ibuf, &router_id);
if (isis->router_id == router_id.u.prefix4.s_addr)
@@ -407,6 +414,12 @@ void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra)
static int isis_zebra_read(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(vrf_id);
+
+ if (isis == NULL)
+ return -1;
if (zapi_route_decode(zclient->ibuf, &api) < 0)
return -1;
@@ -428,10 +441,11 @@ static int isis_zebra_read(ZAPI_CALLBACK_ARGS)
}
if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
- isis_redist_add(api.type, &api.prefix, &api.src_prefix,
+ isis_redist_add(isis, api.type, &api.prefix, &api.src_prefix,
api.distance, api.metric);
else
- isis_redist_delete(api.type, &api.prefix, &api.src_prefix);
+ isis_redist_delete(isis, api.type, &api.prefix,
+ &api.src_prefix);
return 0;
}
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 53e48bd1cf..0d39aba20b 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -35,6 +35,7 @@
#include "prefix.h"
#include "table.h"
#include "qobj.h"
+#include "vrf.h"
#include "spf_backoff.h"
#include "lib/northbound_cli.h"
@@ -75,44 +76,142 @@ unsigned long debug_bfd;
unsigned long debug_tx_queue;
unsigned long debug_sr;
-struct isis *isis = NULL;
-
-DEFINE_QOBJ_TYPE(isis)
DEFINE_QOBJ_TYPE(isis_area)
+/* ISIS process wide configuration. */
+static struct isis_master isis_master;
+
+/* ISIS process wide configuration pointer to export. */
+struct isis_master *im;
+
/*
* Prototypes.
*/
int isis_area_get(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 show_isis_interface_common(struct vty *, const char *ifname, char,
+ const char *vrf_name, bool all_vrf);
+int show_isis_neighbor_common(struct vty *, const char *id, char,
+ const char *vrf_name, bool all_vrf);
+int clear_isis_neighbor_common(struct vty *, const char *id, const char *vrf_name,
+ bool all_vrf);
+
+static void isis_add(struct isis *isis)
+{
+ listnode_add(im->isis, isis);
+}
+
+static void isis_delete(struct isis *isis)
+{
+ listnode_delete(im->isis, isis);
+}
+
+/* Link ISIS instance to VRF. */
+void isis_vrf_link(struct isis *isis, struct vrf *vrf)
+{
+ isis->vrf_id = vrf->vrf_id;
+ if (vrf->info != (void *)isis)
+ vrf->info = (void *)isis;
+}
+
+/* Unlink ISIS instance to VRF. */
+void isis_vrf_unlink(struct isis *isis, struct vrf *vrf)
+{
+ if (vrf->info == (void *)isis)
+ vrf->info = NULL;
+ isis->vrf_id = VRF_UNKNOWN;
+}
+
+struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id)
+{
+ struct isis *isis = NULL;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis))
+ if (isis->vrf_id == vrf_id)
+ return isis;
+ return NULL;
+}
+
+struct isis *isis_lookup_by_vrfname(const char *vrfname)
+{
+ struct isis *isis = NULL;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis))
+ if (isis->name && vrfname && strcmp(isis->name, vrfname) == 0)
+ return isis;
+ return NULL;
+}
+
+struct isis *isis_lookup_by_sysid(uint8_t *sysid)
+{
+ struct isis *isis = NULL;
+ struct listnode *node, *nnode;
+ for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis))
+ if (!memcmp(isis->sysid, sysid, ISIS_SYS_ID_LEN))
+ return isis;
+ return NULL;
+}
+
+void isis_master_init(struct thread_master *master)
+{
+ memset(&isis_master, 0, sizeof(struct isis_master));
+ im = &isis_master;
+ im->isis = list_new();
+ im->master = master;
+}
+
+void isis_global_instance_create()
+{
+ struct isis *isis = NULL;
+ isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+ if (isis == NULL) {
+ isis = isis_new(VRF_DEFAULT);
+ isis_add(isis);
+ }
+}
-void isis_new(unsigned long process_id, vrf_id_t vrf_id)
+struct isis *isis_new(vrf_id_t vrf_id)
{
+ struct vrf *vrf = NULL;
+ struct isis *isis = NULL;
+
isis = XCALLOC(MTYPE_ISIS, sizeof(struct isis));
+ isis->vrf_id = vrf_id;
+ vrf = vrf_lookup_by_id(vrf_id);
+
+ if (vrf) {
+ isis_vrf_link(isis, vrf);
+ isis->name = XSTRDUP(MTYPE_ISIS, vrf->name);
+ }
+
+ if (IS_DEBUG_EVENTS)
+ zlog_debug(
+ "%s: Create new isis instance with vrf_name %s vrf_id %u",
+ __func__, isis->name, isis->vrf_id);
+
/*
* Default values
*/
- isis->vrf_id = vrf_id;
isis->max_area_addrs = 3;
- isis->process_id = process_id;
+ isis->process_id = getpid();
isis->router_id = 0;
isis->area_list = list_new();
isis->init_circ_list = list_new();
isis->uptime = time(NULL);
- dyn_cache_init();
+ dyn_cache_init(isis);
- QOBJ_REG(isis, isis);
+ return isis;
}
-struct isis_area *isis_area_create(const char *area_tag)
+struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
{
struct isis_area *area;
-
+ struct isis *isis = NULL;
+ struct vrf *vrf = NULL;
area = XCALLOC(MTYPE_ISIS_AREA, sizeof(struct isis_area));
/*
@@ -136,6 +235,7 @@ struct isis_area *isis_area_create(const char *area_tag)
spftree_area_init(area);
area->circuit_list = list_new();
+ area->adjacency_list = list_new();
area->area_addrs = list_new();
thread_add_timer(master, lsp_tick, area, 1, &area->t_tick);
flags_initialize(&area->flags);
@@ -192,6 +292,25 @@ struct isis_area *isis_area_create(const char *area_tag)
area_mt_init(area);
area->area_tag = strdup(area_tag);
+
+ if (vrf_name) {
+ vrf = vrf_lookup_by_name(vrf_name);
+ if (vrf) {
+ isis = isis_lookup_by_vrfid(vrf->vrf_id);
+ if (isis == NULL) {
+ isis = isis_new(vrf->vrf_id);
+ isis_add(isis);
+ }
+ } else
+ return NULL;
+ } else {
+ isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+ if (isis == NULL) {
+ isis = isis_new(VRF_DEFAULT);
+ isis_add(isis);
+ }
+ }
+
listnode_add(isis->area_list, area);
area->isis = isis;
@@ -212,10 +331,15 @@ struct isis_area *isis_area_create(const char *area_tag)
return area;
}
-struct isis_area *isis_area_lookup(const char *area_tag)
+struct isis_area *isis_area_lookup(const char *area_tag, vrf_id_t vrf_id)
{
struct isis_area *area;
struct listnode *node;
+ struct isis *isis = NULL;
+
+ isis = isis_lookup_by_vrfid(vrf_id);
+ if (isis == NULL)
+ return NULL;
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
if ((area->area_tag == NULL && area_tag == NULL)
@@ -230,14 +354,14 @@ int isis_area_get(struct vty *vty, const char *area_tag)
{
struct isis_area *area;
- area = isis_area_lookup(area_tag);
+ area = isis_area_lookup(area_tag, VRF_DEFAULT);
if (area) {
VTY_PUSH_CONTEXT(ROUTER_NODE, area);
return CMD_SUCCESS;
}
- area = isis_area_create(area_tag);
+ area = isis_area_create(area_tag, VRF_DEFAULT_NAME);
if (IS_DEBUG_EVENTS)
zlog_debug("New IS-IS area instance %s", area->area_tag);
@@ -247,21 +371,12 @@ int isis_area_get(struct vty *vty, const char *area_tag)
return CMD_SUCCESS;
}
-int isis_area_destroy(const char *area_tag)
+void isis_area_destroy(struct isis_area *area)
{
- 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) {
- zlog_warn("%s: could not find area with area-tag %s",
- __func__, area_tag);
- return CMD_ERR_NO_MATCH;
- }
-
QOBJ_UNREG(area);
if (fabricd)
@@ -280,6 +395,7 @@ int isis_area_destroy(const char *area_tag)
}
list_delete(&area->circuit_list);
}
+ list_delete(&area->adjacency_list);
lsp_db_fini(&area->lspdb[0]);
lsp_db_fini(&area->lspdb[1]);
@@ -312,20 +428,50 @@ int isis_area_destroy(const char *area_tag)
thread_cancel_event(master, area);
- listnode_delete(isis->area_list, area);
+ listnode_delete(area->isis->area_list, area);
free(area->area_tag);
area_mt_finish(area);
+ if (listcount(area->isis->area_list) == 0) {
+ memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN);
+ area->isis->sysid_set = 0;
+ }
+
XFREE(MTYPE_ISIS_AREA, area);
- if (listcount(isis->area_list) == 0) {
- memset(isis->sysid, 0, ISIS_SYS_ID_LEN);
- isis->sysid_set = 0;
+}
+
+void isis_finish(struct isis *isis)
+{
+ struct vrf *vrf = NULL;
+
+ isis_delete(isis);
+ if (isis->name) {
+ vrf = vrf_lookup_by_name(isis->name);
+ if (vrf)
+ isis_vrf_unlink(isis, vrf);
+ XFREE(MTYPE_ISIS, isis->name);
+ } else {
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ if (vrf)
+ isis_vrf_unlink(isis, vrf);
}
- return CMD_SUCCESS;
+ XFREE(MTYPE_ISIS, isis);
+}
+
+void isis_terminate()
+{
+ struct isis *isis = NULL;
+ struct listnode *node, *nnode;
+
+ if (listcount(im->isis) == 0)
+ return;
+
+ for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis))
+ isis_finish(isis);
}
#ifdef FABRICD
@@ -366,10 +512,10 @@ int area_net_title(struct vty *vty, const char *net_title)
uint8_t buff[255];
/* We check that we are not over the maximal number of addresses */
- if (listcount(area->area_addrs) >= isis->max_area_addrs) {
+ if (listcount(area->area_addrs) >= area->isis->max_area_addrs) {
vty_out(vty,
"Maximum of area addresses (%d) already reached \n",
- isis->max_area_addrs);
+ area->isis->max_area_addrs);
return CMD_ERR_NOTHING_TODO;
}
@@ -395,20 +541,21 @@ int area_net_title(struct vty *vty, const char *net_title)
return CMD_WARNING_CONFIG_FAILED;
}
- if (isis->sysid_set == 0) {
+ if (area->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;
+ memcpy(area->isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN);
+ area->isis->sysid_set = 1;
if (IS_DEBUG_EVENTS)
zlog_debug("Router has SystemID %s",
- sysid_print(isis->sysid));
+ sysid_print(area->isis->sysid));
} else {
/*
* Check that the SystemID portions match
*/
- if (memcmp(isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN)) {
+ if (memcmp(area->isis->sysid, GETSYSID(addr),
+ ISIS_SYS_ID_LEN)) {
vty_out(vty,
"System ID must not change when defining additional area addresses\n");
XFREE(MTYPE_ISIS_AREA_ADDR, addr);
@@ -480,8 +627,8 @@ int area_clear_net_title(struct vty *vty, const char *net_title)
* 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;
+ memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN);
+ area->isis->sysid_set = 0;
if (IS_DEBUG_EVENTS)
zlog_debug("Router has no SystemID");
}
@@ -493,100 +640,144 @@ int area_clear_net_title(struct vty *vty, const char *net_title)
* 'show isis interface' command
*/
-int show_isis_interface_common(struct vty *vty, const char *ifname, char detail)
+int show_isis_interface_common(struct vty *vty, const char *ifname, char detail,
+ const char *vrf_name, bool all_vrf)
{
- struct listnode *anode, *cnode;
+ struct listnode *anode, *cnode, *mnode, *inode;
struct isis_area *area;
struct isis_circuit *circuit;
+ struct isis *isis = NULL;
- if (!isis) {
+ if (!im) {
vty_out(vty, "IS-IS Routing Process not enabled\n");
return CMD_SUCCESS;
}
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, mnode, inode, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list,
+ anode, area)) {
+ vty_out(vty, "Area %s:\n",
+ area->area_tag);
+
+ if (detail == ISIS_UI_LEVEL_BRIEF)
+ vty_out(vty,
+ " Interface CircId State Type Level\n");
+
+ 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 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode,
+ area)) {
+ vty_out(vty, "Area %s:\n", area->area_tag);
- for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
- vty_out(vty, "Area %s:\n", area->area_tag);
-
- if (detail == ISIS_UI_LEVEL_BRIEF)
- vty_out(vty,
- " Interface CircId State Type Level\n");
-
- 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);
+ if (detail == ISIS_UI_LEVEL_BRIEF)
+ vty_out(vty,
+ " Interface CircId State Type Level\n");
+
+ 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 " PROTO_NAME " interface",
- SHOW_STR
- PROTO_HELP
- "ISIS interface\n")
+DEFUN(show_isis_interface,
+ show_isis_interface_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] interface",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "IS-IS interface\n")
{
- return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF);
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF,
+ vrf_name, all_vrf);
}
-DEFUN (show_isis_interface_detail,
- show_isis_interface_detail_cmd,
- "show " PROTO_NAME " interface detail",
- SHOW_STR
- PROTO_HELP
- "ISIS interface\n"
- "show detailed information\n")
+DEFUN(show_isis_interface_detail,
+ show_isis_interface_detail_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] interface detail",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "IS-IS interface\n"
+ "show detailed information\n")
{
- return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL);
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL,
+ vrf_name, all_vrf);
}
-DEFUN (show_isis_interface_arg,
- show_isis_interface_arg_cmd,
- "show " PROTO_NAME " interface WORD",
- SHOW_STR
- PROTO_HELP
- "ISIS interface\n"
- "ISIS interface name\n")
+DEFUN(show_isis_interface_arg,
+ show_isis_interface_arg_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] interface WORD",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "IS-IS interface\n"
+ "IS-IS interface name\n")
{
- int idx_word = 3;
- return show_isis_interface_common(vty, argv[idx_word]->arg,
- ISIS_UI_LEVEL_DETAIL);
-}
+ int idx_word = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
-/*
- * 'show isis neighbor' command
- */
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
-int show_isis_neighbor_common(struct vty *vty, const char *id, char detail)
+ char *ifname = argv_find(argv, argc, "WORD", &idx_word)
+ ? argv[idx_word]->arg
+ : NULL;
+ return show_isis_interface_common(vty, ifname, ISIS_UI_LEVEL_DETAIL,
+ vrf_name, all_vrf);
+}
+
+static void isis_neighbor_common(struct vty *vty, const char *id, char detail,
+ struct isis *isis, uint8_t *sysid)
{
struct listnode *anode, *cnode, *node;
struct isis_area *area;
struct isis_circuit *circuit;
struct list *adjdb;
struct isis_adjacency *adj;
- struct isis_dynhn *dynhn;
- uint8_t sysid[ISIS_SYS_ID_LEN];
int i;
- if (!isis) {
- vty_out(vty, "IS-IS Routing Process not enabled\n");
- 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\n", id);
- 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:\n", area->area_tag);
@@ -602,9 +793,10 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail)
for (ALL_LIST_ELEMENTS_RO(
adjdb, node, adj))
if (!id
- || !memcmp(adj->sysid,
- sysid,
- ISIS_SYS_ID_LEN))
+ || !memcmp(
+ adj->sysid,
+ sysid,
+ ISIS_SYS_ID_LEN))
isis_adj_print_vty(
adj,
vty,
@@ -622,24 +814,20 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail)
}
}
- return CMD_SUCCESS;
}
-
/*
- * 'clear isis neighbor' command
+ * 'show isis neighbor' command
*/
-int clear_isis_neighbor_common(struct vty *vty, const char *id)
+
+int show_isis_neighbor_common(struct vty *vty, const char *id, char detail,
+ const char *vrf_name, bool all_vrf)
{
- struct listnode *anode, *cnode, *cnextnode, *node, *nnode;
- struct isis_area *area;
- struct isis_circuit *circuit;
- struct list *adjdb;
- struct isis_adjacency *adj;
+ struct listnode *nnode, *inode;
struct isis_dynhn *dynhn;
uint8_t sysid[ISIS_SYS_ID_LEN];
- int i;
+ struct isis *isis = NULL;
- if (!isis) {
+ if (!im) {
vty_out(vty, "IS-IS Routing Process not enabled\n");
return CMD_SUCCESS;
}
@@ -656,6 +844,32 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id)
}
}
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ isis_neighbor_common(vty, id, detail, isis,
+ sysid);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ isis_neighbor_common(vty, id, detail, isis, sysid);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void isis_neighbor_common_clear(struct vty *vty, const char *id,
+ uint8_t *sysid, struct isis *isis)
+{
+ struct listnode *anode, *cnode, *cnextnode, *node, *nnode;
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+ struct list *adjdb;
+ struct isis_adjacency *adj;
+ int i;
+
for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
for (ALL_LIST_ELEMENTS(area->circuit_list, cnode, cnextnode,
circuit)) {
@@ -667,9 +881,10 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id)
adjdb, node, nnode,
adj))
if (!id
- || !memcmp(adj->sysid,
- sysid,
- ISIS_SYS_ID_LEN))
+ || !memcmp(
+ adj->sysid,
+ sysid,
+ ISIS_SYS_ID_LEN))
isis_adj_state_change(
&adj,
ISIS_ADJ_DOWN,
@@ -688,64 +903,149 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id)
}
}
}
-
- return CMD_SUCCESS;
}
-
-DEFUN (show_isis_neighbor,
- show_isis_neighbor_cmd,
- "show " PROTO_NAME " neighbor",
- SHOW_STR
- PROTO_HELP
- "ISIS neighbor adjacencies\n")
+/*
+ * 'clear isis neighbor' command
+ */
+int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_name,
+ bool all_vrf)
{
- return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF);
-}
+ struct listnode *nnode, *inode;
+ struct isis_dynhn *dynhn;
+ uint8_t sysid[ISIS_SYS_ID_LEN];
+ struct isis *isis = NULL;
-DEFUN (show_isis_neighbor_detail,
- show_isis_neighbor_detail_cmd,
- "show " PROTO_NAME " neighbor detail",
- SHOW_STR
- PROTO_HELP
- "ISIS neighbor adjacencies\n"
- "show detailed information\n")
-{
- return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL);
-}
+ if (!im) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
-DEFUN (show_isis_neighbor_arg,
- show_isis_neighbor_arg_cmd,
- "show " PROTO_NAME " neighbor WORD",
- SHOW_STR
- PROTO_HELP
- "ISIS neighbor adjacencies\n"
- "System id\n")
-{
- int idx_word = 3;
- return show_isis_neighbor_common(vty, argv[idx_word]->arg,
- ISIS_UI_LEVEL_DETAIL);
-}
+ 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\n", id);
+ return CMD_SUCCESS;
+ }
+ memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN);
+ }
+ }
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ isis_neighbor_common_clear(vty, id, sysid,
+ isis);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ isis_neighbor_common_clear(vty, id, sysid, isis);
+ }
-DEFUN (clear_isis_neighbor,
- clear_isis_neighbor_cmd,
- "clear " PROTO_NAME " neighbor",
- CLEAR_STR
- PROTO_HELP
- "ISIS neighbor adjacencies\n")
-{
- return clear_isis_neighbor_common(vty, NULL);
+ return CMD_SUCCESS;
}
-DEFUN (clear_isis_neighbor_arg,
- clear_isis_neighbor_arg_cmd,
- "clear " PROTO_NAME " neighbor WORD",
- CLEAR_STR
- PROTO_HELP
- "ISIS neighbor adjacencies\n"
- "System id\n")
-{
- int idx_word = 3;
- return clear_isis_neighbor_common(vty, argv[idx_word]->arg);
+DEFUN(show_isis_neighbor,
+ show_isis_neighbor_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] neighbor",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All vrfs\n"
+ "IS-IS neighbor adjacencies\n")
+{
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF,
+ vrf_name, all_vrf);
+}
+
+DEFUN(show_isis_neighbor_detail,
+ show_isis_neighbor_detail_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "all vrfs\n"
+ "IS-IS neighbor adjacencies\n"
+ "show detailed information\n")
+{
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL,
+ vrf_name, all_vrf);
+}
+
+DEFUN(show_isis_neighbor_arg,
+ show_isis_neighbor_arg_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD",
+ SHOW_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All vrfs\n"
+ "IS-IS neighbor adjacencies\n"
+ "System id\n")
+{
+ int idx_word = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ char *id = argv_find(argv, argc, "WORD", &idx_word)
+ ? argv[idx_word]->arg
+ : NULL;
+
+ return show_isis_neighbor_common(vty, id, ISIS_UI_LEVEL_DETAIL,
+ vrf_name, all_vrf);
+}
+
+DEFUN(clear_isis_neighbor,
+ clear_isis_neighbor_cmd,
+ "clear " PROTO_NAME " [vrf <NAME|all>] neighbor",
+ CLEAR_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All vrfs\n"
+ "IS-IS neighbor adjacencies\n")
+{
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return clear_isis_neighbor_common(vty, NULL, vrf_name, all_vrf);
+}
+
+DEFUN(clear_isis_neighbor_arg,
+ clear_isis_neighbor_arg_cmd,
+ "clear " PROTO_NAME " [vrf <NAME|all>] neighbor WORD",
+ CLEAR_STR
+ PROTO_HELP
+ VRF_CMD_HELP_STR
+ "All vrfs\n"
+ "IS-IS neighbor adjacencies\n"
+ "System id\n")
+{
+ int idx_word = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+
+ char *id = argv_find(argv, argc, "WORD", &idx_word)
+ ? argv[idx_word]->arg
+ : NULL;
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return clear_isis_neighbor_common(vty, id, vrf_name, all_vrf);
}
/*
@@ -1248,34 +1548,41 @@ DEFUN (no_debug_isis_bfd,
return CMD_SUCCESS;
}
-DEFUN (show_hostname,
- show_hostname_cmd,
- "show " PROTO_NAME " hostname",
- SHOW_STR
- PROTO_HELP
- "IS-IS Dynamic hostname mapping\n")
+DEFUN(show_hostname, show_hostname_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] hostname",
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "IS-IS Dynamic hostname mapping\n")
{
- dynhn_print_all(vty);
+ struct listnode *nnode, *inode;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+ int idx_vrf = 0;
+ struct isis *isis = NULL;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ dynhn_print_all(vty, isis);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ dynhn_print_all(vty, isis);
+ }
return CMD_SUCCESS;
}
-DEFUN (show_isis_spf_ietf,
- show_isis_spf_ietf_cmd,
- "show " PROTO_NAME " spf-delay-ietf",
- SHOW_STR
- PROTO_HELP
- "SPF delay IETF information\n")
+static void isis_spf_ietf_common(struct vty *vty, struct isis *isis)
{
- if (!isis) {
- vty_out(vty, "ISIS is not running\n");
- return CMD_SUCCESS;
- }
-
struct listnode *node;
struct isis_area *area;
-
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+
+ vty_out(vty, "vrf : %s\n", isis->name);
vty_out(vty, "Area %s:\n",
area->area_tag ? area->area_tag : "null");
@@ -1306,23 +1613,49 @@ DEFUN (show_isis_spf_ietf,
}
}
}
+}
+
+DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] spf-delay-ietf",
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "SPF delay IETF information\n")
+{
+ struct listnode *nnode, *inode;
+ struct isis *isis = NULL;
+ int idx_vrf = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
+
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf)
+
+ if (!im) {
+ vty_out(vty, "ISIS is not running\n");
+ return CMD_SUCCESS;
+ }
+
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ isis_spf_ietf_common(vty, isis);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ isis_spf_ietf_common(vty, isis);
+ }
+
return CMD_SUCCESS;
}
-DEFUN (show_isis_summary,
- show_isis_summary_cmd,
- "show " PROTO_NAME " summary",
- SHOW_STR PROTO_HELP "summary\n")
+static void common_isis_summary(struct vty *vty, struct isis *isis)
{
struct listnode *node, *node2;
struct isis_area *area;
int level;
- if (isis == NULL) {
- vty_out(vty, PROTO_NAME " is not running\n");
- return CMD_SUCCESS;
- }
-
+ vty_out(vty, "vrf : %s\n", isis->name);
vty_out(vty, "Process Id : %ld\n", isis->process_id);
if (isis->sysid_set)
vty_out(vty, "System Id : %s\n",
@@ -1344,7 +1677,7 @@ DEFUN (show_isis_summary,
if (tier == ISIS_TIER_UNDEFINED)
vty_out(vty, " Tier: undefined\n");
else
- vty_out(vty, " Tier: %" PRIu8 "\n", tier);
+ vty_out(vty, " Tier: %hhu\n", tier);
}
if (listcount(area->area_addrs) > 0) {
@@ -1390,25 +1723,68 @@ DEFUN (show_isis_summary,
" (not used, IETF SPF delay activated)");
vty_out(vty, "\n");
- vty_out(vty, " IPv4 route computation:\n");
- isis_spf_print(area->spftree[SPFTREE_IPV4][level - 1],
- vty);
+ if (area->ip_circuits) {
+ vty_out(vty, " IPv4 route computation:\n");
+ isis_spf_print(
+ area->spftree[SPFTREE_IPV4][level - 1],
+ vty);
+ }
+
+ if (area->ipv6_circuits) {
+ vty_out(vty, " IPv6 route computation:\n");
+ isis_spf_print(
+ area->spftree[SPFTREE_IPV6][level - 1],
+ vty);
+ }
+
+ if (area->ipv6_circuits
+ && isis_area_ipv6_dstsrc_enabled(area)) {
+ vty_out(vty,
+ " IPv6 dst-src route computation:\n");
+ isis_spf_print(area->spftree[SPFTREE_DSTSRC]
+ [level - 1],
+ vty);
+ }
+ }
+ }
+}
- vty_out(vty, " IPv6 route computation:\n");
- isis_spf_print(area->spftree[SPFTREE_IPV6][level - 1],
- vty);
+DEFUN(show_isis_summary, show_isis_summary_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] summary",
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "summary\n")
+{
+ struct listnode *inode, *nnode;
+ int idx_vrf = 0;
+ struct isis *isis = NULL;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
- vty_out(vty, " IPv6 dst-src route computation:\n");
- isis_spf_print(area->spftree[SPFTREE_DSTSRC][level-1],
- vty);
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf)
+ if (!im) {
+ vty_out(vty, PROTO_NAME " is not running\n");
+ return CMD_SUCCESS;
+ }
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ common_isis_summary(vty, isis);
+ }
+ return 0;
}
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ common_isis_summary(vty, isis);
}
+
vty_out(vty, "\n");
return CMD_SUCCESS;
}
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv)
+struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
+ struct isis *isis)
{
char sysid[255] = {0};
uint8_t number[3];
@@ -1468,23 +1844,8 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv)
return lsp;
}
-/*
- * This function supports following display options:
- * [ show isis database [detail] ]
- * [ show isis database <sysid> [detail] ]
- * [ show isis database <hostname> [detail] ]
- * [ show isis database <sysid>.<pseudo-id> [detail] ]
- * [ show isis database <hostname>.<pseudo-id> [detail] ]
- * [ show isis database <sysid>.<pseudo-id>-<fragment-number> [detail] ]
- * [ show isis database <hostname>.<pseudo-id>-<fragment-number> [detail] ]
- * [ show isis database detail <sysid> ]
- * [ show isis database detail <hostname> ]
- * [ show isis database detail <sysid>.<pseudo-id> ]
- * [ show isis database detail <hostname>.<pseudo-id> ]
- * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ]
- * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ]
- */
-static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
+static int show_isis_database_common(struct vty *vty, const char *argv,
+ int ui_level, struct isis *isis)
{
struct listnode *node;
struct isis_area *area;
@@ -1500,7 +1861,9 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
for (level = 0; level < ISIS_LEVELS; level++) {
if (lspdb_count(&area->lspdb[level]) > 0) {
- lsp = lsp_for_arg(&area->lspdb[level], argv);
+ lsp = NULL;
+ lsp = lsp_for_arg(&area->lspdb[level], argv,
+ isis);
if (lsp != NULL || argv == NULL) {
vty_out(vty,
@@ -1516,14 +1879,17 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
if (ui_level == ISIS_UI_LEVEL_DETAIL)
lsp_print_detail(
lsp, vty,
- area->dynhostname);
+ area->dynhostname,
+ isis);
else
lsp_print(lsp, vty,
- area->dynhostname);
+ area->dynhostname,
+ isis);
} else if (argv == NULL) {
lsp_count = lsp_print_all(
vty, &area->lspdb[level],
- ui_level, area->dynhostname);
+ ui_level, area->dynhostname,
+ isis);
vty_out(vty, " %u LSPs\n\n",
lsp_count);
@@ -1531,25 +1897,64 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level)
}
}
}
+ return CMD_SUCCESS;
+}
+/*
+ * This function supports following display options:
+ * [ show isis database [detail] ]
+ * [ show isis database <sysid> [detail] ]
+ * [ show isis database <hostname> [detail] ]
+ * [ show isis database <sysid>.<pseudo-id> [detail] ]
+ * [ show isis database <hostname>.<pseudo-id> [detail] ]
+ * [ show isis database <sysid>.<pseudo-id>-<fragment-number> [detail] ]
+ * [ show isis database <hostname>.<pseudo-id>-<fragment-number> [detail] ]
+ * [ show isis database detail <sysid> ]
+ * [ show isis database detail <hostname> ]
+ * [ show isis database detail <sysid>.<pseudo-id> ]
+ * [ show isis database detail <hostname>.<pseudo-id> ]
+ * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ]
+ * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ]
+ */
+static int show_isis_database(struct vty *vty, const char *argv, int ui_level,
+ const char *vrf_name, bool all_vrf)
+{
+ struct listnode *inode, *nnode;
+ struct isis *isis = NULL;
+
+ if (vrf_name) {
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
+ show_isis_database_common(vty, argv, ui_level,
+ isis);
+ }
+ return 0;
+ }
+ isis = isis_lookup_by_vrfname(vrf_name);
+ if (isis != NULL)
+ show_isis_database_common(vty, argv, ui_level, isis);
+ }
return CMD_SUCCESS;
}
-DEFUN (show_database,
- show_database_cmd,
- "show " PROTO_NAME " database [detail] [WORD]",
- SHOW_STR
- PROTO_HELP
- "Link state database\n"
- "Detailed information\n"
- "LSP ID\n")
+DEFUN(show_database, show_database_cmd,
+ "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD]",
+ SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "Link state database\n"
+ "Detailed information\n"
+ "LSP ID\n")
{
int idx = 0;
+ int idx_vrf = 0;
+ const char *vrf_name = VRF_DEFAULT_NAME;
+ bool all_vrf = false;
int uilevel = argv_find(argv, argc, "detail", &idx)
? ISIS_UI_LEVEL_DETAIL
: ISIS_UI_LEVEL_BRIEF;
char *id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL;
- return show_isis_database(vty, id, uilevel);
+ ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+ return show_isis_database(vty, id, uilevel, vrf_name, all_vrf);
}
#ifdef FABRICD
@@ -1578,8 +1983,20 @@ DEFUN (no_router_openfabric,
PROTO_HELP
"ISO Routing area tag\n")
{
+ struct isis_area *area;
+ const char *area_tag;
int idx_word = 3;
- return isis_area_destroy(argv[idx_word]->arg);
+
+ area_tag = argv[idx_word]->arg;
+ area = isis_area_lookup(area_tag, VRF_DEFAULT);
+ if (area == NULL) {
+ zlog_warn("%s: could not find area with area-tag %s",
+ __func__, area_tag);
+ return CMD_ERR_NO_MATCH;
+ }
+
+ isis_area_destroy(area);
+ return CMD_SUCCESS;
}
#endif /* ifdef FABRICD */
#ifdef FABRICD
@@ -1934,10 +2351,16 @@ DEFUN (no_log_adj_changes,
static int isis_config_write(struct vty *vty)
{
int write = 0;
+ struct isis_area *area;
+ struct listnode *node, *node2, *inode, *nnode;
+ struct isis *isis = NULL;
+
+ if (!im) {
+ vty_out(vty, "IS-IS Routing Process not enabled\n");
+ return CMD_SUCCESS;
+ }
- if (isis != NULL) {
- struct isis_area *area;
- struct listnode *node, *node2;
+ for (ALL_LIST_ELEMENTS(im->isis, nnode, inode, isis)) {
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
/* ISIS - Area name */
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 57d9691cc7..41b69df2bf 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -55,6 +55,12 @@ static const bool fabricd = false;
extern void isis_cli_init(void);
#endif
+#define ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \
+ if (argv_find(argv, argc, "vrf", &idx_vrf)) { \
+ vrf_name = argv[idx_vrf + 1]->arg; \
+ all_vrf = strmatch(vrf_name, "all"); \
+ }
+
extern struct zebra_privs_t isisd_privs;
/* uncomment if you are a developer in bug hunt */
@@ -62,8 +68,18 @@ extern struct zebra_privs_t isisd_privs;
struct fabricd;
+struct isis_master {
+ /* ISIS instance. */
+ struct list *isis;
+ /* ISIS thread master. */
+ struct thread_master *master;
+ /* Various OSPF global configuration. */
+ uint8_t options;
+};
+
struct isis {
vrf_id_t vrf_id;
+ char *name;
unsigned long process_id;
int sysid_set;
uint8_t sysid[ISIS_SYS_ID_LEN]; /* SystemID for this IS */
@@ -77,12 +93,9 @@ struct isis {
uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */
struct route_table *ext_info[REDIST_PROTOCOL_COUNT];
-
- QOBJ_FIELDS
};
-extern struct isis *isis;
-DECLARE_QOBJ_TYPE(isis_area)
+extern struct isis_master *im;
enum spf_tree_id {
SPFTREE_IPV4 = 0,
@@ -110,6 +123,7 @@ struct isis_area {
#define DEFAULT_LSP_MTU 1497
unsigned int lsp_mtu; /* Size of LSPs to generate */
struct list *circuit_list; /* IS-IS circuits */
+ struct list *adjacency_list; /* IS-IS adjacencies */
struct flags flags;
struct thread *t_tick; /* LSP walker */
struct thread *t_lsp_refresh[ISIS_LEVELS];
@@ -193,14 +207,25 @@ struct isis_area {
};
DECLARE_QOBJ_TYPE(isis_area)
+void isis_terminate(void);
+void isis_finish(struct isis *isis);
+void isis_master_init(struct thread_master *master);
+void isis_vrf_link(struct isis *isis, struct vrf *vrf);
+void isis_vrf_unlink(struct isis *isis, struct vrf *vrf);
+void isis_global_instance_create(void);
+struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id);
+struct isis *isis_lookup_by_vrfname(const char *vrfname);
+struct isis *isis_lookup_by_sysid(uint8_t *sysid);
+
void isis_init(void);
-void isis_new(unsigned long process_id, vrf_id_t vrf_id);
-struct isis_area *isis_area_create(const char *);
-struct isis_area *isis_area_lookup(const char *);
+struct isis *isis_new(vrf_id_t vrf_id);
+struct isis_area *isis_area_create(const char *, const char *);
+struct isis_area *isis_area_lookup(const char *, vrf_id_t vrf_id);
int isis_area_get(struct vty *vty, const char *area_tag);
-int isis_area_destroy(const char *area_tag);
+void isis_area_destroy(struct isis_area *area);
void print_debug(struct vty *, int, int);
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv);
+struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
+ struct isis *isis);
void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
diff --git a/ldpd/hello.c b/ldpd/hello.c
index a8d6e58cda..ac24704bca 100644
--- a/ldpd/hello.c
+++ b/ldpd/hello.c
@@ -234,8 +234,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
* check)".
*/
if (flags & F_HELLO_TARGETED) {
- log_debug("%s: lsr-id %s: invalid targeted hello "
- "transport address %s", __func__, inet_ntoa(lsr_id),
+ log_debug("%s: lsr-id %s: invalid targeted hello transport address %s", __func__, inet_ntoa(lsr_id),
log_addr(af, &trans_addr));
return;
}
@@ -250,8 +249,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
* targeted LDP Hello packet's source or destination addresses".
*/
if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&src->v6)) {
- log_debug("%s: lsr-id %s: targeted hello with "
- "link-local source address", __func__,
+ log_debug("%s: lsr-id %s: targeted hello with link-local source address", __func__,
inet_ntoa(lsr_id));
return;
}
@@ -318,8 +316,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
* send a fatal Notification message with status code of
* 'Transport Connection Mismatch' and reset the session".
*/
- log_debug("%s: lsr-id %s: remote transport preference does not "
- "match the local preference", __func__, inet_ntoa(lsr_id));
+ log_debug("%s: lsr-id %s: remote transport preference does not match the local preference", __func__, inet_ntoa(lsr_id));
if (nbr)
session_shutdown(nbr, S_TRANS_MISMTCH, msg->id,
msg->type);
@@ -359,8 +356,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
if (nbr && nbr->af == af &&
(ldp_addrcmp(af, &nbr->raddr, &trans_addr) ||
nbr->raddr_scope != scope_id)) {
- log_warnx("%s: lsr-id %s: hello packet advertising a different "
- "transport address", __func__, inet_ntoa(lsr_id));
+ log_warnx("%s: lsr-id %s: hello packet advertising a different transport address", __func__, inet_ntoa(lsr_id));
if (adj)
adj_del(adj, S_SHUTDOWN);
return;
@@ -368,8 +364,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af,
if (nbr == NULL) {
nbrt = nbr_find_addr(af, &trans_addr);
if (nbrt) {
- log_debug("%s: transport address %s is already being "
- "used by lsr-id %s", __func__, log_addr(af,
+ log_debug("%s: transport address %s is already being used by lsr-id %s", __func__, log_addr(af,
&trans_addr), inet_ntoa(nbrt->id));
if (adj)
adj_del(adj, S_SHUTDOWN);
diff --git a/ldpd/init.c b/ldpd/init.c
index 8b2abe85e5..30b78315f9 100644
--- a/ldpd/init.c
+++ b/ldpd/init.c
@@ -146,8 +146,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
nbr->flags |= F_NBR_CAP_DYNAMIC;
- log_debug("%s: lsr-id %s announced the Dynamic "
- "Capability Announcement capability", __func__,
+ log_debug("%s: lsr-id %s announced the Dynamic Capability Announcement capability", __func__,
inet_ntoa(nbr->id));
break;
case TLV_TYPE_TWCARD_CAP:
@@ -166,8 +165,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
nbr->flags |= F_NBR_CAP_TWCARD;
- log_debug("%s: lsr-id %s announced the Typed Wildcard "
- "FEC capability", __func__, inet_ntoa(nbr->id));
+ log_debug("%s: lsr-id %s announced the Typed Wildcard FEC capability", __func__, inet_ntoa(nbr->id));
break;
case TLV_TYPE_UNOTIF_CAP:
if (tlv_len != CAP_TLV_UNOTIF_LEN) {
@@ -185,8 +183,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
nbr->flags |= F_NBR_CAP_UNOTIF;
- log_debug("%s: lsr-id %s announced the Unrecognized "
- "Notification capability", __func__,
+ log_debug("%s: lsr-id %s announced the Unrecognized Notification capability", __func__,
inet_ntoa(nbr->id));
break;
default:
@@ -321,8 +318,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len)
else
nbr->flags &= ~F_NBR_CAP_TWCARD;
- log_debug("%s: lsr-id %s %s the Typed Wildcard FEC "
- "capability", __func__, inet_ntoa(nbr->id),
+ log_debug("%s: lsr-id %s %s the Typed Wildcard FEC capability", __func__, inet_ntoa(nbr->id),
(enable) ? "announced" : "withdrew");
break;
case TLV_TYPE_UNOTIF_CAP:
@@ -346,8 +342,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len)
else
nbr->flags &= ~F_NBR_CAP_UNOTIF;
- log_debug("%s: lsr-id %s %s the Unrecognized "
- "Notification capability", __func__,
+ log_debug("%s: lsr-id %s %s the Unrecognized Notification capability", __func__,
inet_ntoa(nbr->id), (enable) ? "announced" :
"withdrew");
break;
diff --git a/ldpd/interface.c b/ldpd/interface.c
index c7d6dea518..371c7d0bb1 100644
--- a/ldpd/interface.c
+++ b/ldpd/interface.c
@@ -528,8 +528,7 @@ if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket,
IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) {
- log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
- "address %s", __func__, iface->name, inet_ntoa(*addr));
+ log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s address %s", __func__, iface->name, inet_ntoa(*addr));
return (-1);
}
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c
index 0d479e77ba..2c68f3edbd 100644
--- a/ldpd/l2vpn.c
+++ b/ldpd/l2vpn.c
@@ -294,6 +294,16 @@ l2vpn_pw_reset(struct l2vpn_pw *pw)
pw->flags |= F_PW_STATUSTLV;
else
pw->flags &= ~F_PW_STATUSTLV;
+
+ if (pw->flags & F_PW_STATUSTLV_CONF) {
+ struct fec_node *fn;
+ struct fec fec;
+ l2vpn_pw_fec(pw, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn)
+ pw->remote_status = fn->pw_remote_status;
+ }
+
}
int
@@ -433,6 +443,8 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
/* unknown fec */
return;
+ fn->pw_remote_status = nm->pw_status;
+
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL)
return;
diff --git a/ldpd/lde.c b/ldpd/lde.c
index afcbe6cd44..734c1ea230 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -296,7 +296,7 @@ lde_dispatch_imsg(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_LABEL_MAPPING:
- lde_check_mapping(map, ln);
+ lde_check_mapping(map, ln, 1);
break;
case IMSG_LABEL_REQUEST:
lde_check_request(map, ln);
@@ -325,8 +325,7 @@ lde_dispatch_imsg(struct thread *thread)
break;
}
if (lde_address_add(ln, lde_addr) < 0) {
- log_debug("%s: cannot add address %s, it "
- "already exists", __func__,
+ log_debug("%s: cannot add address %s, it already exists", __func__,
log_addr(lde_addr->af, &lde_addr->addr));
}
break;
@@ -343,8 +342,7 @@ lde_dispatch_imsg(struct thread *thread)
break;
}
if (lde_address_del(ln, lde_addr) < 0) {
- log_debug("%s: cannot delete address %s, it "
- "does not exist", __func__,
+ log_debug("%s: cannot delete address %s, it does not exist", __func__,
log_addr(lde_addr->af, &lde_addr->addr));
}
break;
@@ -382,8 +380,7 @@ lde_dispatch_imsg(struct thread *thread)
fatalx("lde_dispatch_imsg: wrong imsg len");
if (lde_nbr_find(imsg.hdr.peerid))
- fatalx("lde_dispatch_imsg: "
- "neighbor already exists");
+ fatalx("lde_dispatch_imsg: neighbor already exists");
lde_nbr_new(imsg.hdr.peerid, imsg.data);
break;
case IMSG_NEIGHBOR_DOWN:
@@ -534,13 +531,11 @@ lde_dispatch_parent(struct thread *thread)
break;
case IMSG_SOCKET_IPC:
if (iev_ldpe) {
- log_warnx("%s: received unexpected imsg fd "
- "to ldpe", __func__);
+ log_warnx("%s: received unexpected imsg fd to ldpe", __func__);
break;
}
if ((fd = imsg.fd) == -1) {
- log_warnx("%s: expected to receive imsg fd to "
- "ldpe but didn't receive any", __func__);
+ log_warnx("%s: expected to receive imsg fd to ldpe but didn't receive any", __func__);
break;
}
@@ -975,8 +970,7 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)
lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);
if (lw) {
if (!fec_find(&ln->sent_map_pending, &fn->fec)) {
- debug_evt("%s: FEC %s: scheduling to send label "
- "mapping later (waiting for pending label release)",
+ debug_evt("%s: FEC %s: scheduling to send label mapping later (waiting for pending label release)",
__func__, log_fec(&fn->fec));
lde_map_pending_add(ln, fn);
}
diff --git a/ldpd/lde.h b/ldpd/lde.h
index 2895e00ae5..9e6db3a90b 100644
--- a/ldpd/lde.h
+++ b/ldpd/lde.h
@@ -125,6 +125,9 @@ struct fec_node {
struct lde_map_head upstream; /* sent mappings */
uint32_t local_label;
+
+ uint32_t pw_remote_status;
+
void *data; /* fec specific data */
};
@@ -209,7 +212,7 @@ void lde_kernel_insert(struct fec *, int, union ldpd_addr *,
void lde_kernel_remove(struct fec *, int, union ldpd_addr *,
ifindex_t, uint8_t, unsigned short);
void lde_kernel_update(struct fec *);
-void lde_check_mapping(struct map *, struct lde_nbr *);
+void lde_check_mapping(struct map *, struct lde_nbr *, int);
void lde_check_request(struct map *, struct lde_nbr *);
void lde_check_request_wcard(struct map *, struct lde_nbr *);
void lde_check_release(struct map *, struct lde_nbr *);
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 8f524e0aa9..11d85b7449 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -267,6 +267,9 @@ fec_add(struct fec *fec)
RB_INIT(lde_map_head, &fn->downstream);
LIST_INIT(&fn->nexthops);
+ if (fec->type == FEC_TYPE_PWID)
+ fn->pw_remote_status = PW_FORWARDING;
+
if (fec_insert(&ft, &fn->fec))
log_warnx("failed to add %s to ft tree",
log_fec(&fn->fec));
@@ -455,13 +458,13 @@ lde_kernel_update(struct fec *fec)
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me)
/* FEC.5 */
- lde_check_mapping(&me->map, ln);
+ lde_check_mapping(&me->map, ln, 0);
}
}
}
void
-lde_check_mapping(struct map *map, struct lde_nbr *ln)
+lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
{
struct fec fec;
struct fec_node *fn;
@@ -507,8 +510,12 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)
lde_req_del(ln, lre, 1);
/* RFC 4447 control word and status tlv negotiation */
- if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map))
+ if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) {
+ if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS)
+ fn->pw_remote_status = map->pw_status;
+
return;
+ }
/*
* LMp.3 - LMp.8: loop detection - unnecessary for frame-mode
@@ -570,8 +577,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)
pw->remote_group = map->fec.pwid.group_id;
if (map->flags & F_MAP_PW_IFMTU)
pw->remote_mtu = map->fec.pwid.ifmtu;
- if (map->flags & F_MAP_PW_STATUS)
+ if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) {
pw->remote_status = map->pw_status;
+ fn->pw_remote_status = map->pw_status;
+ }
else
pw->remote_status = PW_FORWARDING;
fnh->remote_label = map->label;
@@ -847,6 +856,9 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
if (me && (map->label == NO_LABEL || map->label == me->map.label))
/* LWd.4: remove record of previously received lbl mapping */
lde_map_del(ln, me, 0);
+ else
+ /* LWd.13 done */
+ return;
/* Ordered Control: additional withdraw steps */
if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) {
@@ -858,15 +870,17 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
/* LWd.9: check if previously sent a label mapping */
me = (struct lde_map *)fec_find(&lnbr->sent_map,
&fn->fec);
+
/*
* LWd.10: does label sent to peer "map" to withdraw
* label
*/
- if (me)
+ if (me && lde_nbr_is_nexthop(fn, lnbr))
/* LWd.11: send label withdraw */
lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
}
}
+
}
void
@@ -924,24 +938,33 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
* label mapping
*/
lde_map_del(ln, me, 0);
+ else
+ /* LWd.13 done */
+ continue;
/* Ordered Control: additional withdraw steps */
if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) {
- /* LWd.8: for each neighbor other that src of withdraw msg */
+ /*
+ * LWd.8: for each neighbor other that src of
+ * withdraw msg
+ */
RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
if (ln->peerid == lnbr->peerid)
continue;
- /* LWd.9: check if previously sent a label mapping */
- me = (struct lde_map *)fec_find(&lnbr->sent_map,
- &fn->fec);
+ /* LWd.9: check if previously sent a label
+ * mapping
+ */
+ me = (struct lde_map *)fec_find(
+ &lnbr->sent_map, &fn->fec);
/*
- * LWd.10: does label sent to peer "map" to withdraw
- * label
+ * LWd.10: does label sent to peer "map" to
+ * withdraw label
*/
- if (me)
+ if (me && lde_nbr_is_nexthop(fn, lnbr))
/* LWd.11: send label withdraw */
- lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
+ lde_send_labelwithdraw(lnbr, fn, NULL,
+ NULL);
}
}
}
diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c
index 3abd0817a8..03dee23b47 100644
--- a/ldpd/ldp_vty_conf.c
+++ b/ldpd/ldp_vty_conf.c
@@ -292,12 +292,10 @@ ldp_config_write(struct vty *vty)
if (nbrp->flags & F_NBRP_GTSM) {
if (nbrp->gtsm_enabled)
- vty_out (vty, " neighbor %s ttl-security hops "
- "%u\n", inet_ntoa(nbrp->lsr_id),
+ vty_out (vty, " neighbor %s ttl-security hops %u\n", inet_ntoa(nbrp->lsr_id),
nbrp->gtsm_hops);
else
- vty_out (vty, " neighbor %s ttl-security "
- "disable\n",inet_ntoa(nbrp->lsr_id));
+ vty_out (vty, " neighbor %s ttl-security disable\n",inet_ntoa(nbrp->lsr_id));
}
if (nbrp->auth.method == AUTH_MD5SIG)
@@ -1079,8 +1077,7 @@ ldp_vty_neighbor_password(struct vty *vty, const char *negate, struct in_addr ls
password_len = strlcpy(nbrp->auth.md5key, password_str,
sizeof(nbrp->auth.md5key));
if (password_len >= sizeof(nbrp->auth.md5key))
- vty_out(vty, "%% password has been truncated to %zu "
- "characters.", sizeof(nbrp->auth.md5key) - 1);
+ vty_out(vty, "%% password has been truncated to %zu characters.", sizeof(nbrp->auth.md5key) - 1);
nbrp->auth.md5key_len = strlen(nbrp->auth.md5key);
nbrp->auth.method = AUTH_MD5SIG;
}
diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c
index 7bc2623eb3..d8ed5ccccc 100644
--- a/ldpd/ldp_vty_exec.c
+++ b/ldpd/ldp_vty_exec.c
@@ -584,8 +584,7 @@ show_nbr_detail_msg(struct vty *vty, struct imsg *imsg,
log_addr(nbr->af, &nbr->raddr),ntohs(nbr->rport));
vty_out (vty, " Authentication: %s\n",
(nbr->auth_method == AUTH_MD5SIG) ? "TCP MD5 Signature" : "none");
- vty_out(vty, " Session Holdtime: %u secs; "
- "KeepAlive interval: %u secs\n", nbr->holdtime,
+ vty_out(vty, " Session Holdtime: %u secs; KeepAlive interval: %u secs\n", nbr->holdtime,
nbr->holdtime / KEEPALIVE_PER_PERIOD);
vty_out(vty, " State: %s; Downstream-Unsolicited\n",
nbr_state_name(nbr->nbr_state));
@@ -1252,8 +1251,7 @@ show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg,
if (pw->local_label != NO_LABEL) {
vty_out (vty, " Local Label: %u\n",
pw->local_label);
- vty_out (vty, "%-8sCbit: %u, VC Type: %s, "
- "GroupID: %u\n", "", pw->local_cword,
+ vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->local_cword,
pw_type_name(pw->type),pw->local_gid);
vty_out (vty, "%-8sMTU: %u\n", "",pw->local_ifmtu);
vty_out (vty, "%-8sLast failure: %s\n", "",
@@ -1265,8 +1263,7 @@ show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg,
if (pw->remote_label != NO_LABEL) {
vty_out (vty, " Remote Label: %u\n",
pw->remote_label);
- vty_out (vty, "%-8sCbit: %u, VC Type: %s, "
- "GroupID: %u\n", "", pw->remote_cword,
+ vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->remote_cword,
pw_type_name(pw->type),pw->remote_gid);
vty_out (vty, "%-8sMTU: %u\n", "",pw->remote_ifmtu);
} else
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index e3b6f4dfcc..927c3a3c03 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -662,23 +662,19 @@ main_dispatch_lde(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_KPW_ADD:
if (kmpw_add(imsg.data))
- log_warnx("%s: error adding "
- "pseudowire", __func__);
+ log_warnx("%s: error adding pseudowire", __func__);
break;
case IMSG_KPW_DELETE:
if (kmpw_del(imsg.data))
- log_warnx("%s: error deleting "
- "pseudowire", __func__);
+ log_warnx("%s: error deleting pseudowire", __func__);
break;
case IMSG_KPW_SET:
if (kmpw_set(imsg.data))
- log_warnx("%s: error setting "
- "pseudowire", __func__);
+ log_warnx("%s: error setting pseudowire", __func__);
break;
case IMSG_KPW_UNSET:
if (kmpw_unset(imsg.data))
- log_warnx("%s: error unsetting "
- "pseudowire", __func__);
+ log_warnx("%s: error unsetting pseudowire", __func__);
break;
}
break;
@@ -857,8 +853,7 @@ main_imsg_send_net_socket(int af, enum socket_type type)
fd = ldp_create_socket(af, type);
if (fd == -1) {
- log_warnx("%s: failed to create %s socket for address-family "
- "%s", __func__, socket_name(type), af_name(af));
+ log_warnx("%s: failed to create %s socket for address-family %s", __func__, socket_name(type), af_name(af));
return;
}
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index 3f0a4fd33e..655313bf16 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -355,13 +355,11 @@ ldpe_dispatch_main(struct thread *thread)
break;
case IMSG_SOCKET_IPC:
if (iev_lde) {
- log_warnx("%s: received unexpected imsg fd "
- "to lde", __func__);
+ log_warnx("%s: received unexpected imsg fd to lde", __func__);
break;
}
if ((fd = imsg.fd) == -1) {
- log_warnx("%s: expected to receive imsg fd to "
- "lde but didn't receive any", __func__);
+ log_warnx("%s: expected to receive imsg fd to lde but didn't receive any", __func__);
break;
}
@@ -617,8 +615,7 @@ ldpe_dispatch_lde(struct thread *thread)
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
- log_debug("ldpe_dispatch_lde: cannot find "
- "neighbor");
+ log_debug("ldpe_dispatch_lde: cannot find neighbor");
break;
}
if (nbr->state != NBR_STA_OPER)
@@ -645,8 +642,7 @@ ldpe_dispatch_lde(struct thread *thread)
case IMSG_WITHDRAW_ADD_END:
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
- log_debug("ldpe_dispatch_lde: cannot find "
- "neighbor");
+ log_debug("ldpe_dispatch_lde: cannot find neighbor");
break;
}
if (nbr->state != NBR_STA_OPER)
@@ -679,8 +675,7 @@ ldpe_dispatch_lde(struct thread *thread)
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
- log_debug("ldpe_dispatch_lde: cannot find "
- "neighbor");
+ log_debug("ldpe_dispatch_lde: cannot find neighbor");
break;
}
if (nbr->state != NBR_STA_OPER)
@@ -700,8 +695,7 @@ ldpe_dispatch_lde(struct thread *thread)
case IMSG_NBR_SHUTDOWN:
nbr = nbr_find_peerid(imsg.hdr.peerid);
if (nbr == NULL) {
- log_debug("ldpe_dispatch_lde: cannot find "
- "neighbor");
+ log_debug("ldpe_dispatch_lde: cannot find neighbor");
break;
}
if (nbr->state != NBR_STA_OPER)
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
index 1aa53151e6..6143beb6b9 100644
--- a/ldpd/neighbor.c
+++ b/ldpd/neighbor.c
@@ -143,8 +143,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event)
if (nbr_fsm_tbl[i].state == -1) {
/* event outside of the defined fsm, ignore it. */
- log_warnx("%s: lsr-id %s, event %s not expected in "
- "state %s", __func__, inet_ntoa(nbr->id),
+ log_warnx("%s: lsr-id %s, event %s not expected in state %s", __func__, inet_ntoa(nbr->id),
nbr_event_names[event], nbr_state_name(old_state));
return (0);
}
@@ -153,8 +152,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event)
nbr->state = new_state;
if (old_state != nbr->state) {
- log_debug("%s: event %s resulted in action %s and "
- "changing state for lsr-id %s from %s to %s",
+ log_debug("%s: event %s resulted in action %s and changing state for lsr-id %s from %s to %s",
__func__, nbr_event_names[event],
nbr_action_names[nbr_fsm_tbl[i].action],
inet_ntoa(nbr->id), nbr_state_name(old_state),
diff --git a/ldpd/notification.c b/ldpd/notification.c
index 6de26107e6..93be9d3cb0 100644
--- a/ldpd/notification.c
+++ b/ldpd/notification.c
@@ -309,8 +309,7 @@ void
log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
{
if (nm->status_code & STATUS_FATAL) {
- debug_msg(out, "notification: lsr-id %s, status %s "
- "(fatal error)", inet_ntoa(nbr->id),
+ debug_msg(out, "notification: lsr-id %s, status %s (fatal error)", inet_ntoa(nbr->id),
status_code_name(nm->status_code));
return;
}
diff --git a/ldpd/packet.c b/ldpd/packet.c
index dfab30eeb3..c00008d120 100644
--- a/ldpd/packet.c
+++ b/ldpd/packet.c
@@ -77,8 +77,7 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia,
if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) {
/* set outgoing interface for multicast traffic */
if (sock_set_ipv4_mcast(ia->iface) == -1) {
- log_debug("%s: error setting multicast "
- "interface, %s", __func__, ia->iface->name);
+ log_debug("%s: error setting multicast interface, %s", __func__, ia->iface->name);
return (-1);
}
}
@@ -87,8 +86,7 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia,
if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) {
/* set outgoing interface for multicast traffic */
if (sock_set_ipv6_mcast(ia->iface) == -1) {
- log_debug("%s: error setting multicast "
- "interface, %s", __func__, ia->iface->name);
+ log_debug("%s: error setting multicast interface, %s", __func__, ia->iface->name);
return (-1);
}
}
@@ -368,8 +366,7 @@ session_accept(struct thread *thread)
return (0);
}
if (nbr->state != NBR_STA_PRESENT) {
- log_debug("%s: lsr-id %s: rejecting additional transport "
- "connection", __func__, inet_ntoa(nbr->id));
+ log_debug("%s: lsr-id %s: rejecting additional transport connection", __func__, inet_ntoa(nbr->id));
close(newfd);
return (0);
}
diff --git a/lib/bfd.c b/lib/bfd.c
index 7c84648d91..d1a0ec671e 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -94,7 +94,8 @@ int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str,
* bfd_set_param - Set the configured BFD paramter values
*/
void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
- uint8_t detect_mult, int defaults, int *command)
+ uint8_t detect_mult, const char *profile, int defaults,
+ int *command)
{
if (!*bfd_info) {
*bfd_info = bfd_info_create();
@@ -102,7 +103,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
} else {
if (((*bfd_info)->required_min_rx != min_rx)
|| ((*bfd_info)->desired_min_tx != min_tx)
- || ((*bfd_info)->detect_mult != detect_mult))
+ || ((*bfd_info)->detect_mult != detect_mult)
+ || (profile && strcmp((*bfd_info)->profile, profile)))
*command = ZEBRA_BFD_DEST_UPDATE;
}
@@ -110,6 +112,11 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
(*bfd_info)->required_min_rx = min_rx;
(*bfd_info)->desired_min_tx = min_tx;
(*bfd_info)->detect_mult = detect_mult;
+ if (profile)
+ strlcpy((*bfd_info)->profile, profile,
+ BFD_PROFILE_NAME_LEN);
+ else
+ (*bfd_info)->profile[0] = '\0';
}
if (!defaults)
@@ -121,6 +128,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
/*
* bfd_peer_sendmsg - Format and send a peer register/Unregister
* command to Zebra to be forwarded to BFD
+ *
+ * DEPRECATED: use zclient_bfd_command instead
*/
void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip, char *if_name,
@@ -143,8 +152,7 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
if (!zclient || zclient->sock < 0) {
if (bfd_debug)
zlog_debug(
- "%s: Can't send BFD peer register, Zebra client not "
- "established",
+ "%s: Can't send BFD peer register, Zebra client not established",
__func__);
return;
}
@@ -162,6 +170,11 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
args.min_rx = bfd_info->required_min_rx;
args.min_tx = bfd_info->desired_min_tx;
args.detection_multiplier = bfd_info->detect_mult;
+ if (bfd_info->profile[0]) {
+ args.profilelen = strlen(bfd_info->profile);
+ strlcpy(args.profile, bfd_info->profile,
+ sizeof(args.profile));
+ }
}
addrlen = family == AF_INET ? sizeof(struct in_addr)
@@ -217,8 +230,7 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
if (ifp == NULL) {
if (bfd_debug)
zlog_debug(
- "zebra_interface_bfd_read: "
- "Can't find interface by ifindex: %d ",
+ "zebra_interface_bfd_read: Can't find interface by ifindex: %d ",
ifindex);
return NULL;
}
@@ -320,8 +332,7 @@ void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag,
json_bfd);
} else {
vty_out(vty,
- " %s%sDetect Multiplier: %d, Min Rx interval: %d,"
- " Min Tx interval: %d\n",
+ " %s%sDetect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
(extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ",
bfd_info->detect_mult, bfd_info->required_min_rx,
bfd_info->desired_min_tx);
@@ -397,8 +408,7 @@ void bfd_client_sendmsg(struct zclient *zclient, int command,
if (!zclient || zclient->sock < 0) {
if (bfd_debug)
zlog_debug(
- "%s: Can't send BFD client register, Zebra client not "
- "established",
+ "%s: Can't send BFD client register, Zebra client not established",
__func__);
return;
}
@@ -429,6 +439,15 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
struct stream *s;
size_t addrlen;
+ /* Individual reg/dereg messages are suppressed during shutdown. */
+ if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) {
+ if (bfd_debug)
+ zlog_debug(
+ "%s: Suppressing BFD peer reg/dereg messages",
+ __func__);
+ return -1;
+ }
+
/* Check socket. */
if (!zc || zc->sock < 0) {
if (bfd_debug)
diff --git a/lib/bfd.h b/lib/bfd.h
index d7d4b5fe35..ceab4628b6 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -92,8 +92,8 @@ extern int bfd_validate_param(struct vty *vty, const char *dm_str,
uint32_t *tx_val);
extern void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx,
- uint32_t min_tx, uint8_t detect_mult, int defaults,
- int *command);
+ uint32_t min_tx, uint8_t detect_mult,
+ const char *profile, int defaults, int *command);
extern void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip,
char *if_name, int ttl, int multihop, int cbit,
diff --git a/lib/bitfield.h b/lib/bitfield.h
index 72980165f9..244938933b 100644
--- a/lib/bitfield.h
+++ b/lib/bitfield.h
@@ -58,7 +58,7 @@ typedef unsigned int word_t;
* @n: The current word number that is being used.
* @m: total number of words in 'data'
*/
-#define bitfield_t struct { word_t *data; size_t n, m; }
+typedef struct {word_t *data; size_t n, m; } bitfield_t;
/**
* Initialize the bits.
@@ -97,6 +97,16 @@ typedef unsigned int word_t;
#define bf_release_index(v, id) \
(v).data[bf_index(id)] &= ~(1 << (bf_offset(id)))
+/* check if an id is in use */
+#define bf_test_index(v, id) \
+ ((v).data[bf_index(id)] & (1 << (bf_offset(id))))
+
+/* check if the bit field has been setup */
+#define bf_is_inited(v) ((v).data)
+
+/* compare two bitmaps of the same length */
+#define bf_cmp(v1, v2) (memcmp((v1).data, (v2).data, ((v1).m * sizeof(word_t))))
+
/*
* return 0th index back to bitfield
*/
@@ -146,6 +156,37 @@ typedef unsigned int word_t;
(b) += (w * WORD_SIZE); \
} while (0)
+static inline unsigned int bf_find_next_set_bit(bitfield_t v,
+ word_t start_index)
+{
+ int start_bit;
+ unsigned long i, offset;
+
+ start_bit = start_index & (WORD_SIZE - 1);
+
+ for (i = bf_index(start_index); i < v.m; ++i) {
+ if (v.data[i] == 0) {
+ /* if the whole word is empty move to the next */
+ start_bit = 0;
+ continue;
+ }
+ /* scan one word for set bits */
+ for (offset = start_bit; offset < WORD_SIZE; ++offset) {
+ if ((v.data[i] >> offset) & 1)
+ return ((i * WORD_SIZE) + offset);
+ }
+ /* move to the next word */
+ start_bit = 0;
+ }
+ return WORD_MAX;
+}
+
+/* iterate through all the set bits */
+#define bf_for_each_set_bit(v, b, max) \
+ for ((b) = bf_find_next_set_bit((v), 0); \
+ (b) < max; \
+ (b) = bf_find_next_set_bit((v), (b) + 1))
+
/*
* Free the allocated memory for data
* @v: an instance of bitfield_t struct.
diff --git a/lib/buffer.c b/lib/buffer.c
index ff49bc83df..459d98e75d 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -327,8 +327,7 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
/* This should absolutely never occur. */
flog_err_sys(
EC_LIB_SYSTEM_CALL,
- "%s: corruption detected: iov_small overflowed; "
- "head %p, tail %p, head->next %p",
+ "%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,
diff --git a/lib/command.c b/lib/command.c
index fc43cce189..159ed07b38 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -841,9 +841,6 @@ enum node_type node_parent(enum node_type node)
case BFD_PROFILE_NODE:
ret = BFD_NODE;
break;
- case RPKI_VRF_NODE:
- ret = VRF_NODE;
- break;
default:
ret = CONFIG_NODE;
break;
@@ -907,6 +904,13 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
> vty->candidate_config->version)
nb_config_replace(vty->candidate_config,
running_config, true);
+
+ /*
+ * Perform pending commit (if any) before executing
+ * non-YANG command.
+ */
+ if (matched_element->attr != CMD_ATTR_YANG)
+ nb_cli_pending_commit_check(vty);
}
ret = matched_element->func(matched_element, vty, argc, argv);
diff --git a/lib/command.h b/lib/command.h
index 9e0fc783c7..e20bfe3318 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -159,7 +159,6 @@ enum node_type {
OPENFABRIC_NODE, /* OpenFabric router configuration node */
VRRP_NODE, /* VRRP node */
BMP_NODE, /* BMP config under router bgp */
- RPKI_VRF_NODE, /* RPKI node for VRF */
NODE_TYPE_MAX, /* maximum */
};
@@ -258,6 +257,12 @@ struct cmd_node {
#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
+#define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_YANG(funcname, cmdname, cmdstr, helpstr)
+
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
@@ -271,10 +276,16 @@ struct cmd_node {
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFUN_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN(funcname, cmdname, cmdstr, helpstr)
+#define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_YANG(funcname, cmdname, cmdstr, helpstr)
+
/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
@@ -283,6 +294,9 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \
daemon)
+#define DEFSH_YANG(daemon, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, daemon)
+
/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
@@ -303,6 +317,9 @@ struct cmd_node {
DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED)
+#define DEFUNSH_YANG(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
/* ALIAS macro which define existing command's alias. */
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
@@ -318,6 +335,9 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED, 0)
+#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, 0)
+
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
@@ -329,18 +349,6 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED, daemon)
-#else /* VTYSH_EXTRACT_PL */
-#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
- DEFUN(funcname, cmdname, cmdstr, helpstr)
-
-#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr)
-
-#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr)
-
-#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr)
#endif /* VTYSH_EXTRACT_PL */
/* Some macroes */
@@ -359,6 +367,8 @@ struct cmd_node {
#define SHOW_STR "Show running system information\n"
#define IP_STR "IP information\n"
#define IPV6_STR "IPv6 information\n"
+#define SRTE_STR "SR-TE information\n"
+#define SRTE_COLOR_STR "SR-TE Color 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"
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 1efe8b1803..179e104a57 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -74,6 +74,7 @@ enum cmd_token_type {
enum { CMD_ATTR_NORMAL,
CMD_ATTR_DEPRECATED,
CMD_ATTR_HIDDEN,
+ CMD_ATTR_YANG,
};
/* Comamand token struct. */
diff --git a/lib/defun_lex.l b/lib/defun_lex.l
index 19b06f51b8..bc5fbd24d9 100644
--- a/lib/defun_lex.l
+++ b/lib/defun_lex.l
@@ -140,6 +140,8 @@ SPECIAL [(),]
"DEFPY_NOSH" value = strdup(yytext); return DEFUNNY;
"DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
"DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
+"DEFPY_YANG" value = strdup(yytext); return DEFUNNY;
+"DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY;
"ALIAS" value = strdup(yytext); return DEFUNNY;
"ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
"install_element" value = strdup(yytext); return INSTALL;
diff --git a/lib/ferr.c b/lib/ferr.c
index ccf63dea17..7b923da177 100644
--- a/lib/ferr.c
+++ b/lib/ferr.c
@@ -130,7 +130,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json)
if (json) {
char key[11];
- snprintf(key, sizeof(key), "%"PRIu32, ref->code);
+ snprintf(key, sizeof(key), "%u", ref->code);
obj = json_object_new_object();
json_object_string_add(obj, "title", ref->title);
json_object_string_add(obj, "description",
@@ -142,7 +142,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json)
char pbuf[256];
char ubuf[256];
- snprintf(pbuf, sizeof(pbuf), "\nError %"PRIu32" - %s",
+ snprintf(pbuf, sizeof(pbuf), "\nError %u - %s",
ref->code, ref->title);
memset(ubuf, '=', strlen(pbuf));
ubuf[strlen(pbuf)] = '\0';
diff --git a/lib/filter.h b/lib/filter.h
index 76e992bf8e..d41f3b65cd 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -170,11 +170,6 @@ enum yang_prefix_list_action {
struct lyd_node;
struct vty;
-extern void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void access_list_legacy_remark_show(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
extern void access_list_show(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode,
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index fe8190d098..8c7a515dc5 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -169,7 +169,7 @@ static long acl_get_seq(struct vty *vty, const char *xpath)
/*
* Cisco (legacy) access lists.
*/
-DEFPY(
+DEFPY_YANG(
access_list_std, access_list_std_cmd,
"access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
ACCESS_LIST_STR
@@ -193,7 +193,8 @@ DEFPY(
* none given (backward compatibility).
*/
snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']",
+ number_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (seq_str == NULL) {
/* Use XPath to find the next sequence number. */
@@ -213,13 +214,13 @@ DEFPY(
concat_addr_mask_v4(host_str, mask_str, ipmask, sizeof(ipmask));
nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
} else {
- nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL);
}
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_std, no_access_list_std_cmd,
"no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
NO_STR
@@ -244,7 +245,7 @@ DEFPY(
if (seq_str != NULL) {
snprintf(
xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']",
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
number_str, seq_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
@@ -252,7 +253,8 @@ DEFPY(
/* Otherwise, to keep compatibility, we need to figure it out. */
snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']",
+ number_str);
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
@@ -278,7 +280,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
access_list_ext, access_list_ext_cmd,
"access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
ACCESS_LIST_STR
@@ -308,7 +310,8 @@ DEFPY(
* none given (backward compatibility).
*/
snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']",
+ number_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (seq_str == NULL) {
/* Use XPath to find the next sequence number. */
@@ -329,7 +332,7 @@ DEFPY(
sizeof(ipmask));
nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
} else {
- nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL);
}
if (dst_str != NULL && dst_mask_str == NULL) {
@@ -348,7 +351,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_ext, no_access_list_ext_cmd,
"no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
NO_STR
@@ -379,7 +382,7 @@ DEFPY(
if (seq_str != NULL) {
snprintfrr(
xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']",
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
number_str, seq_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
@@ -387,7 +390,8 @@ DEFPY(
/* Otherwise, to keep compatibility, we need to figure it out. */
snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']",
+ number_str);
/* Access-list must exist before entries. */
if (yang_dnode_exists(running_config->dnode, xpath) == false)
@@ -429,131 +433,10 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
- no_access_list_legacy, no_access_list_legacy_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number",
- NO_STR
- ACCESS_LIST_STR
- ACCESS_LIST_XLEG_STR)
-{
- char xpath[XPATH_MAXLEN];
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- return nb_cli_apply_changes(vty, NULL);
-}
-
-void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults)
-{
- uint16_t number = yang_dnode_get_uint16(dnode, "../number");
- bool extended;
- struct prefix p;
- struct in_addr mask;
-
- vty_out(vty, "access-list %d seq %s %s", number,
- yang_dnode_get_string(dnode, "./sequence"),
- yang_dnode_get_string(dnode, "./action"));
-
- extended = (number >= 100 && number <= 199)
- || (number >= 2000 && number <= 2699);
- if (extended)
- vty_out(vty, " ip");
-
- if (yang_dnode_exists(dnode, "./network")) {
- yang_dnode_get_prefix(&p, dnode, "./network");
- masklen2ip(p.prefixlen, &mask);
- vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
- } else if (yang_dnode_exists(dnode, "./host")) {
- if (extended)
- vty_out(vty, " host");
-
- vty_out(vty, " %s", yang_dnode_get_string(dnode, "./host"));
- } else if (yang_dnode_exists(dnode, "./any"))
- vty_out(vty, " any");
-
- if (extended) {
- if (yang_dnode_exists(dnode, "./destination-network")) {
- yang_dnode_get_prefix(&p, dnode,
- "./destination-network");
- masklen2ip(p.prefixlen, &mask);
- vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
- } else if (yang_dnode_exists(dnode, "./destination-host"))
- vty_out(vty, " host %s",
- yang_dnode_get_string(dnode,
- "./destination-host"));
- else if (yang_dnode_exists(dnode, "./destination-any"))
- vty_out(vty, " any");
- }
-
- vty_out(vty, "\n");
-}
-
-DEFPY(
- access_list_legacy_remark, access_list_legacy_remark_cmd,
- "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...",
- ACCESS_LIST_STR
- ACCESS_LIST_XLEG_STR
- ACCESS_LIST_REMARK_STR
- ACCESS_LIST_REMARK_LINE_STR)
-{
- int rv;
- char *remark;
- char xpath[XPATH_MAXLEN];
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
- nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
-
- remark = argv_concat(argv, argc, 3);
- nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
- rv = nb_cli_apply_changes(vty, xpath);
- XFREE(MTYPE_TMP, remark);
-
- return rv;
-}
-
-DEFPY(
- no_access_list_legacy_remark, no_access_list_legacy_remark_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark",
- NO_STR
- ACCESS_LIST_STR
- ACCESS_LIST_XLEG_STR
- ACCESS_LIST_REMARK_STR)
-{
- char xpath[XPATH_MAXLEN];
-
- snprintf(xpath, sizeof(xpath),
- "/frr-filter:lib/access-list-legacy[number='%s']/remark",
- number_str);
- nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
-
- return nb_cli_apply_changes(vty, NULL);
-}
-
-ALIAS(
- no_access_list_legacy_remark, no_access_list_legacy_remark_line_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...",
- NO_STR
- ACCESS_LIST_STR
- ACCESS_LIST_XLEG_STR
- ACCESS_LIST_REMARK_STR
- ACCESS_LIST_REMARK_LINE_STR)
-
-void access_list_legacy_remark_show(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults)
-{
- vty_out(vty, "access-list %s remark %s\n",
- yang_dnode_get_string(dnode, "../number"),
- yang_dnode_get_string(dnode, NULL));
-}
-
/*
* Zebra access lists.
*/
-DEFPY(
+DEFPY_YANG(
access_list, access_list_cmd,
"access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
ACCESS_LIST_STR
@@ -599,7 +482,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list, no_access_list_cmd,
"no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
NO_STR
@@ -656,7 +539,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_all, no_access_list_all_cmd,
"no access-list WORD$name",
NO_STR
@@ -672,7 +555,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
access_list_remark, access_list_remark_cmd,
"access-list WORD$name remark LINE...",
ACCESS_LIST_STR
@@ -696,7 +579,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_access_list_remark, no_access_list_remark_cmd,
"no access-list WORD$name remark",
NO_STR
@@ -723,7 +606,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
ipv6_access_list, ipv6_access_list_cmd,
"ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
IPV6_STR
@@ -770,7 +653,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list, no_ipv6_access_list_cmd,
"no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
NO_STR
@@ -828,7 +711,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list_all, no_ipv6_access_list_all_cmd,
"no ipv6 access-list WORD$name",
NO_STR
@@ -845,7 +728,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ipv6_access_list_remark, ipv6_access_list_remark_cmd,
"ipv6 access-list WORD$name remark LINE...",
IPV6_STR
@@ -870,7 +753,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd,
"no ipv6 access-list WORD$name remark",
NO_STR
@@ -899,7 +782,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
mac_access_list, mac_access_list_cmd,
"mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>",
MAC_STR
@@ -942,7 +825,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list, no_mac_access_list_cmd,
"no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>",
NO_STR
@@ -999,7 +882,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list_all, no_mac_access_list_all_cmd,
"no mac access-list WORD$name",
NO_STR
@@ -1016,7 +899,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
mac_access_list_remark, mac_access_list_remark_cmd,
"mac access-list WORD$name remark LINE...",
MAC_STR
@@ -1041,7 +924,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list_remark, no_mac_access_list_remark_cmd,
"no mac access-list WORD$name remark",
NO_STR
@@ -1077,6 +960,9 @@ void access_list_show(struct vty *vty, struct lyd_node *dnode,
struct prefix p;
bool is_any;
bool is_exact = false;
+ bool cisco_style = false;
+ bool cisco_extended = false;
+ struct in_addr mask;
char macstr[PREFIX2STR_BUFFER];
is_any = yang_dnode_exists(dnode, "./any");
@@ -1085,8 +971,19 @@ void access_list_show(struct vty *vty, struct lyd_node *dnode,
if (is_any)
break;
- yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
- is_exact = yang_dnode_get_bool(dnode, "./ipv4-exact-match");
+ if (yang_dnode_exists(dnode, "./host")
+ || yang_dnode_exists(dnode, "./network")
+ || yang_dnode_exists(dnode, "./source-any")) {
+ cisco_style = true;
+ if (yang_dnode_exists(dnode, "./destination-host")
+ || yang_dnode_exists(dnode, "./destination-network")
+ || yang_dnode_exists(dnode, "./destination-any"))
+ cisco_extended = true;
+ } else {
+ yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
+ is_exact = yang_dnode_get_bool(dnode,
+ "./ipv4-exact-match");
+ }
break;
case YALT_IPV6: /* ipv6 */
vty_out(vty, "ipv6 ");
@@ -1110,6 +1007,48 @@ void access_list_show(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(dnode, "./sequence"),
yang_dnode_get_string(dnode, "./action"));
+ /* Handle Cisco style access lists. */
+ if (cisco_style) {
+ if (cisco_extended)
+ vty_out(vty, " ip");
+
+ if (yang_dnode_exists(dnode, "./network")) {
+ yang_dnode_get_prefix(&p, dnode, "./network");
+ masklen2ip(p.prefixlen, &mask);
+ vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
+ } else if (yang_dnode_exists(dnode, "./host")) {
+ if (cisco_extended)
+ vty_out(vty, " host");
+
+ vty_out(vty, " %s",
+ yang_dnode_get_string(dnode, "./host"));
+ } else if (yang_dnode_exists(dnode, "./source-any"))
+ vty_out(vty, " any");
+
+ /* Not extended, exit earlier. */
+ if (!cisco_extended) {
+ vty_out(vty, "\n");
+ return;
+ }
+
+ /* Handle destination address. */
+ if (yang_dnode_exists(dnode, "./destination-network")) {
+ yang_dnode_get_prefix(&p, dnode,
+ "./destination-network");
+ masklen2ip(p.prefixlen, &mask);
+ vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
+ } else if (yang_dnode_exists(dnode, "./destination-host"))
+ vty_out(vty, " host %s",
+ yang_dnode_get_string(dnode,
+ "./destination-host"));
+ else if (yang_dnode_exists(dnode, "./destination-any"))
+ vty_out(vty, " any");
+
+ vty_out(vty, "\n");
+ return;
+ }
+
+ /* Zebra style access list. */
if (!is_any) {
/* If type is MAC don't show '/mask'. */
if (type == 2 /* mac */) {
@@ -1243,7 +1182,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name,
return rv;
}
-DEFPY(
+DEFPY_YANG(
ip_prefix_list, ip_prefix_list_cmd,
"ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
IP_STR
@@ -1300,7 +1239,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list, no_ip_prefix_list_cmd,
"no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
NO_STR
@@ -1320,7 +1259,7 @@ DEFPY(
(struct prefix *)prefix, ge, le);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
"no ip prefix-list WORD$name seq (1-4294967295)$seq",
NO_STR
@@ -1332,7 +1271,7 @@ DEFPY(
return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_all, no_ip_prefix_list_all_cmd,
"no ip prefix-list WORD$name",
NO_STR
@@ -1349,7 +1288,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ip_prefix_list_remark, ip_prefix_list_remark_cmd,
"ip prefix-list WORD$name description LINE...",
IP_STR
@@ -1374,7 +1313,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd,
"no ip prefix-list WORD$name description",
NO_STR
@@ -1403,7 +1342,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
ipv6_prefix_list, ipv6_prefix_list_cmd,
"ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
IPV6_STR
@@ -1460,7 +1399,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list, no_ipv6_prefix_list_cmd,
"no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
NO_STR
@@ -1480,7 +1419,7 @@ DEFPY(
(struct prefix *)prefix, ge, le);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd,
"no ipv6 prefix-list WORD$name seq (1-4294967295)$seq",
NO_STR
@@ -1492,7 +1431,7 @@ DEFPY(
return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd,
"no ipv6 prefix-list WORD$name",
NO_STR
@@ -1509,7 +1448,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd,
"ipv6 prefix-list WORD$name description LINE...",
IPV6_STR
@@ -1534,7 +1473,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd,
"no ipv6 prefix-list WORD$name description",
NO_STR
@@ -1648,10 +1587,6 @@ void filter_cli_init(void)
install_element(CONFIG_NODE, &no_access_list_std_cmd);
install_element(CONFIG_NODE, &access_list_ext_cmd);
install_element(CONFIG_NODE, &no_access_list_ext_cmd);
- install_element(CONFIG_NODE, &no_access_list_legacy_cmd);
- install_element(CONFIG_NODE, &access_list_legacy_remark_cmd);
- install_element(CONFIG_NODE, &no_access_list_legacy_remark_cmd);
- install_element(CONFIG_NODE, &no_access_list_legacy_remark_line_cmd);
/* access-list zebra-style. */
install_element(CONFIG_NODE, &access_list_cmd);
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index 83cf586ed3..91691d2f1d 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -24,6 +24,7 @@
#include "lib/northbound.h"
#include "lib/prefix.h"
+#include "lib/printfrr.h"
#include "lib/filter.h"
#include "lib/plist.h"
@@ -39,20 +40,19 @@ ipv4_network_addr(in_addr_t hostaddr, int masklen)
return hostaddr & mask.s_addr;
}
-static enum nb_error
-prefix_list_length_validate(const struct lyd_node *dnode)
+static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args)
{
- int type = yang_dnode_get_enum(dnode, "../../type");
+ int type = yang_dnode_get_enum(args->dnode, "../../type");
const char *xpath_le = NULL, *xpath_ge = NULL;
struct prefix p;
uint8_t le, ge;
if (type == YPLT_IPV4) {
- yang_dnode_get_prefix(&p, dnode, "../ipv4-prefix");
+ yang_dnode_get_prefix(&p, args->dnode, "../ipv4-prefix");
xpath_le = "../ipv4-prefix-length-lesser-or-equal";
xpath_ge = "../ipv4-prefix-length-greater-or-equal";
} else {
- yang_dnode_get_prefix(&p, dnode, "../ipv6-prefix");
+ yang_dnode_get_prefix(&p, args->dnode, "../ipv6-prefix");
xpath_le = "../ipv6-prefix-length-lesser-or-equal";
xpath_ge = "../ipv6-prefix-length-greater-or-equal";
}
@@ -61,19 +61,18 @@ prefix_list_length_validate(const struct lyd_node *dnode)
* Check rule:
* prefix length <= le.
*/
- if (yang_dnode_exists(dnode, xpath_le)) {
- le = yang_dnode_get_uint8(dnode, xpath_le);
+ if (yang_dnode_exists(args->dnode, xpath_le)) {
+ le = yang_dnode_get_uint8(args->dnode, xpath_le);
if (p.prefixlen > le)
goto log_and_fail;
-
}
/*
* Check rule:
* prefix length < ge.
*/
- if (yang_dnode_exists(dnode, xpath_ge)) {
- ge = yang_dnode_get_uint8(dnode, xpath_ge);
+ if (yang_dnode_exists(args->dnode, xpath_ge)) {
+ ge = yang_dnode_get_uint8(args->dnode, xpath_ge);
if (p.prefixlen >= ge)
goto log_and_fail;
}
@@ -82,19 +81,21 @@ prefix_list_length_validate(const struct lyd_node *dnode)
* Check rule:
* ge <= le.
*/
- if (yang_dnode_exists(dnode, xpath_le) &&
- yang_dnode_exists(dnode, xpath_ge)) {
- le = yang_dnode_get_uint8(dnode, xpath_le);
- ge = yang_dnode_get_uint8(dnode, xpath_ge);
+ if (yang_dnode_exists(args->dnode, xpath_le)
+ && yang_dnode_exists(args->dnode, xpath_ge)) {
+ le = yang_dnode_get_uint8(args->dnode, xpath_le);
+ ge = yang_dnode_get_uint8(args->dnode, xpath_ge);
if (ge > le)
goto log_and_fail;
}
return NB_OK;
- log_and_fail:
- zlog_info("prefix-list: invalid prefix range for %pFX: "
- "Make sure that mask length < ge <= le", &p);
+log_and_fail:
+ snprintfrr(
+ args->errmsg, args->errmsg_len,
+ "Invalid prefix range for %pFX: Make sure that mask length < ge <= le",
+ &p);
return NB_ERR_VALIDATION;
}
@@ -112,24 +113,38 @@ static void prefix_list_entry_set_empty(struct prefix_list_entry *ple)
}
/*
- * XPath: /frr-filter:lib/access-list-legacy
+ * XPath: /frr-filter:lib/access-list
*/
-static int lib_access_list_legacy_create(struct nb_cb_create_args *args)
+static int lib_access_list_create(struct nb_cb_create_args *args)
{
- struct access_list *acl;
+ struct access_list *acl = NULL;
const char *acl_name;
+ int type;
if (args->event != NB_EV_APPLY)
return NB_OK;
- acl_name = yang_dnode_get_string(args->dnode, "./number");
- acl = access_list_get(AFI_IP, acl_name);
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ acl_name = yang_dnode_get_string(args->dnode, "./name");
+
+ switch (type) {
+ case YALT_IPV4:
+ acl = access_list_get(AFI_IP, acl_name);
+ break;
+ case YALT_IPV6:
+ acl = access_list_get(AFI_IP6, acl_name);
+ break;
+ case YALT_MAC:
+ acl = access_list_get(AFI_L2VPN, acl_name);
+ break;
+ }
+
nb_running_set_entry(args->dnode, acl);
return NB_OK;
}
-static int lib_access_list_legacy_destroy(struct nb_cb_destroy_args *args)
+static int lib_access_list_destroy(struct nb_cb_destroy_args *args)
{
struct access_master *am;
struct access_list *acl;
@@ -148,9 +163,9 @@ static int lib_access_list_legacy_destroy(struct nb_cb_destroy_args *args)
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/remark
+ * XPath: /frr-filter:lib/access-list/remark
*/
-static int lib_access_list_legacy_remark_modify(struct nb_cb_modify_args *args)
+static int lib_access_list_remark_modify(struct nb_cb_modify_args *args)
{
struct access_list *acl;
const char *remark;
@@ -169,7 +184,7 @@ static int lib_access_list_legacy_remark_modify(struct nb_cb_modify_args *args)
}
static int
-lib_access_list_legacy_remark_destroy(struct nb_cb_destroy_args *args)
+lib_access_list_remark_destroy(struct nb_cb_destroy_args *args)
{
struct access_list *acl;
@@ -183,31 +198,20 @@ lib_access_list_legacy_remark_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry
+ * XPath: /frr-filter:lib/access-list/entry
*/
-static int lib_access_list_legacy_entry_create(struct nb_cb_create_args *args)
+static int lib_access_list_entry_create(struct nb_cb_create_args *args)
{
- struct filter_cisco *fc;
struct access_list *acl;
struct filter *f;
- uint32_t aclno;
-
- /* TODO: validate `filter_lookup_cisco` returns NULL. */
if (args->event != NB_EV_APPLY)
return NB_OK;
- aclno = yang_dnode_get_uint16(args->dnode, "../number");
-
f = filter_new();
- f->cisco = 1;
f->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
- fc = &f->u.cfilter;
- if ((aclno >= 1 && aclno <= 99) || (aclno >= 1300 && aclno <= 1999))
- fc->extended = 0;
- else
- fc->extended = 1;
acl = nb_running_get_entry(args->dnode, NULL, true);
f->acl = acl;
@@ -217,7 +221,7 @@ static int lib_access_list_legacy_entry_create(struct nb_cb_create_args *args)
return NB_OK;
}
-static int lib_access_list_legacy_entry_destroy(struct nb_cb_destroy_args *args)
+static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args)
{
struct access_list *acl;
struct filter *f;
@@ -233,10 +237,10 @@ static int lib_access_list_legacy_entry_destroy(struct nb_cb_destroy_args *args)
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/action
+ * XPath: /frr-filter:lib/access-list/entry/action
*/
static int
-lib_access_list_legacy_entry_action_modify(struct nb_cb_modify_args *args)
+lib_access_list_entry_action_modify(struct nb_cb_modify_args *args)
{
const char *filter_type;
struct filter *f;
@@ -255,86 +259,81 @@ lib_access_list_legacy_entry_action_modify(struct nb_cb_modify_args *args)
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/host
+ * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix
*/
static int
-lib_access_list_legacy_entry_host_modify(struct nb_cb_modify_args *args)
+lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
{
- struct filter_cisco *fc;
+ struct filter_zebra *fz;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fc = &f->u.cfilter;
- yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
- fc->addr_mask.s_addr = INADDR_ANY;
+ f->cisco = 0;
+ fz = &f->u.zfilter;
+ yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL);
return NB_OK;
}
static int
-lib_access_list_legacy_entry_host_destroy(struct nb_cb_destroy_args *args)
+lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
{
- struct filter_cisco *fc;
+ struct filter_zebra *fz;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fc = &f->u.cfilter;
- fc->addr.s_addr = INADDR_ANY;
- fc->addr_mask.s_addr = INADDR_NONE;
+ fz = &f->u.zfilter;
+ memset(&fz->prefix, 0, sizeof(fz->prefix));
return NB_OK;
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/network
+ * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match
*/
static int
-lib_access_list_legacy_entry_network_modify(struct nb_cb_modify_args *args)
+lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
{
- struct filter_cisco *fc;
+ struct filter_zebra *fz;
struct filter *f;
- struct prefix p;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fc = &f->u.cfilter;
- yang_dnode_get_prefix(&p, args->dnode, NULL);
- fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
- masklen2ip(p.prefixlen, &fc->addr_mask);
+ fz = &f->u.zfilter;
+ fz->exact = yang_dnode_get_bool(args->dnode, NULL);
return NB_OK;
}
static int
-lib_access_list_legacy_entry_network_destroy(struct nb_cb_destroy_args *args)
+lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args)
{
- struct filter_cisco *fc;
+ struct filter_zebra *fz;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fc = &f->u.cfilter;
- fc->addr.s_addr = INADDR_ANY;
- fc->addr_mask.s_addr = INADDR_NONE;
+ fz = &f->u.zfilter;
+ fz->exact = 0;
return NB_OK;
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/any
+ * XPath: /frr-filter:lib/access-list/entry/host
*/
static int
-lib_access_list_legacy_entry_any_create(struct nb_cb_create_args *args)
+lib_access_list_entry_host_modify(struct nb_cb_modify_args *args)
{
struct filter_cisco *fc;
struct filter *f;
@@ -343,15 +342,16 @@ lib_access_list_legacy_entry_any_create(struct nb_cb_create_args *args)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
+ f->cisco = 1;
fc = &f->u.cfilter;
- fc->addr.s_addr = INADDR_ANY;
- fc->addr_mask.s_addr = INADDR_NONE;
+ yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
+ fc->addr_mask.s_addr = INADDR_ANY;
return NB_OK;
}
static int
-lib_access_list_legacy_entry_any_destroy(struct nb_cb_destroy_args *args)
+lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args)
{
struct filter_cisco *fc;
struct filter *f;
@@ -368,27 +368,30 @@ lib_access_list_legacy_entry_any_destroy(struct nb_cb_destroy_args *args)
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/destination-host
+ * XPath: /frr-filter:lib/access-list/entry/network
*/
-static int lib_access_list_legacy_entry_destination_host_modify(
- struct nb_cb_modify_args *args)
+static int
+lib_access_list_entry_network_modify(struct nb_cb_modify_args *args)
{
struct filter_cisco *fc;
struct filter *f;
+ struct prefix p;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
+ f->cisco = 1;
fc = &f->u.cfilter;
- yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
- fc->mask_mask.s_addr = INADDR_ANY;
+ yang_dnode_get_prefix(&p, args->dnode, NULL);
+ fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
+ masklen2ip(p.prefixlen, &fc->addr_mask);
return NB_OK;
}
-static int lib_access_list_legacy_entry_destination_host_destroy(
- struct nb_cb_destroy_args *args)
+static int
+lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args)
{
struct filter_cisco *fc;
struct filter *f;
@@ -398,36 +401,35 @@ static int lib_access_list_legacy_entry_destination_host_destroy(
f = nb_running_get_entry(args->dnode, NULL, true);
fc = &f->u.cfilter;
- fc->mask.s_addr = INADDR_ANY;
- fc->mask_mask.s_addr = INADDR_NONE;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
return NB_OK;
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/destination-network
+ * XPath: /frr-filter:lib/access-list/entry/source-any
*/
-static int lib_access_list_legacy_entry_destination_network_modify(
- struct nb_cb_modify_args *args)
+static int
+lib_access_list_entry_source_any_create(struct nb_cb_create_args *args)
{
struct filter_cisco *fc;
struct filter *f;
- struct prefix p;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
+ f->cisco = 1;
fc = &f->u.cfilter;
- yang_dnode_get_prefix(&p, args->dnode, NULL);
- fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
- masklen2ip(p.prefixlen, &fc->mask_mask);
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
return NB_OK;
}
-static int lib_access_list_legacy_entry_destination_network_destroy(
- struct nb_cb_destroy_args *args)
+static int
+lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args)
{
struct filter_cisco *fc;
struct filter *f;
@@ -437,17 +439,17 @@ static int lib_access_list_legacy_entry_destination_network_destroy(
f = nb_running_get_entry(args->dnode, NULL, true);
fc = &f->u.cfilter;
- fc->mask.s_addr = INADDR_ANY;
- fc->mask_mask.s_addr = INADDR_NONE;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
return NB_OK;
}
/*
- * XPath: /frr-filter:lib/access-list-legacy/entry/destination-any
+ * XPath: /frr-filter:lib/access-list/entry/destination-host
*/
-static int lib_access_list_legacy_entry_destination_any_create(
- struct nb_cb_create_args *args)
+static int lib_access_list_entry_destination_host_modify(
+ struct nb_cb_modify_args *args)
{
struct filter_cisco *fc;
struct filter *f;
@@ -457,13 +459,14 @@ static int lib_access_list_legacy_entry_destination_any_create(
f = nb_running_get_entry(args->dnode, NULL, true);
fc = &f->u.cfilter;
- fc->mask.s_addr = INADDR_ANY;
- fc->mask_mask.s_addr = INADDR_NONE;
+ fc->extended = 1;
+ yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
+ fc->mask_mask.s_addr = INADDR_ANY;
return NB_OK;
}
-static int lib_access_list_legacy_entry_destination_any_destroy(
+static int lib_access_list_entry_destination_host_destroy(
struct nb_cb_destroy_args *args)
{
struct filter_cisco *fc;
@@ -474,6 +477,7 @@ static int lib_access_list_legacy_entry_destination_any_destroy(
f = nb_running_get_entry(args->dnode, NULL, true);
fc = &f->u.cfilter;
+ fc->extended = 0;
fc->mask.s_addr = INADDR_ANY;
fc->mask_mask.s_addr = INADDR_NONE;
@@ -481,160 +485,81 @@ static int lib_access_list_legacy_entry_destination_any_destroy(
}
/*
- * XPath: /frr-filter:lib/access-list
+ * XPath: /frr-filter:lib/access-list/entry/destination-network
*/
-static int lib_access_list_create(struct nb_cb_create_args *args)
-{
- struct access_list *acl = NULL;
- const char *acl_name;
- int type;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- type = yang_dnode_get_enum(args->dnode, "./type");
- acl_name = yang_dnode_get_string(args->dnode, "./name");
-
- switch (type) {
- case YALT_IPV4:
- acl = access_list_get(AFI_IP, acl_name);
- break;
- case YALT_IPV6:
- acl = access_list_get(AFI_IP6, acl_name);
- break;
- case YALT_MAC:
- acl = access_list_get(AFI_L2VPN, acl_name);
- break;
- }
-
- nb_running_set_entry(args->dnode, acl);
-
- return NB_OK;
-}
-
-static int lib_access_list_destroy(struct nb_cb_destroy_args *args)
-{
- struct access_master *am;
- struct access_list *acl;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- acl = nb_running_unset_entry(args->dnode);
- am = acl->master;
- if (am->delete_hook)
- am->delete_hook(acl);
-
- access_list_delete(acl);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-filter:lib/access-list/entry
- */
-static int lib_access_list_entry_create(struct nb_cb_create_args *args)
-{
- struct access_list *acl;
- struct filter *f;
-
- /* TODO: validate `filter_lookup_zebra` returns NULL. */
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- f = filter_new();
- f->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
-
- acl = nb_running_get_entry(args->dnode, NULL, true);
- f->acl = acl;
- access_list_filter_add(acl, f);
- nb_running_set_entry(args->dnode, f);
-
- return NB_OK;
-}
-
-static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args)
-{
- struct access_list *acl;
- struct filter *f;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- f = nb_running_unset_entry(args->dnode);
- acl = f->acl;
- access_list_filter_delete(acl, f);
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix
- */
-static int
-lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
+static int lib_access_list_entry_destination_network_modify(
+ struct nb_cb_modify_args *args)
{
- struct filter_zebra *fz;
+ struct filter_cisco *fc;
struct filter *f;
+ struct prefix p;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fz = &f->u.zfilter;
- yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL);
+ fc = &f->u.cfilter;
+ fc->extended = 1;
+ yang_dnode_get_prefix(&p, args->dnode, NULL);
+ fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
+ masklen2ip(p.prefixlen, &fc->mask_mask);
return NB_OK;
}
-static int
-lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
+static int lib_access_list_entry_destination_network_destroy(
+ struct nb_cb_destroy_args *args)
{
- struct filter_zebra *fz;
+ struct filter_cisco *fc;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fz = &f->u.zfilter;
- memset(&fz->prefix, 0, sizeof(fz->prefix));
+ fc = &f->u.cfilter;
+ fc->extended = 0;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
return NB_OK;
}
/*
- * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match
+ * XPath: /frr-filter:lib/access-list/entry/destination-any
*/
-static int
-lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
+static int lib_access_list_entry_destination_any_create(
+ struct nb_cb_create_args *args)
{
- struct filter_zebra *fz;
+ struct filter_cisco *fc;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fz = &f->u.zfilter;
- fz->exact = yang_dnode_get_bool(args->dnode, NULL);
+ fc = &f->u.cfilter;
+ fc->extended = 1;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
return NB_OK;
}
-static int
-lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args)
+static int lib_access_list_entry_destination_any_destroy(
+ struct nb_cb_destroy_args *args)
{
- struct filter_zebra *fz;
+ struct filter_cisco *fc;
struct filter *f;
if (args->event != NB_EV_APPLY)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
- fz = &f->u.zfilter;
- fz->exact = 0;
+ fc = &f->u.cfilter;
+ fc->extended = 0;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
return NB_OK;
}
@@ -652,6 +577,7 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
return NB_OK;
f = nb_running_get_entry(args->dnode, NULL, true);
+ f->cisco = 0;
fz = &f->u.zfilter;
memset(&fz->prefix, 0, sizeof(fz->prefix));
@@ -905,7 +831,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
struct prefix_list_entry *ple;
if (args->event == NB_EV_VALIDATE &&
- prefix_list_length_validate(args->dnode) != NB_OK)
+ prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
if (args->event != NB_EV_APPLY)
@@ -954,7 +880,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
struct prefix_list_entry *ple;
if (args->event == NB_EV_VALIDATE &&
- prefix_list_length_validate(args->dnode) != NB_OK)
+ prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
if (args->event != NB_EV_APPLY)
@@ -1060,117 +986,88 @@ const struct frr_yang_module_info frr_filter_info = {
.name = "frr-filter",
.nodes = {
{
- .xpath = "/frr-filter:lib/access-list-legacy",
- .cbs = {
- .create = lib_access_list_legacy_create,
- .destroy = lib_access_list_legacy_destroy,
- }
- },
- {
- .xpath = "/frr-filter:lib/access-list-legacy/remark",
- .cbs = {
- .modify = lib_access_list_legacy_remark_modify,
- .destroy = lib_access_list_legacy_remark_destroy,
- .cli_show = access_list_legacy_remark_show,
- }
- },
- {
- .xpath = "/frr-filter:lib/access-list-legacy/entry",
- .cbs = {
- .create = lib_access_list_legacy_entry_create,
- .destroy = lib_access_list_legacy_entry_destroy,
- .cli_show = access_list_legacy_show,
- }
- },
- {
- .xpath = "/frr-filter:lib/access-list-legacy/entry/action",
- .cbs = {
- .modify = lib_access_list_legacy_entry_action_modify,
- }
- },
- {
- .xpath = "/frr-filter:lib/access-list-legacy/entry/host",
+ .xpath = "/frr-filter:lib/access-list",
.cbs = {
- .modify = lib_access_list_legacy_entry_host_modify,
- .destroy = lib_access_list_legacy_entry_host_destroy,
+ .create = lib_access_list_create,
+ .destroy = lib_access_list_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list-legacy/entry/network",
+ .xpath = "/frr-filter:lib/access-list/remark",
.cbs = {
- .modify = lib_access_list_legacy_entry_network_modify,
- .destroy = lib_access_list_legacy_entry_network_destroy,
+ .modify = lib_access_list_remark_modify,
+ .destroy = lib_access_list_remark_destroy,
+ .cli_show = access_list_remark_show,
}
},
{
- .xpath = "/frr-filter:lib/access-list-legacy/entry/any",
+ .xpath = "/frr-filter:lib/access-list/entry",
.cbs = {
- .create = lib_access_list_legacy_entry_any_create,
- .destroy = lib_access_list_legacy_entry_any_destroy,
+ .create = lib_access_list_entry_create,
+ .destroy = lib_access_list_entry_destroy,
+ .cli_show = access_list_show,
}
},
{
- .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-host",
+ .xpath = "/frr-filter:lib/access-list/entry/action",
.cbs = {
- .modify = lib_access_list_legacy_entry_destination_host_modify,
- .destroy = lib_access_list_legacy_entry_destination_host_destroy,
+ .modify = lib_access_list_entry_action_modify,
}
},
{
- .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-network",
+ .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix",
.cbs = {
- .modify = lib_access_list_legacy_entry_destination_network_modify,
- .destroy = lib_access_list_legacy_entry_destination_network_destroy,
+ .modify = lib_access_list_entry_ipv4_prefix_modify,
+ .destroy = lib_access_list_entry_ipv4_prefix_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-any",
+ .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match",
.cbs = {
- .create = lib_access_list_legacy_entry_destination_any_create,
- .destroy = lib_access_list_legacy_entry_destination_any_destroy,
+ .modify = lib_access_list_entry_ipv4_exact_match_modify,
+ .destroy = lib_access_list_entry_ipv4_exact_match_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list",
+ .xpath = "/frr-filter:lib/access-list/entry/host",
.cbs = {
- .create = lib_access_list_create,
- .destroy = lib_access_list_destroy,
+ .modify = lib_access_list_entry_host_modify,
+ .destroy = lib_access_list_entry_host_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list/remark",
+ .xpath = "/frr-filter:lib/access-list/entry/network",
.cbs = {
- .modify = lib_access_list_legacy_remark_modify,
- .destroy = lib_access_list_legacy_remark_destroy,
- .cli_show = access_list_remark_show,
+ .modify = lib_access_list_entry_network_modify,
+ .destroy = lib_access_list_entry_network_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list/entry",
+ .xpath = "/frr-filter:lib/access-list/entry/source-any",
.cbs = {
- .create = lib_access_list_entry_create,
- .destroy = lib_access_list_entry_destroy,
- .cli_show = access_list_show,
+ .create = lib_access_list_entry_source_any_create,
+ .destroy = lib_access_list_entry_source_any_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list/entry/action",
+ .xpath = "/frr-filter:lib/access-list/entry/destination-host",
.cbs = {
- .modify = lib_access_list_legacy_entry_action_modify,
+ .modify = lib_access_list_entry_destination_host_modify,
+ .destroy = lib_access_list_entry_destination_host_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix",
+ .xpath = "/frr-filter:lib/access-list/entry/destination-network",
.cbs = {
- .modify = lib_access_list_entry_ipv4_prefix_modify,
- .destroy = lib_access_list_entry_ipv4_prefix_destroy,
+ .modify = lib_access_list_entry_destination_network_modify,
+ .destroy = lib_access_list_entry_destination_network_destroy,
}
},
{
- .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match",
+ .xpath = "/frr-filter:lib/access-list/entry/destination-any",
.cbs = {
- .modify = lib_access_list_entry_ipv4_exact_match_modify,
- .destroy = lib_access_list_entry_ipv4_exact_match_destroy,
+ .create = lib_access_list_entry_destination_any_create,
+ .destroy = lib_access_list_entry_destination_any_destroy,
}
},
{
diff --git a/lib/id_alloc.c b/lib/id_alloc.c
index 7c7f2c4689..95096fa5f0 100644
--- a/lib/id_alloc.c
+++ b/lib/id_alloc.c
@@ -108,7 +108,7 @@ static struct id_alloc_page *find_or_create_page(struct id_alloc *alloc,
} else if (page != NULL && create) {
flog_err(
EC_LIB_ID_CONSISTENCY,
- "ID Allocator %s attempt to re-create page at %" PRIu32,
+ "ID Allocator %s attempt to re-create page at %u",
alloc->name, id);
}
@@ -131,8 +131,7 @@ void idalloc_free(struct id_alloc *alloc, uint32_t id)
page = find_or_create_page(alloc, id, 0);
if (!page) {
flog_err(EC_LIB_ID_CONSISTENCY,
- "ID Allocator %s cannot free #%" PRIu32
- ". ID Block does not exist.",
+ "ID Allocator %s cannot free #%u. ID Block does not exist.",
alloc->name, id);
return;
}
@@ -142,8 +141,7 @@ void idalloc_free(struct id_alloc *alloc, uint32_t id)
if ((page->allocated_mask[word] & (1 << offset)) == 0) {
flog_err(EC_LIB_ID_CONSISTENCY,
- "ID Allocator %s cannot free #%" PRIu32
- ". ID was not allocated at the time of free.",
+ "ID Allocator %s cannot free #%u. ID was not allocated at the time of free.",
alloc->name, id);
return;
}
@@ -285,8 +283,7 @@ uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id)
if (page->allocated_mask[word] & (((uint32_t)1) << offset)) {
flog_err(EC_LIB_ID_CONSISTENCY,
- "ID Allocator %s could not reserve %" PRIu32
- " because it is already allocated.",
+ "ID Allocator %s could not reserve %u because it is already allocated.",
alloc->name, id);
return IDALLOC_INVALID;
}
diff --git a/lib/if.c b/lib/if.c
index e12b4f8c8f..d8392708e1 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -217,16 +217,14 @@ struct interface *if_create_name(const char *name, vrf_id_t vrf_id)
return ifp;
}
-struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
- char *optional_name)
+struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
{
struct interface *ifp;
ifp = if_new(vrf_id);
if_set_index(ifp, ifindex);
- if (optional_name)
- if_set_name(ifp, optional_name);
+
hook_call(if_add, ifp);
return ifp;
}
@@ -573,8 +571,7 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id)
return NULL;
}
-struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
- char *optional_name)
+struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
{
struct interface *ifp;
@@ -584,7 +581,7 @@ struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
ifp = if_lookup_by_ifindex(ifindex, vrf_id);
if (ifp)
return ifp;
- return if_create_ifindex(ifindex, vrf_id, optional_name);
+ return if_create_ifindex(ifindex, vrf_id);
case VRF_BACKEND_VRF_LITE:
ifp = if_lookup_by_index_all_vrf(ifindex);
if (ifp) {
@@ -596,7 +593,7 @@ struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
if_update_to_new_vrf(ifp, vrf_id);
return ifp;
}
- return if_create_ifindex(ifindex, vrf_id, optional_name);
+ return if_create_ifindex(ifindex, vrf_id);
}
return NULL;
@@ -785,8 +782,7 @@ static void if_dump(const struct interface *ifp)
struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id);
zlog_info(
- "Interface %s vrf %s(%u) index %d metric %d mtu %d "
- "mtu6 %d %s",
+ "Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s",
ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex,
ifp->metric, ifp->mtu, ifp->mtu6,
if_flag_dump(ifp->flags));
@@ -1322,7 +1318,7 @@ void if_link_params_free(struct interface *ifp)
/*
* XPath: /frr-interface:lib/interface
*/
-DEFPY_NOSH (interface,
+DEFPY_YANG_NOSH (interface,
interface_cmd,
"interface IFNAME [vrf NAME$vrf_name]",
"Select an interface to configure\n"
@@ -1385,6 +1381,7 @@ DEFPY_NOSH (interface,
* all interface-level commands are converted to the new
* northbound model.
*/
+ nb_cli_pending_commit_check(vty);
ifp = if_lookup_by_name(ifname, vrf_id);
if (ifp)
VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp);
@@ -1393,7 +1390,7 @@ DEFPY_NOSH (interface,
return ret;
}
-DEFPY (no_interface,
+DEFPY_YANG (no_interface,
no_interface_cmd,
"no interface IFNAME [vrf NAME$vrf_name]",
NO_STR
@@ -1428,7 +1425,7 @@ static void cli_show_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/description
*/
-DEFPY (interface_desc,
+DEFPY_YANG (interface_desc,
interface_desc_cmd,
"description LINE...",
"Interface specific description\n"
@@ -1445,7 +1442,7 @@ DEFPY (interface_desc,
return ret;
}
-DEFPY (no_interface_desc,
+DEFPY_YANG (no_interface_desc,
no_interface_desc_cmd,
"no description",
NO_STR
diff --git a/lib/if.h b/lib/if.h
index 40e87c1e31..1fb0757db2 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -509,8 +509,7 @@ extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id);
extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id);
/* Create new interface, adds to index list only */
-extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
- char *name);
+extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index_all_vrf(ifindex_t);
extern struct interface *if_lookup_exact_address(const void *matchaddr,
@@ -527,8 +526,8 @@ extern struct interface *if_lookup_by_name_all_vrf(const char *ifname);
extern struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf);
extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id);
extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id);
-extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id,
- char *optional_name);
+extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
+
/* Sets the index and adds to index list */
extern int if_set_index(struct interface *ifp, ifindex_t ifindex);
/* Sets the name and adds to name list */
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index cd7f79a04e..730c7ce130 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -25,6 +25,8 @@
#include <zebra.h>
+#include "lib/log.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -33,9 +35,9 @@ extern "C" {
* Generic IP address - union of IPv4 and IPv6 address.
*/
enum ipaddr_type_t {
- IPADDR_NONE = 0,
- IPADDR_V4 = 1, /* IPv4 */
- IPADDR_V6 = 2, /* IPv6 */
+ IPADDR_NONE = AF_UNSPEC,
+ IPADDR_V4 = AF_INET,
+ IPADDR_V6 = AF_INET6,
};
struct ipaddr {
@@ -59,6 +61,18 @@ struct ipaddr {
#define IPADDRSZ(p) \
(IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr))
+static inline int ipaddr_family(const struct ipaddr *ip)
+{
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ return AF_INET;
+ case IPADDR_V6:
+ return AF_INET6;
+ default:
+ return AF_UNSPEC;
+ }
+}
+
static inline int str2ipaddr(const char *str, struct ipaddr *ip)
{
int ret;
@@ -84,15 +98,18 @@ static inline int str2ipaddr(const char *str, struct ipaddr *ip)
static inline char *ipaddr2str(const struct ipaddr *ip, char *buf, int size)
{
buf[0] = '\0';
- if (ip) {
- if (IS_IPADDR_V4(ip))
- inet_ntop(AF_INET, &ip->ip.addr, buf, size);
- else if (IS_IPADDR_V6(ip))
- inet_ntop(AF_INET6, &ip->ip.addr, buf, size);
- }
+ if (ip)
+ inet_ntop(ip->ipa_type, &ip->ip.addr, buf, size);
return buf;
}
+#define IS_MAPPED_IPV6(A) \
+ ((A)->s6_addr32[0] == 0x00000000 \
+ ? ((A)->s6_addr32[1] == 0x00000000 \
+ ? (ntohl((A)->s6_addr32[2]) == 0xFFFF ? 1 : 0) \
+ : 0) \
+ : 0)
+
/*
* Convert IPv4 address to IPv4-mapped IPv6 address which is of the
* form ::FFFF:<IPv4 address> (RFC 4291). This IPv6 address can then
@@ -128,6 +145,35 @@ static inline bool ipaddr_isset(struct ipaddr *ip)
return (0 != memcmp(&a, ip, sizeof(struct ipaddr)));
}
+/*
+ * generic ordering comparison between IP addresses
+ */
+static inline int ipaddr_cmp(const struct ipaddr *a, const struct ipaddr *b)
+{
+ uint32_t va, vb;
+ va = a->ipa_type;
+ vb = b->ipa_type;
+ if (va != vb)
+ return (va < vb) ? -1 : 1;
+ switch (a->ipa_type) {
+ case IPADDR_V4:
+ va = ntohl(a->ipaddr_v4.s_addr);
+ vb = ntohl(b->ipaddr_v4.s_addr);
+ if (va != vb)
+ return (va < vb) ? -1 : 1;
+ return 0;
+ case IPADDR_V6:
+ return memcmp((void *)&a->ipaddr_v6, (void *)&b->ipaddr_v6,
+ sizeof(a->ipaddr_v6));
+ default:
+ return 0;
+ }
+}
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pIA" (struct ipaddr *)
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libfrr.c b/lib/libfrr.c
index b3df7de6d3..2597eb61e2 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -105,6 +105,7 @@ static const struct option lo_always[] = {
{"daemon", no_argument, NULL, 'd'},
{"module", no_argument, NULL, 'M'},
{"profile", required_argument, NULL, 'F'},
+ {"pathspace", required_argument, NULL, 'N'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
{"log", required_argument, NULL, OPTION_LOG},
@@ -113,12 +114,13 @@ static const struct option lo_always[] = {
{"command-log-always", no_argument, NULL, OPTION_LOGGING},
{NULL}};
static const struct optspec os_always = {
- "hvdM:F:",
+ "hvdM:F:N:",
" -h, --help Display this help and exit\n"
" -v, --version Print program version\n"
" -d, --daemon Runs in daemon mode\n"
" -M, --module Load specified module\n"
" -F, --profile Use specified configuration profile\n"
+ " -N, --pathspace Insert prefix into config & socket paths\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
@@ -133,18 +135,16 @@ static const struct option lo_cfg_pid_dry[] = {
#ifdef HAVE_SQLITE3
{"db_file", required_argument, NULL, OPTION_DB_FILE},
#endif
- {"pathspace", required_argument, NULL, 'N'},
{"dryrun", no_argument, NULL, 'C'},
{"terminal", no_argument, NULL, 't'},
{NULL}};
static const struct optspec os_cfg_pid_dry = {
- "f:i:CtN:",
+ "f:i:Ct",
" -f, --config_file Set configuration file name\n"
" -i, --pid_file Set process identifier file name\n"
#ifdef HAVE_SQLITE3
" --db_file Set database file name\n"
#endif
- " -N, --pathspace Insert prefix into config & socket paths\n"
" -C, --dryrun Check configuration for validity and exit\n"
" -t, --terminal Open terminal session on stdio\n"
" -d -t Daemonize after terminal session ends\n",
@@ -428,8 +428,6 @@ static int frr_opt(int opt)
di->config_file = optarg;
break;
case 'N':
- if (di->flags & FRR_NO_CFG_PID_DRY)
- return 1;
if (di->pathspace) {
fprintf(stderr,
"-N/--pathspace option specified more than once!\n");
diff --git a/lib/linklist.c b/lib/linklist.c
index 272e153276..84dc6e1419 100644
--- a/lib/linklist.c
+++ b/lib/linklist.c
@@ -38,16 +38,30 @@ static void list_free_internal(struct list *l)
XFREE(MTYPE_LINK_LIST, l);
}
+
/* Allocate new listnode. Internal use only. */
-static struct listnode *listnode_new(void)
+static struct listnode *listnode_new(struct list *list, void *val)
{
- return XCALLOC(MTYPE_LINK_NODE, sizeof(struct listnode));
+ struct listnode *node;
+
+ /* if listnode memory is managed by the app then the val
+ * passed in is the listnode
+ */
+ if (list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP) {
+ node = val;
+ node->prev = node->next = NULL;
+ } else {
+ node = XCALLOC(MTYPE_LINK_NODE, sizeof(struct listnode));
+ node->data = val;
+ }
+ return node;
}
/* Free listnode. */
-static void listnode_free(struct listnode *node)
+static void listnode_free(struct list *list, struct listnode *node)
{
- XFREE(MTYPE_LINK_NODE, node);
+ if (!(list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP))
+ XFREE(MTYPE_LINK_NODE, node);
}
struct listnode *listnode_add(struct list *list, void *val)
@@ -56,10 +70,9 @@ struct listnode *listnode_add(struct list *list, void *val)
assert(val != NULL);
- node = listnode_new();
+ node = listnode_new(list, val);
node->prev = list->tail;
- node->data = val;
if (list->head == NULL)
list->head = node;
@@ -78,10 +91,9 @@ void listnode_add_head(struct list *list, void *val)
assert(val != NULL);
- node = listnode_new();
+ node = listnode_new(list, val);
node->next = list->head;
- node->data = val;
if (list->head == NULL)
list->head = node;
@@ -97,15 +109,22 @@ bool listnode_add_sort_nodup(struct list *list, void *val)
struct listnode *n;
struct listnode *new;
int ret;
+ void *data;
assert(val != NULL);
+ if (list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP) {
+ n = val;
+ data = n->data;
+ } else {
+ data = val;
+ }
+
if (list->cmp) {
for (n = list->head; n; n = n->next) {
- ret = (*list->cmp)(val, n->data);
+ ret = (*list->cmp)(data, n->data);
if (ret < 0) {
- new = listnode_new();
- new->data = val;
+ new = listnode_new(list, val);
new->next = n;
new->prev = n->prev;
@@ -124,14 +143,30 @@ bool listnode_add_sort_nodup(struct list *list, void *val)
}
}
- new = listnode_new();
- new->data = val;
+ new = listnode_new(list, val);
LISTNODE_ATTACH(list, new);
return true;
}
+struct list *list_dup(struct list *list)
+{
+ struct list *dup;
+ struct listnode *node;
+ void *data;
+
+ assert(list);
+
+ dup = list_new();
+ dup->cmp = list->cmp;
+ dup->del = list->del;
+ for (ALL_LIST_ELEMENTS_RO(list, node, data))
+ listnode_add(dup, data);
+
+ return dup;
+}
+
void listnode_add_sort(struct list *list, void *val)
{
struct listnode *n;
@@ -139,8 +174,8 @@ void listnode_add_sort(struct list *list, void *val)
assert(val != NULL);
- new = listnode_new();
- new->data = val;
+ new = listnode_new(list, val);
+ val = new->data;
if (list->cmp) {
for (n = list->head; n; n = n->next) {
@@ -177,8 +212,7 @@ struct listnode *listnode_add_after(struct list *list, struct listnode *pp,
assert(val != NULL);
- nn = listnode_new();
- nn->data = val;
+ nn = listnode_new(list, val);
if (pp == NULL) {
if (list->head)
@@ -212,8 +246,7 @@ struct listnode *listnode_add_before(struct list *list, struct listnode *pp,
assert(val != NULL);
- nn = listnode_new();
- nn->data = val;
+ nn = listnode_new(list, val);
if (pp == NULL) {
if (list->tail)
@@ -276,7 +309,7 @@ void list_delete_all_node(struct list *list)
next = node->next;
if (*list->del)
(*list->del)(node->data);
- listnode_free(node);
+ listnode_free(list, node);
}
list->head = list->tail = NULL;
list->count = 0;
@@ -336,7 +369,7 @@ void list_delete_node(struct list *list, struct listnode *node)
else
list->tail = node->prev;
list->count--;
- listnode_free(node);
+ listnode_free(list, node);
}
void list_sort(struct list *list, int (*cmp)(const void **, const void **))
diff --git a/lib/linklist.h b/lib/linklist.h
index 00cb9f8714..d8820c924d 100644
--- a/lib/linklist.h
+++ b/lib/linklist.h
@@ -43,6 +43,12 @@ struct list {
/* invariant: count is the number of listnodes in the list */
unsigned int count;
+ uint8_t flags;
+/* Indicates that listnode memory is managed by the application and
+ * doesn't need to be freed by this library via listnode_delete etc.
+ */
+#define LINKLIST_FLAG_NODE_MEM_BY_APP (1 << 0)
+
/*
* Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2.
* Used as definition of sorted for listnode_add_sort
@@ -60,10 +66,14 @@ struct list {
#define listhead(X) ((X) ? ((X)->head) : NULL)
#define listhead_unchecked(X) ((X)->head)
#define listtail(X) ((X) ? ((X)->tail) : NULL)
+#define listtail_unchecked(X) ((X)->tail)
#define listcount(X) ((X)->count)
#define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL)
/* return X->data only if X and X->data are not NULL */
#define listgetdata(X) (assert(X), assert((X)->data != NULL), (X)->data)
+/* App is going to manage listnode memory */
+#define listset_app_node_mem(X) ((X)->flags |= LINKLIST_FLAG_NODE_MEM_BY_APP)
+#define listnode_init(X, val) ((X)->data = (val))
/*
* Create a new linked list.
@@ -95,7 +105,7 @@ extern struct listnode *listnode_add(struct list *list, void *data);
* list to operate on
*
* data
- * element to add
+ * If MEM_BY_APP is set this is listnode. Otherwise it is element to add.
*/
extern void listnode_add_head(struct list *list, void *data);
@@ -112,7 +122,7 @@ extern void listnode_add_head(struct list *list, void *data);
* list to operate on
*
* val
- * element to add
+ * If MEM_BY_APP is set this is listnode. Otherwise it is element to add.
*/
extern void listnode_add_sort(struct list *list, void *val);
@@ -128,7 +138,7 @@ extern void listnode_add_sort(struct list *list, void *val);
* listnode to insert after
*
* data
- * data to insert
+ * If MEM_BY_APP is set this is listnode. Otherwise it is element to add.
*
* Returns:
* pointer to newly created listnode that contains the inserted data
@@ -148,7 +158,7 @@ extern struct listnode *listnode_add_after(struct list *list,
* listnode to insert before
*
* data
- * data to insert
+ * If MEM_BY_APP is set this is listnode. Otherwise it is element to add.
*
* Returns:
* pointer to newly created listnode that contains the inserted data
@@ -313,10 +323,23 @@ extern void list_filter_out_nodes(struct list *list, bool (*cond)(void *data));
* list to operate on
*
* val
- * element to add
+ * If MEM_BY_APP is set this is listnode. Otherwise it is element to add.
*/
extern bool listnode_add_sort_nodup(struct list *list, void *val);
+
+/*
+ * Duplicate the specified list, creating a shallow copy of each of its
+ * elements.
+ *
+ * list
+ * list to duplicate
+ *
+ * Returns:
+ * the duplicated list
+ */
+extern struct list *list_dup(struct list *list);
+
/* List iteration macro.
* Usage: for (ALL_LIST_ELEMENTS (...) { ... }
* It is safe to delete the listnode using this macro.
diff --git a/lib/log.c b/lib/log.c
index 089a3e3a07..4054185019 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -273,8 +273,7 @@ void zlog_backtrace(int priority)
if (size <= 0 || (size_t)size > array_size(array)) {
flog_err_sys(
EC_LIB_SYSTEM_CALL,
- "Cannot get backtrace, returned invalid # of frames %d "
- "(valid range is between 1 and %lu)",
+ "Cannot get backtrace, returned invalid # of frames %d (valid range is between 1 and %lu)",
size, (unsigned long)(array_size(array)));
return;
}
@@ -301,8 +300,7 @@ void zlog_thread_info(int log_level)
if (tc)
zlog(log_level,
- "Current thread function %s, scheduled from "
- "file %s, line %u",
+ "Current thread function %s, scheduled from file %s, line %u",
tc->funcname, tc->schedfrom, tc->schedfrom_line);
else
zlog(log_level, "Current thread not known/applicable");
@@ -386,6 +384,9 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_MPLS_LABELS_ADD),
DESC_ENTRY(ZEBRA_MPLS_LABELS_DELETE),
DESC_ENTRY(ZEBRA_MPLS_LABELS_REPLACE),
+ DESC_ENTRY(ZEBRA_SR_POLICY_SET),
+ DESC_ENTRY(ZEBRA_SR_POLICY_DELETE),
+ DESC_ENTRY(ZEBRA_SR_POLICY_NOTIFY_STATUS),
DESC_ENTRY(ZEBRA_IPMR_ROUTE_STATS),
DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT),
DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC),
@@ -400,6 +401,10 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET),
DESC_ENTRY(ZEBRA_LOCAL_ES_ADD),
DESC_ENTRY(ZEBRA_LOCAL_ES_DEL),
+ DESC_ENTRY(ZEBRA_REMOTE_ES_VTEP_ADD),
+ DESC_ENTRY(ZEBRA_REMOTE_ES_VTEP_DEL),
+ DESC_ENTRY(ZEBRA_LOCAL_ES_EVI_ADD),
+ DESC_ENTRY(ZEBRA_LOCAL_ES_EVI_DEL),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
DESC_ENTRY(ZEBRA_L3VNI_ADD),
@@ -446,7 +451,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES),
DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE),
DESC_ENTRY(ZEBRA_OPAQUE_REGISTER),
- DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER)};
+ DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER),
+ DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)};
#undef DESC_ENTRY
static const struct zebra_desc_table unknown = {0, "unknown", '?'};
@@ -465,8 +471,7 @@ static const struct zebra_desc_table *zroute_lookup(unsigned int 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",
+ "internal error: route type table out of order while searching for %u, please notify developers",
zroute);
return &route_types[i];
}
diff --git a/lib/log.h b/lib/log.h
index b65ae5d5d5..3d2f0ed829 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -64,13 +64,13 @@ struct message {
/* For logs which have error codes associated with them */
#define flog_err(ferr_id, format, ...) \
- zlog_err("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
+ zlog_err("[EC %u] " format, ferr_id, ##__VA_ARGS__)
#define flog_err_sys(ferr_id, format, ...) \
flog_err(ferr_id, format, ##__VA_ARGS__)
#define flog_warn(ferr_id, format, ...) \
- zlog_warn("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
+ zlog_warn("[EC %u] " format, ferr_id, ##__VA_ARGS__)
#define flog(priority, ferr_id, format, ...) \
- zlog(priority, "[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__)
+ zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__)
extern void zlog_thread_info(int log_level);
diff --git a/lib/memory.c b/lib/memory.c
index 3a29404827..2c902d123b 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -157,8 +157,7 @@ static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt)
if (!mt) {
fprintf(eda->fp,
- "%s: showing active allocations in "
- "memory group %s\n",
+ "%s: showing active allocations in memory group %s\n",
eda->prefix, mg->name);
} else if (mt->n_alloc) {
diff --git a/lib/mpls.h b/lib/mpls.h
index 126dbf753d..8922a36664 100644
--- a/lib/mpls.h
+++ b/lib/mpls.h
@@ -129,6 +129,7 @@ enum lsp_types_t {
ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */
ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */
ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */
+ ZEBRA_LSP_SRTE = 7, /* SR-TE LSP */
};
/* Functions for basic label operations. */
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
index e1c0159fc5..98f359401e 100644
--- a/lib/netns_linux.c
+++ b/lib/netns_linux.c
@@ -379,20 +379,12 @@ struct ns *ns_lookup(ns_id_t ns_id)
return ns_lookup_internal(ns_id);
}
-void ns_walk_func(int (*func)(struct ns *,
- void *param_in,
- void **param_out),
- void *param_in,
- void **param_out)
+void ns_walk_func(int (*func)(struct ns *))
{
struct ns *ns = NULL;
- int ret;
- RB_FOREACH (ns, ns_head, &ns_tree) {
- ret = func(ns, param_in, param_out);
- if (ret == NS_WALK_STOP)
- return;
- }
+ RB_FOREACH (ns, ns_head, &ns_tree)
+ func(ns);
}
const char *ns_get_name(struct ns *ns)
@@ -592,33 +584,9 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
return ret;
}
-/* if relative link_nsid matches default netns,
- * then return default absolute netns value
- * otherwise, return NS_UNKNOWN
- */
-ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid)
-{
- struct ns *ns;
-
- ns = ns_lookup(ns_id_reference);
- if (!ns)
- return NS_UNKNOWN;
- if (ns->relative_default_ns != link_nsid)
- return NS_UNKNOWN;
- ns = ns_get_default();
- assert(ns);
- return ns->ns_id;
-}
-
ns_id_t ns_get_default_id(void)
{
if (default_ns)
return default_ns->ns_id;
return NS_DEFAULT_INTERNAL;
}
-
-struct ns *ns_get_default(void)
-{
- return default_ns;
-}
-
diff --git a/lib/nexthop.c b/lib/nexthop.c
index 3496081d47..0ea72d03e1 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -152,11 +152,20 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
break;
}
+ if (next1->srte_color < next2->srte_color)
+ return -1;
+ if (next1->srte_color > next2->srte_color)
+ return 1;
+
ret = _nexthop_source_cmp(next1, next2);
if (ret != 0)
goto done;
if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
+ !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ return 0;
+
+ if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return -1;
@@ -164,12 +173,18 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
!CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return 1;
- if (next1->backup_idx < next2->backup_idx)
+ if (next1->backup_num == 0 && next2->backup_num == 0)
+ goto done;
+
+ if (next1->backup_num < next2->backup_num)
return -1;
- if (next1->backup_idx > next2->backup_idx)
+ if (next1->backup_num > next2->backup_num)
return 1;
+ ret = memcmp(next1->backup_idx,
+ next2->backup_idx, next1->backup_num);
+
done:
return ret;
}
@@ -515,11 +530,12 @@ struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
return next;
}
-unsigned int nexthop_level(struct nexthop *nexthop)
+unsigned int nexthop_level(const struct nexthop *nexthop)
{
unsigned int rv = 0;
- for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
+ for (const struct nexthop *par = nexthop->rparent;
+ par; par = par->rparent)
rv++;
return rv;
@@ -529,14 +545,15 @@ unsigned int nexthop_level(struct nexthop *nexthop)
uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
- uint32_t val;
+ int i;
key = jhash_3words(nexthop->type, nexthop->vrf_id,
nexthop->nh_label_type, key);
if (nexthop->nh_label) {
int labels = nexthop->nh_label->num_labels;
- int i = 0;
+
+ i = 0;
while (labels >= 3) {
key = jhash_3words(nexthop->nh_label->label[i],
@@ -559,14 +576,35 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
key = jhash_1word(nexthop->nh_label->label[i], key);
}
- val = 0;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- val = (uint32_t)nexthop->backup_idx;
-
- key = jhash_3words(nexthop->ifindex,
- CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val,
+ key = jhash_2words(nexthop->ifindex,
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
key);
+ /* Include backup nexthops, if present */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ int backups = nexthop->backup_num;
+
+ i = 0;
+
+ while (backups >= 3) {
+ key = jhash_3words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1],
+ nexthop->backup_idx[i + 2], key);
+ backups -= 3;
+ i += 3;
+ }
+
+ while (backups >= 2) {
+ key = jhash_2words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1], key);
+ backups -= 2;
+ i += 2;
+ }
+
+ if (backups >= 1)
+ key = jhash_1word(nexthop->backup_idx[i], key);
+ }
+
return key;
}
@@ -604,7 +642,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
copy->type = nexthop->type;
copy->flags = nexthop->flags;
copy->weight = nexthop->weight;
- copy->backup_idx = nexthop->backup_idx;
+
+ assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
+ copy->backup_num = nexthop->backup_num;
+ if (copy->backup_num > 0)
+ memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
+
+ copy->srte_color = nexthop->srte_color;
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
@@ -621,7 +665,7 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
nexthop_copy_no_recurse(copy, nexthop, rparent);
/* Bit of a special case here, we need to handle the case
- * of a nexthop resolving to agroup. Hence, we need to
+ * of a nexthop resolving to a group. Hence, we need to
* use a nexthop_group API.
*/
if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -647,6 +691,67 @@ struct nexthop *nexthop_dup(const struct nexthop *nexthop,
}
/*
+ * Parse one or more backup index values, as comma-separated numbers,
+ * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
+ * in size. Mails back the number of values converted, and returns 0 on
+ * success, <0 if an error in parsing.
+ */
+int nexthop_str2backups(const char *str, int *num_backups,
+ uint8_t *backups)
+{
+ char *ostr; /* copy of string (start) */
+ char *lstr; /* working copy of string */
+ char *nump; /* pointer to next segment */
+ char *endp; /* end pointer */
+ int i, ret;
+ uint8_t tmp[NEXTHOP_MAX_BACKUPS];
+ uint32_t lval;
+
+ /* Copy incoming string; the parse is destructive */
+ lstr = ostr = XSTRDUP(MTYPE_TMP, str);
+ *num_backups = 0;
+ ret = 0;
+
+ for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) {
+ nump = strsep(&lstr, ",");
+ lval = strtoul(nump, &endp, 10);
+
+ /* Format check */
+ if (*endp != '\0') {
+ ret = -1;
+ break;
+ }
+
+ /* Empty value */
+ if (endp == nump) {
+ ret = -1;
+ break;
+ }
+
+ /* Limit to one octet */
+ if (lval > 255) {
+ ret = -1;
+ break;
+ }
+
+ tmp[i] = lval;
+ }
+
+ /* Excess values */
+ if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr)
+ ret = -1;
+
+ if (ret == 0) {
+ *num_backups = i;
+ memcpy(backups, tmp, i);
+ }
+
+ XFREE(MTYPE_TMP, ostr);
+
+ return ret;
+}
+
+/*
* nexthop printing variants:
* %pNHvv
* via 1.2.3.4
diff --git a/lib/nexthop.h b/lib/nexthop.h
index eda88efc08..cadcea1f41 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -65,6 +65,12 @@ enum nh_encap_type {
NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */
};
+/* Fixed limit on the number of backup nexthops per primary nexthop */
+#define NEXTHOP_MAX_BACKUPS 8
+
+/* Backup index value is limited */
+#define NEXTHOP_BACKUP_IDX_MAX 255
+
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
@@ -92,6 +98,7 @@ struct nexthop {
*/
#define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */
#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */
+#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */
#define NEXTHOP_IS_ACTIVE(flags) \
(CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \
@@ -124,20 +131,21 @@ struct nexthop {
/* Weight of the nexthop ( for unequal cost ECMP ) */
uint8_t weight;
- /* Index of a corresponding backup nexthop in a backup list;
+ /* Count and index of corresponding backup nexthop(s) in a backup list;
* only meaningful if the HAS_BACKUP flag is set.
*/
- uint8_t backup_idx;
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
/* Encapsulation information. */
enum nh_encap_type nh_encap_type;
union {
vni_t vni;
} nh_encap;
-};
-/* Backup index value is limited */
-#define NEXTHOP_BACKUP_IDX_MAX 255
+ /* SR-TE color used for matching SR-TE policies */
+ uint32_t srte_color;
+};
/* Utility to append one nexthop to another. */
#define NEXTHOP_APPEND(to, new) \
@@ -216,7 +224,7 @@ extern const char *nexthop2str(const struct nexthop *nexthop,
extern struct nexthop *nexthop_next(const struct nexthop *nexthop);
extern struct nexthop *
nexthop_next_active_resolved(const struct nexthop *nexthop);
-extern unsigned int nexthop_level(struct nexthop *nexthop);
+extern unsigned int nexthop_level(const struct nexthop *nexthop);
/* Copies to an already allocated nexthop struct */
extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
struct nexthop *rparent);
@@ -231,6 +239,15 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
struct nexthop *rparent);
+/*
+ * Parse one or more backup index values, as comma-separated numbers,
+ * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
+ * in size. Mails back the number of values converted, and returns 0 on
+ * success, <0 if an error in parsing.
+ */
+int nexthop_str2backups(const char *str, int *num_backups,
+ uint8_t *backups);
+
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pNH" (struct nexthop *)
#endif
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index 4f0c72af27..8ae001e42a 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -43,12 +43,9 @@ struct nexthop_hold {
char *intf;
char *labels;
uint32_t weight;
- int backup_idx; /* Index of backup nexthop, if >= 0 */
+ char *backup_str;
};
-/* Invalid/unset value for nexthop_hold's backup_idx */
-#define NHH_BACKUP_IDX_INVALID -1
-
struct nexthop_group_hooks {
void (*new)(const char *name);
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
@@ -677,7 +674,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
const char *intf, const char *labels,
- const uint32_t weight, int backup_idx)
+ const uint32_t weight,
+ const char *backup_str)
{
struct nexthop_hold *nh;
@@ -694,7 +692,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
nh->weight = weight;
- nh->backup_idx = backup_idx;
+ if (backup_str)
+ nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
listnode_add_sort(nhgc->nhg_list, nh);
}
@@ -741,10 +740,11 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
const union sockunion *addr,
const char *intf, const char *name,
const char *labels, int *lbl_ret,
- uint32_t weight, int backup_idx)
+ uint32_t weight, const char *backup_str)
{
int ret = 0;
struct vrf *vrf;
+ int num;
memset(nhop, 0, sizeof(*nhop));
@@ -800,13 +800,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
nhop->weight = weight;
- if (backup_idx != NHH_BACKUP_IDX_INVALID) {
- /* Validate index value */
- if (backup_idx > NEXTHOP_BACKUP_IDX_MAX)
+ if (backup_str) {
+ /* Parse backup indexes */
+ ret = nexthop_str2backups(backup_str,
+ &num, nhop->backup_idx);
+ if (ret == 0) {
+ SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nhop->backup_num = num;
+ } else
return false;
-
- SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nhop->backup_idx = backup_idx;
}
return true;
@@ -820,7 +822,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop,
{
return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name, nhh->labels, NULL,
- nhh->weight, nhh->backup_idx));
+ nhh->weight, nhh->backup_str));
}
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
@@ -833,7 +835,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
nexthop-vrf NAME$vrf_name \
|label WORD \
|weight (1-255) \
- |backup-idx$bi_str (0-254)$idx \
+ |backup-idx WORD \
}]",
NO_STR
"Specify one of the nexthops in this ECMP group\n"
@@ -847,19 +849,26 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"One or more labels in the range (16-1048575) separated by '/'\n"
"Weight to be used by the nexthop for purposes of ECMP\n"
"Weight value to be used\n"
- "Backup nexthop index in another group\n"
- "Nexthop index value\n")
+ "Specify backup nexthop indexes in another group\n"
+ "One or more indexes in the range (0-254) separated by ','\n")
{
VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct nexthop nhop;
struct nexthop *nh;
int lbl_ret = 0;
bool legal;
- int backup_idx = idx;
+ int num;
+ uint8_t backups[NEXTHOP_MAX_BACKUPS];
bool yes = !no;
- if (bi_str == NULL)
- backup_idx = NHH_BACKUP_IDX_INVALID;
+ /* Pre-parse backup string to validate */
+ if (backup_idx) {
+ lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
+ if (lbl_ret < 0) {
+ vty_out(vty, "%% Invalid backups\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
&lbl_ret, weight, backup_idx);
@@ -943,10 +952,11 @@ static struct cmd_node nexthop_group_node = {
.config_write = nexthop_group_write,
};
-void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
+void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
{
char buf[100];
struct vrf *vrf;
+ int i;
vty_out(vty, "nexthop ");
@@ -976,7 +986,7 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
if (nh->vrf_id != VRF_DEFAULT) {
vrf = vrf_lookup_by_id(nh->vrf_id);
- vty_out(vty, " nexthop-vrf %s", vrf->name);
+ vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
}
if (nh->nh_label && nh->nh_label->num_labels > 0) {
@@ -991,16 +1001,22 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
- if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, " backup-idx %d", nh->backup_idx);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
+
+ for (i = 1; i < nh->backup_num; i++)
+ vty_out(vty, ",%d", nh->backup_idx[i]);
+ }
vty_out(vty, "\n");
}
-void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh)
+void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
{
char buf[100];
struct vrf *vrf;
+ json_object *json_backups = NULL;
+ int i;
switch (nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -1047,12 +1063,19 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh)
if (nh->weight)
json_object_int_add(j, "weight", nh->weight);
- if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(j, "backupIdx", nh->backup_idx);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nh->backup_num; i++)
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nh->backup_idx[i]));
+
+ json_object_object_add(j, "backupIdx", json_backups);
+ }
}
static void nexthop_group_write_nexthop_internal(struct vty *vty,
- struct nexthop_hold *nh)
+ const struct nexthop_hold *nh)
{
char buf[100];
@@ -1073,8 +1096,8 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
- if (nh->backup_idx != NHH_BACKUP_IDX_INVALID)
- vty_out(vty, " backup-idx %d", nh->backup_idx);
+ if (nh->backup_str)
+ vty_out(vty, " backup-idx %s", nh->backup_str);
vty_out(vty, "\n");
}
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index 9888dad982..0b5ac91bb2 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -135,9 +135,11 @@ extern bool nexthop_group_equal(const struct nexthop_group *nhg1,
extern struct nexthop_group_cmd *nhgc_find(const char *name);
-extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh);
+extern void nexthop_group_write_nexthop(struct vty *vty,
+ const struct nexthop *nh);
-extern void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh);
+extern void nexthop_group_json_nexthop(json_object *j,
+ const struct nexthop *nh);
/* Return the number of nexthops in this nhg */
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
diff --git a/lib/northbound.c b/lib/northbound.c
index 48b8499bfc..29e843a84e 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -680,8 +680,12 @@ int nb_candidate_commit_prepare(struct nb_context *context,
RB_INIT(nb_config_cbs, &changes);
nb_config_diff(running_config, candidate, &changes);
- if (RB_EMPTY(nb_config_cbs, &changes))
+ if (RB_EMPTY(nb_config_cbs, &changes)) {
+ snprintf(
+ errmsg, errmsg_len,
+ "No changes to apply were found during preparation phase");
return NB_ERR_NO_CHANGES;
+ }
if (nb_candidate_validate_code(context, candidate, &changes, errmsg,
errmsg_len)
@@ -707,23 +711,21 @@ int nb_candidate_commit_prepare(struct nb_context *context,
errmsg_len);
}
-void nb_candidate_commit_abort(struct nb_transaction *transaction)
+void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg,
+ size_t errmsg_len)
{
- char errmsg[BUFSIZ] = {0};
-
(void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg,
- sizeof(errmsg));
+ errmsg_len);
nb_transaction_free(transaction);
}
void nb_candidate_commit_apply(struct nb_transaction *transaction,
- bool save_transaction, uint32_t *transaction_id)
+ bool save_transaction, uint32_t *transaction_id,
+ char *errmsg, size_t errmsg_len)
{
- char errmsg[BUFSIZ] = {0};
-
(void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg,
- sizeof(errmsg));
- nb_transaction_apply_finish(transaction, errmsg, sizeof(errmsg));
+ errmsg_len);
+ nb_transaction_apply_finish(transaction, errmsg, errmsg_len);
/* Replace running by candidate. */
transaction->config->version++;
@@ -754,9 +756,9 @@ int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate,
*/
if (ret == NB_OK)
nb_candidate_commit_apply(transaction, save_transaction,
- transaction_id);
+ transaction_id, errmsg, errmsg_len);
else if (transaction != NULL)
- nb_candidate_commit_abort(transaction);
+ nb_candidate_commit_abort(transaction, errmsg, errmsg_len);
return ret;
}
@@ -2046,18 +2048,21 @@ void *nb_running_unset_entry(const struct lyd_node *dnode)
return entry;
}
-void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
- bool abort_if_not_found)
+static void *nb_running_get_entry_worker(const struct lyd_node *dnode,
+ const char *xpath,
+ bool abort_if_not_found,
+ bool rec_search)
{
const struct lyd_node *orig_dnode = dnode;
char xpath_buf[XPATH_MAXLEN];
+ bool rec_flag = true;
assert(dnode || xpath);
if (!dnode)
dnode = yang_dnode_get(running_config->dnode, xpath);
- while (dnode) {
+ while (rec_flag && dnode) {
struct nb_config_entry *config, s;
yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
@@ -2065,6 +2070,8 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
if (config)
return config->entry;
+ rec_flag = rec_search;
+
dnode = dnode->parent;
}
@@ -2078,6 +2085,20 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
abort();
}
+void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
+ bool abort_if_not_found)
+{
+ return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
+ true);
+}
+
+void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
+ const char *xpath, bool abort_if_not_found)
+{
+ return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
+ false);
+}
+
/* Logging functions. */
const char *nb_event_name(enum nb_event event)
{
diff --git a/lib/northbound.h b/lib/northbound.h
index bd57013f59..fa5ac5616c 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -910,8 +910,15 @@ extern int nb_candidate_commit_prepare(struct nb_context *context,
*
* transaction
* Candidate configuration to abort. It's consumed by this function.
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
*/
-extern void nb_candidate_commit_abort(struct nb_transaction *transaction);
+extern void nb_candidate_commit_abort(struct nb_transaction *transaction,
+ char *errmsg, size_t errmsg_len);
/*
* Commit a previously created configuration transaction.
@@ -925,10 +932,17 @@ extern void nb_candidate_commit_abort(struct nb_transaction *transaction);
*
* transaction_id
* Optional output parameter providing the ID of the committed transaction.
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
*/
extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
bool save_transaction,
- uint32_t *transaction_id);
+ uint32_t *transaction_id, char *errmsg,
+ size_t errmsg_len);
/*
* Create a new transaction to commit a candidate configuration. This is a
@@ -1165,6 +1179,14 @@ extern void *nb_running_get_entry(const struct lyd_node *dnode,
const char *xpath, bool abort_if_not_found);
/*
+ * Same as 'nb_running_get_entry', but doesn't search within parent nodes
+ * recursively if an user point is not found.
+ */
+extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
+ const char *xpath,
+ bool abort_if_not_found);
+
+/*
* Return a human-readable string representing a northbound event.
*
* event
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 105fc83cef..2f6aef5398 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -53,6 +53,108 @@ static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg)
vty_out(vty, "Error description: %s\n", errmsg);
}
+static int nb_cli_classic_commit(struct vty *vty)
+{
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
+ int ret;
+
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL,
+ NULL, errmsg, sizeof(errmsg));
+ if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
+ vty_out(vty, "%% Configuration failed.\n\n");
+ vty_show_nb_errors(vty, ret, errmsg);
+ if (vty->t_pending_commit)
+ vty_out(vty,
+ "The following commands were dynamically grouped into the same transaction and rejected:\n%s",
+ vty->pending_cmds_buf);
+
+ /* Regenerate candidate for consistency. */
+ nb_config_replace(vty->candidate_config, running_config, true);
+ return CMD_WARNING_CONFIG_FAILED;
+ } else if (strlen(errmsg) > 0)
+ /* Successful commit. Print warnings (if any). */
+ vty_out(vty, "%s\n", errmsg);
+
+ return CMD_SUCCESS;
+}
+
+static void nb_cli_pending_commit_clear(struct vty *vty)
+{
+ THREAD_TIMER_OFF(vty->t_pending_commit);
+ vty->backoff_cmd_count = 0;
+ XFREE(MTYPE_TMP, vty->pending_cmds_buf);
+ vty->pending_cmds_buflen = 0;
+ vty->pending_cmds_bufpos = 0;
+}
+
+static int nb_cli_pending_commit_cb(struct thread *thread)
+{
+ struct vty *vty = THREAD_ARG(thread);
+
+ (void)nb_cli_classic_commit(vty);
+ nb_cli_pending_commit_clear(vty);
+
+ return 0;
+}
+
+void nb_cli_pending_commit_check(struct vty *vty)
+{
+ if (vty->t_pending_commit) {
+ (void)nb_cli_classic_commit(vty);
+ nb_cli_pending_commit_clear(vty);
+ }
+}
+
+static bool nb_cli_backoff_start(struct vty *vty)
+{
+ struct timeval now, delta;
+
+ /*
+ * Start the configuration backoff timer only if 100 YANG-modeled
+ * commands or more were entered within the last second.
+ */
+ monotime(&now);
+ if (monotime_since(&vty->backoff_start, &delta) >= 1000000) {
+ vty->backoff_start = now;
+ vty->backoff_cmd_count = 1;
+ return false;
+ }
+ if (++vty->backoff_cmd_count < 100)
+ return false;
+
+ return true;
+}
+
+static int nb_cli_schedule_command(struct vty *vty)
+{
+ /* Append command to dynamically sized buffer of scheduled commands. */
+ if (!vty->pending_cmds_buf) {
+ vty->pending_cmds_buflen = 4096;
+ vty->pending_cmds_buf =
+ XCALLOC(MTYPE_TMP, vty->pending_cmds_buflen);
+ }
+ if ((strlen(vty->buf) + 3)
+ > (vty->pending_cmds_buflen - vty->pending_cmds_bufpos)) {
+ vty->pending_cmds_buflen *= 2;
+ vty->pending_cmds_buf =
+ XREALLOC(MTYPE_TMP, vty->pending_cmds_buf,
+ vty->pending_cmds_buflen);
+ }
+ strlcat(vty->pending_cmds_buf, "- ", vty->pending_cmds_buflen);
+ vty->pending_cmds_bufpos = strlcat(vty->pending_cmds_buf, vty->buf,
+ vty->pending_cmds_buflen);
+
+ /* Schedule the commit operation. */
+ THREAD_TIMER_OFF(vty->t_pending_commit);
+ thread_add_timer_msec(master, nb_cli_pending_commit_cb, vty, 100,
+ &vty->t_pending_commit);
+
+ return CMD_SUCCESS;
+}
+
void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
enum nb_operation operation, const char *value)
{
@@ -76,7 +178,6 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
{
char xpath_base[XPATH_MAXLEN] = {};
bool error = false;
- int ret;
VTY_CHECK_XPATH;
@@ -95,6 +196,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
struct yang_data *data;
+ int ret;
/* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
@@ -158,25 +260,19 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
}
- /* Do an implicit "commit" when using the classic CLI mode. */
+ /*
+ * Do an implicit commit when using the classic CLI mode.
+ *
+ * NOTE: the implicit commit might be scheduled to run later when
+ * too many commands are being sent at the same time. This is a
+ * protection mechanism where multiple commands are grouped into the
+ * same configuration transaction, allowing them to be processed much
+ * faster.
+ */
if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
- struct nb_context context = {};
- char errmsg[BUFSIZ] = {0};
-
- context.client = NB_CLIENT_CLI;
- context.user = vty;
- ret = nb_candidate_commit(&context, vty->candidate_config,
- false, NULL, NULL, errmsg,
- sizeof(errmsg));
- if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
- vty_out(vty, "%% Configuration failed.\n\n");
- vty_show_nb_errors(vty, ret, errmsg);
-
- /* Regenerate candidate for consistency. */
- nb_config_replace(vty->candidate_config, running_config,
- true);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (vty->t_pending_commit || nb_cli_backoff_start(vty))
+ return nb_cli_schedule_command(vty);
+ return nb_cli_classic_commit(vty);
}
return CMD_SUCCESS;
@@ -224,11 +320,14 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty)
&context, vty->confirmed_commit_rollback, true,
"Rollback to previous configuration - confirmed commit has timed out",
&transaction_id, errmsg, sizeof(errmsg));
- if (ret == NB_OK)
+ if (ret == NB_OK) {
vty_out(vty,
"Rollback performed successfully (Transaction ID #%u).\n",
transaction_id);
- else {
+ /* Print warnings (if any). */
+ if (strlen(errmsg) > 0)
+ vty_out(vty, "%s\n", errmsg);
+ } else {
vty_out(vty,
"Failed to rollback to previous configuration.\n\n");
vty_show_nb_errors(vty, ret, errmsg);
@@ -313,6 +412,9 @@ static int nb_cli_commit(struct vty *vty, bool force,
vty_out(vty,
"%% Configuration committed successfully (Transaction ID #%u).\n\n",
transaction_id);
+ /* Print warnings (if any). */
+ if (strlen(errmsg) > 0)
+ vty_out(vty, "%s\n", errmsg);
return CMD_SUCCESS;
case NB_ERR_NO_CHANGES:
vty_out(vty, "%% No configuration changes to commit.\n\n");
@@ -1572,6 +1674,9 @@ static int nb_cli_rollback_configuration(struct vty *vty,
case NB_OK:
vty_out(vty,
"%% Configuration was successfully rolled back.\n\n");
+ /* Print warnings (if any). */
+ if (strlen(errmsg) > 0)
+ vty_out(vty, "%s\n", errmsg);
return CMD_SUCCESS;
case NB_ERR_NO_CHANGES:
vty_out(vty,
diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h
index b2d8c8f035..112d62efda 100644
--- a/lib/northbound_cli.h
+++ b/lib/northbound_cli.h
@@ -108,6 +108,14 @@ extern int nb_cli_rpc(const char *xpath, struct list *input,
extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+/*
+ * Perform pending commit, if any.
+ *
+ * vty
+ * The vty context.
+ */
+extern void nb_cli_pending_commit_check(struct vty *vty);
+
/* Prototypes of internal functions. */
extern void nb_cli_show_config_prepare(struct nb_config *config,
bool with_defaults);
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
index a3aaf02f08..1f480f3d02 100644
--- a/lib/northbound_confd.c
+++ b/lib/northbound_confd.c
@@ -375,8 +375,10 @@ static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen)
/* Apply the transaction. */
if (transaction) {
struct nb_config *candidate = transaction->config;
+ char errmsg[BUFSIZ] = {0};
- nb_candidate_commit_apply(transaction, true, NULL);
+ nb_candidate_commit_apply(transaction, true, NULL, errmsg,
+ sizeof(errmsg));
nb_config_free(candidate);
}
@@ -400,8 +402,9 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen)
/* Abort the transaction. */
if (transaction) {
struct nb_config *candidate = transaction->config;
+ char errmsg[BUFSIZ] = {0};
- nb_candidate_commit_abort(transaction);
+ nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg));
nb_config_free(candidate);
}
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index 83d7e0ce95..f35b4bb31b 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -677,11 +677,13 @@ class NorthboundImpl
switch (phase) {
case frr::CommitRequest::VALIDATE:
+ zlog_debug("`-> Performing VALIDATE");
ret = nb_candidate_validate(
&context, candidate->config, errmsg,
sizeof(errmsg));
break;
case frr::CommitRequest::PREPARE:
+ zlog_debug("`-> Performing PREPARE");
ret = nb_candidate_commit_prepare(
&context, candidate->config,
comment.c_str(),
@@ -689,15 +691,20 @@ class NorthboundImpl
sizeof(errmsg));
break;
case frr::CommitRequest::ABORT:
+ zlog_debug("`-> Performing ABORT");
nb_candidate_commit_abort(
- candidate->transaction);
+ candidate->transaction, errmsg,
+ sizeof(errmsg));
break;
case frr::CommitRequest::APPLY:
+ zlog_debug("`-> Performing ABORT");
nb_candidate_commit_apply(
candidate->transaction, true,
- &transaction_id);
+ &transaction_id, errmsg,
+ sizeof(errmsg));
break;
case frr::CommitRequest::ALL:
+ zlog_debug("`-> Performing ALL");
ret = nb_candidate_commit(
&context, candidate->config, true,
comment.c_str(), &transaction_id,
@@ -735,12 +742,20 @@ class NorthboundImpl
grpc::StatusCode::INTERNAL, errmsg);
break;
}
+
+ if (nb_dbg_client_grpc)
+ zlog_debug("`-> Result: %s (message: '%s')",
+ nb_err_name((enum nb_error)ret),
+ errmsg);
+
if (ret == NB_OK) {
// Response: uint32 transaction_id = 1;
if (transaction_id)
tag->response.set_transaction_id(
transaction_id);
}
+ if (strlen(errmsg) > 0)
+ tag->response.set_error_message(errmsg);
tag->responder.Finish(tag->response, status, tag);
tag->state = FINISH;
@@ -1281,10 +1296,13 @@ class NorthboundImpl
void delete_candidate(struct candidate *candidate)
{
+ char errmsg[BUFSIZ] = {0};
+
_candidates.erase(candidate->id);
nb_config_free(candidate->config);
if (candidate->transaction)
- nb_candidate_commit_abort(candidate->transaction);
+ nb_candidate_commit_abort(candidate->transaction,
+ errmsg, sizeof(errmsg));
}
struct candidate *get_candidate(uint32_t candidate_id)
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 500203173c..2209b19c14 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -329,8 +329,10 @@ static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session,
/* Apply the transaction. */
if (transaction) {
struct nb_config *candidate = transaction->config;
+ char errmsg[BUFSIZ] = {0};
- nb_candidate_commit_apply(transaction, true, NULL);
+ nb_candidate_commit_apply(transaction, true, NULL, errmsg,
+ sizeof(errmsg));
nb_config_free(candidate);
}
@@ -343,8 +345,9 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session,
/* Abort the transaction. */
if (transaction) {
struct nb_config *candidate = transaction->config;
+ char errmsg[BUFSIZ] = {0};
- nb_candidate_commit_abort(transaction);
+ nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg));
nb_config_free(candidate);
}
diff --git a/lib/ns.h b/lib/ns.h
index 286ff5b295..20e0a38e3b 100644
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -53,11 +53,6 @@ struct ns {
/* Identifier, mapped on the NSID value */
ns_id_t internal_ns_id;
- /* Identifier, value of NSID of default netns,
- * relative value in that local netns
- */
- ns_id_t relative_default_ns;
-
/* Name */
char *name;
@@ -125,14 +120,7 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id);
extern char *ns_netns_pathname(struct vty *vty, const char *name);
/* Parse and execute a function on all the NETNS */
-#define NS_WALK_CONTINUE 0
-#define NS_WALK_STOP 1
-
-extern void ns_walk_func(int (*func)(struct ns *,
- void *,
- void **),
- void *param_in,
- void **param_out);
+extern void ns_walk_func(int (*func)(struct ns *));
/* API to get the NETNS name, from the ns pointer */
extern const char *ns_get_name(struct ns *ns);
@@ -186,9 +174,7 @@ extern struct ns *ns_lookup_name(const char *name);
*/
extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *));
extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id);
-extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid);
extern void ns_disable(struct ns *ns);
-extern struct ns *ns_get_default(void);
#ifdef __cplusplus
}
diff --git a/lib/pbr.h b/lib/pbr.h
index cf6ac41d32..fd183d7115 100644
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,6 +49,10 @@ struct pbr_filter {
#define PBR_FILTER_PROTO (1 << 5)
#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
+#define PBR_FILTER_DSFIELD (1 << 8)
+
+#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
+#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
/* Source and Destination IP address with masks. */
struct prefix src_ip;
@@ -58,6 +62,9 @@ struct pbr_filter {
uint16_t src_port;
uint16_t dst_port;
+ /* Filter by Differentiated Services field */
+ uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */
+
/* Filter with fwmark */
uint32_t fwmark;
};
diff --git a/lib/prefix.c b/lib/prefix.c
index 0900100be3..697e1a6239 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -22,6 +22,7 @@
#include <zebra.h>
#include "prefix.h"
+#include "ipaddr.h"
#include "vty.h"
#include "sockunion.h"
#include "memory.h"
@@ -1316,6 +1317,26 @@ char *esi_to_str(const esi_t *esi, char *buf, int size)
return ptr;
}
+printfrr_ext_autoreg_p("EA", printfrr_ea)
+static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt,
+ int prec, const void *ptr)
+{
+ const struct ethaddr *mac = ptr;
+
+ prefix_mac2str(mac, buf, bsz);
+ return 2;
+}
+
+printfrr_ext_autoreg_p("IA", printfrr_ia)
+static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt,
+ int prec, const void *ptr)
+{
+ const struct ipaddr *ipa = ptr;
+
+ ipaddr2str(ipa, buf, bsz);
+ return 2;
+}
+
printfrr_ext_autoreg_p("I4", printfrr_i4)
static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt,
int prec, const void *ptr)
diff --git a/lib/prefix.h b/lib/prefix.h
index 0bd457cc23..400f07386f 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -43,9 +43,36 @@ extern "C" {
#define ETH_ALEN 6
#endif
+/* EVPN route types. */
+typedef enum {
+ BGP_EVPN_AD_ROUTE = 1, /* Ethernet Auto-Discovery (A-D) route */
+ BGP_EVPN_MAC_IP_ROUTE, /* MAC/IP Advertisement route */
+ BGP_EVPN_IMET_ROUTE, /* Inclusive Multicast Ethernet Tag route */
+ BGP_EVPN_ES_ROUTE, /* Ethernet Segment route */
+ BGP_EVPN_IP_PREFIX_ROUTE, /* IP Prefix route */
+} bgp_evpn_route_type;
+
+/* value of first byte of ESI */
+#define ESI_TYPE_ARBITRARY 0 /* */
+#define ESI_TYPE_LACP 1 /* <> */
+#define ESI_TYPE_BRIDGE 2 /* <Root bridge Mac-6B>:<Root Br Priority-2B>:00 */
+#define ESI_TYPE_MAC 3 /* <Syst Mac Add-6B>:<Local Discriminator Value-3B> */
+#define ESI_TYPE_ROUTER 4 /* <RouterId-4B>:<Local Discriminator Value-4B> */
+#define ESI_TYPE_AS 5 /* <AS-4B>:<Local Discriminator Value-4B> */
+
+#define MAX_ESI {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+
+
+#define EVPN_ETH_TAG_BYTES 4
#define ESI_BYTES 10
#define ESI_STR_LEN (3 * ESI_BYTES)
+/* Maximum number of VTEPs per-ES -
+ * XXX - temporary limit for allocating strings etc.
+ */
+#define ES_VTEP_MAX_CNT 10
+#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * 16)
+
#define ETHER_ADDR_STRLEN (3*ETH_ALEN)
/*
* there isn't a portable ethernet address type. We define our
@@ -64,12 +91,13 @@ struct ethaddr {
#define PREFIX_LEN_ROUTE_TYPE_5_IPV6 (30*8)
typedef struct esi_t_ {
- uint8_t val[10];
+ uint8_t val[ESI_BYTES];
} esi_t;
struct evpn_ead_addr {
esi_t esi;
uint32_t eth_tag;
+ struct ipaddr ip;
};
struct evpn_macip_addr {
@@ -217,39 +245,45 @@ struct prefix_evpn {
static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp)
{
- if (evp->prefix.route_type == 2)
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ return IS_IPADDR_NONE(&(evp)->prefix.ead_addr.ip);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip);
- if (evp->prefix.route_type == 3)
+ if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE)
return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip);
- if (evp->prefix.route_type == 4)
+ if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE)
return IS_IPADDR_NONE(&(evp)->prefix.es_addr.ip);
- if (evp->prefix.route_type == 5)
+ if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip);
return 0;
}
static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp)
{
- if (evp->prefix.route_type == 2)
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ return IS_IPADDR_V4(&(evp)->prefix.ead_addr.ip);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip);
- if (evp->prefix.route_type == 3)
+ if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE)
return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip);
- if (evp->prefix.route_type == 4)
+ if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE)
return IS_IPADDR_V4(&(evp)->prefix.es_addr.ip);
- if (evp->prefix.route_type == 5)
+ if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip);
return 0;
}
static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp)
{
- if (evp->prefix.route_type == 2)
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ return IS_IPADDR_V6(&(evp)->prefix.ead_addr.ip);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip);
- if (evp->prefix.route_type == 3)
+ if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE)
return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip);
- if (evp->prefix.route_type == 4)
+ if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE)
return IS_IPADDR_V6(&(evp)->prefix.es_addr.ip);
- if (evp->prefix.route_type == 5)
+ if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip);
return 0;
}
@@ -555,6 +589,8 @@ static inline int is_default_host_route(const struct prefix *p)
}
#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pEA" (struct ethaddr *)
+
#pragma FRR printfrr_ext "%pI4" (struct in_addr *)
#pragma FRR printfrr_ext "%pI4" (in_addr_t *)
diff --git a/lib/privs.c b/lib/privs.c
index eb0dbe0783..5c7e1240e2 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -558,8 +558,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs)
/* 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",
+ "%s: capabilities enabled, but no valid capabilities supplied\n",
__func__);
}
diff --git a/lib/privs.h b/lib/privs.h
index db5707d675..18ba8e8888 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -24,6 +24,7 @@
#define _ZEBRA_PRIVS_H
#include <pthread.h>
+#include <stdint.h>
#include "lib/queue.h"
#ifdef __cplusplus
diff --git a/lib/route_types.pl b/lib/route_types.pl
index f297096633..e007de4d69 100755
--- a/lib/route_types.pl
+++ b/lib/route_types.pl
@@ -121,7 +121,7 @@ sub codelist {
}
$str =~ s/ $//;
push @lines, $str . "\\n\" \\\n";
- push @lines, " \" > - selected route, * - FIB route, q - queued route, r - rejected route\\n\\n\"";
+ push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\\n\"";
return join("", @lines);
}
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 71d0a46449..b549c11cfc 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -85,6 +85,7 @@ ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric"
ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP"
ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group"
+ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE"
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
diff --git a/lib/routemap.c b/lib/routemap.c
index 7749ea4cc7..fb70860024 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -263,6 +263,24 @@ void route_map_no_match_tag_hook(int (*func)(
rmap_match_set_hook.no_match_tag = func;
}
+/* set sr-te color */
+void route_map_set_srte_color_hook(int (*func)(struct vty *vty,
+ struct route_map_index *index,
+ const char *command,
+ const char *arg))
+{
+ rmap_match_set_hook.set_srte_color = func;
+}
+
+/* no set sr-te color */
+void route_map_no_set_srte_color_hook(int (*func)(struct vty *vty,
+ struct route_map_index *index,
+ const char *command,
+ const char *arg))
+{
+ rmap_match_set_hook.no_set_srte_color = func;
+}
+
/* set ip nexthop */
void route_map_set_ip_nexthop_hook(int (*func)(struct vty *vty,
struct route_map_index *index,
@@ -353,8 +371,7 @@ int generic_match_add(struct vty *vty, struct route_map_index *index,
"%% [%s] Argument form is unsupported or malformed.\n",
frr_protonameinst);
else
- zlog_warn("Argument form is unsupported or malformed: "
- "%s %s", command, arg);
+ zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg);
return CMD_WARNING_CONFIG_FAILED;
case RMAP_COMPILE_SUCCESS:
/*
@@ -405,8 +422,7 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index,
"%% [%s] Argument form is unsupported or malformed.\n",
frr_protonameinst);
else
- zlog_warn("Argument form is unsupported or malformed: "
- "%s %s", command, arg);
+ zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg);
retval = CMD_WARNING_CONFIG_FAILED;
break;
case RMAP_COMPILE_SUCCESS:
@@ -441,8 +457,7 @@ int generic_set_add(struct vty *vty, struct route_map_index *index,
"%% [%s] Argument form is unsupported or malformed.\n",
frr_protonameinst);
else
- zlog_warn("Argument form is unsupported or malformed: "
- "%s %s", command, arg);
+ zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg);
return CMD_WARNING_CONFIG_FAILED;
case RMAP_COMPILE_SUCCESS:
break;
@@ -470,8 +485,7 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index,
"%% [%s] Argument form is unsupported or malformed.\n",
frr_protonameinst);
else
- zlog_warn("Argument form is unsupported or malformed: "
- "%s %s", command, arg);
+ zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg);
return CMD_WARNING_CONFIG_FAILED;
case RMAP_COMPILE_SUCCESS:
break;
@@ -825,9 +839,10 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map)
struct route_map_index *index;
struct route_map_rule *rule;
- vty_out(vty, "route-map: %s Invoked: %" PRIu64 " Optimization: %s\n",
+ vty_out(vty, "route-map: %s Invoked: %" PRIu64 " Optimization: %s Processed Change: %s\n",
map->name, map->applied - map->applied_clear,
- map->optimization_disabled ? "disabled" : "enabled");
+ map->optimization_disabled ? "disabled" : "enabled",
+ map->to_be_processed ? "true" : "false");
for (index = map->head; index; index = index->next) {
vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n",
@@ -1692,14 +1707,19 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
* more noops, we retain this return value and
* return this eventually if there are no
* matches.
+ * If a best match route-map index already
+ * exists, do not reset the match_ret.
*/
- if (*match_ret != RMAP_NOMATCH)
+ if (!best_index && (*match_ret != RMAP_NOMATCH))
*match_ret = ret;
} else {
/*
* ret is RMAP_NOMATCH.
+ * If a best match route-map index already
+ * exists, do not reset the match_ret.
*/
- *match_ret = ret;
+ if (!best_index)
+ *match_ret = ret;
}
}
@@ -2411,6 +2431,7 @@ route_map_result_t route_map_apply(struct route_map *map,
for (; index; index = index->next) {
if (!skip_match_clause) {
+ index->applied++;
/* Apply this index. */
match_ret = route_map_apply_match(&index->match_list,
prefix, type, object);
@@ -2610,6 +2631,47 @@ static unsigned int route_map_dep_data_hash_make_key(const void *p)
return string_hash_make(dep_data->rname);
}
+DEFUN (set_srte_color,
+ set_srte_color_cmd,
+ "set sr-te color [(1-4294967295)]",
+ SET_STR
+ SRTE_STR
+ SRTE_COLOR_STR
+ "Color of the SR-TE Policies to match with\n")
+{
+ VTY_DECLVAR_CONTEXT(route_map_index, index);
+ int idx = 0;
+ char *arg = argv_find(argv, argc, "(1-4294967295)", &idx)
+ ? argv[idx]->arg
+ : NULL;
+
+ if (rmap_match_set_hook.set_srte_color)
+ return rmap_match_set_hook.set_srte_color(vty, index,
+ "sr-te color", arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_set_srte_color,
+ no_set_srte_color_cmd,
+ "no set sr-te color [(1-4294967295)]",
+ NO_STR
+ SET_STR
+ SRTE_STR
+ SRTE_COLOR_STR
+ "Color of the SR-TE Policies to match with\n")
+{
+ VTY_DECLVAR_CONTEXT(route_map_index, index);
+ int idx = 0;
+ char *arg = argv_find(argv, argc, "(1-4294967295)", &idx)
+ ? argv[idx]->arg
+ : NULL;
+
+ if (rmap_match_set_hook.no_set_srte_color)
+ return rmap_match_set_hook.no_set_srte_color(
+ vty, index, "sr-te color", arg);
+ return CMD_SUCCESS;
+}
+
static void *route_map_dep_hash_alloc(void *p)
{
char *dep_name = (char *)p;
@@ -3234,5 +3296,8 @@ void route_map_init(void)
install_element(RMAP_NODE, &routemap_optimization_cmd);
install_element(RMAP_NODE, &no_routemap_optimization_cmd);
+ install_element(RMAP_NODE, &set_srte_color_cmd);
+ install_element(RMAP_NODE, &no_set_srte_color_cmd);
+
install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
}
diff --git a/lib/routemap.h b/lib/routemap.h
index 62195b8349..64da4b87ef 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -424,6 +424,14 @@ extern void route_map_match_tag_hook(int (*func)(
extern void route_map_no_match_tag_hook(int (*func)(
struct vty *vty, struct route_map_index *index, const char *command,
const char *arg, route_map_event_t type));
+/* set sr-te color */
+extern void route_map_set_srte_color_hook(
+ int (*func)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg));
+/* no set sr-te color */
+extern void route_map_no_set_srte_color_hook(
+ int (*func)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg));
/* set ip nexthop */
extern void route_map_set_ip_nexthop_hook(
int (*func)(struct vty *vty, struct route_map_index *index,
@@ -606,6 +614,14 @@ struct route_map_match_set_hooks {
const char *command, const char *arg,
route_map_event_t type);
+ /* set sr-te color */
+ int (*set_srte_color)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg);
+
+ /* no set sr-te color */
+ int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg);
+
/* set ip nexthop */
int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
const char *command, const char *arg);
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 2c45f09751..836be38113 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -39,7 +39,7 @@
#define ROUTE_MAP_SEQUENCE_CMD_STR \
"Sequence to insert to/delete from existing route-map entry\n"
-DEFPY_NOSH(
+DEFPY_YANG_NOSH(
route_map, route_map_cmd,
"route-map WORD$name <deny|permit>$action (1-65535)$sequence",
ROUTE_MAP_CMD_STR
@@ -70,6 +70,7 @@ DEFPY_NOSH(
VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
/* Add support for non-migrated route map users. */
+ nb_cli_pending_commit_check(vty);
rm = route_map_get(name);
action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY;
rmi = route_map_index_get(rm, action_type, sequence);
@@ -79,7 +80,7 @@ DEFPY_NOSH(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_route_map_all, no_route_map_all_cmd,
"no route-map WORD$name",
NO_STR
@@ -94,7 +95,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_route_map, no_route_map_cmd,
"no route-map WORD$name <deny|permit>$action (1-65535)$sequence",
NO_STR
@@ -179,7 +180,7 @@ void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
vty_out(vty, "!\n");
}
-DEFPY(
+DEFPY_YANG(
match_interface, match_interface_cmd,
"match interface IFNAME",
MATCH_STR
@@ -196,7 +197,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_interface, no_match_interface_cmd,
"no match interface [IFNAME]",
NO_STR
@@ -211,9 +212,9 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_address, match_ip_address_cmd,
- "match ip address <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
+ "match ip address <(1-199)|(1300-2699)|WORD>$name",
MATCH_STR
IP_STR
"Match address of route\n"
@@ -223,34 +224,15 @@ DEFPY(
{
const char *xpath = "./match-condition[condition='ipv4-address-list']";
char xpath_value[XPATH_MAXLEN + 32];
- int acln = acll ? acll : aclh;
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
- if (name) {
- snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
- xpath);
- nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
- } else /* if (acll || aclh) */ {
- if ((acln >= 1 && acln <= 99)
- || (acln >= 1300 && acln <= 1999)) {
- snprintf(xpath_value, sizeof(xpath_value),
- "%s/access-list-num", xpath);
- } else {
- /*
- * if ((acln >= 100 && acln <= 199)
- * || (acln >= 2000 && acln <= 2699))
- */
- snprintf(xpath_value, sizeof(xpath_value),
- "%s/access-list-num-extended", xpath);
- }
- nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
- acll_str ? acll_str : aclh_str);
- }
+ snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_address, no_match_ip_address_cmd,
"no match ip address [<(1-199)|(1300-2699)|WORD>]",
NO_STR
@@ -268,7 +250,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_address_prefix_list,
match_ip_address_prefix_list_cmd,
"match ip address prefix-list WORD$name",
@@ -288,7 +270,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
"no match ip address prefix-list [WORD]",
NO_STR
@@ -305,9 +287,9 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop, match_ip_next_hop_cmd,
- "match ip next-hop <(1-199)$acll|(1300-2699)$aclh|WORD$name>",
+ "match ip next-hop <(1-199)|(1300-2699)|WORD>$name",
MATCH_STR
IP_STR
"Match next-hop address of route\n"
@@ -317,34 +299,15 @@ DEFPY(
{
const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
char xpath_value[XPATH_MAXLEN + 32];
- int acln = acll ? acll : aclh;
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
- if (name) {
- snprintf(xpath_value, sizeof(xpath_value), "%s/list-name",
- xpath);
- nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
- } else /* if (acll || aclh) */ {
- if ((acln >= 1 && acln <= 99)
- || (acln >= 1300 && acln <= 1999)) {
- snprintf(xpath_value, sizeof(xpath_value),
- "%s/access-list-num", xpath);
- } else {
- /*
- * if ((acln >= 100 && acln <= 199)
- * || (acln >= 2000 && acln <= 2699))
- */
- snprintf(xpath_value, sizeof(xpath_value),
- "%s/access-list-num-extended", xpath);
- }
- nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
- acll_str ? acll_str : aclh_str);
- }
+ snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop, no_match_ip_next_hop_cmd,
"no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
NO_STR
@@ -362,7 +325,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop_prefix_list,
match_ip_next_hop_prefix_list_cmd,
"match ip next-hop prefix-list WORD$name",
@@ -383,7 +346,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop_prefix_list,
no_match_ip_next_hop_prefix_list_cmd,
"no match ip next-hop prefix-list [WORD]",
@@ -402,7 +365,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop_type, match_ip_next_hop_type_cmd,
"match ip next-hop type <blackhole>$type",
MATCH_STR
@@ -422,7 +385,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
"no match ip next-hop type [<blackhole>]",
NO_STR MATCH_STR IP_STR
@@ -437,7 +400,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_address, match_ipv6_address_cmd,
"match ipv6 address WORD$name",
MATCH_STR
@@ -455,7 +418,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_address, no_match_ipv6_address_cmd,
"no match ipv6 address [WORD]",
NO_STR
@@ -471,7 +434,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
"match ipv6 address prefix-list WORD$name",
MATCH_STR
@@ -490,7 +453,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_address_prefix_list,
no_match_ipv6_address_prefix_list_cmd,
"no match ipv6 address prefix-list [WORD]",
@@ -508,7 +471,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
"match ipv6 next-hop type <blackhole>$type",
MATCH_STR IPV6_STR
@@ -527,7 +490,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
"no match ipv6 next-hop type [<blackhole>]",
NO_STR MATCH_STR IPV6_STR
@@ -542,7 +505,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_metric, match_metric_cmd,
"match metric (0-4294967295)$metric",
MATCH_STR
@@ -559,7 +522,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_metric, no_match_metric_cmd,
"no match metric [(0-4294967295)]",
NO_STR
@@ -574,7 +537,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_tag, match_tag_cmd,
"match tag (1-4294967295)$tag",
MATCH_STR
@@ -591,7 +554,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_tag, no_match_tag_cmd,
"no match tag [(1-4294967295)]",
NO_STR
@@ -610,8 +573,6 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
int condition = yang_dnode_get_enum(dnode, "./condition");
- struct lyd_node *ln;
- const char *acl;
switch (condition) {
case 0: /* interface */
@@ -620,25 +581,14 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
break;
case 1: /* ipv4-address-list */
case 3: /* ipv4-next-hop-list */
- acl = NULL;
- if ((ln = yang_dnode_get(dnode, "./list-name")) != NULL)
- acl = yang_dnode_get_string(ln, NULL);
- else if ((ln = yang_dnode_get(dnode, "./access-list-num"))
- != NULL)
- acl = yang_dnode_get_string(ln, NULL);
- else if ((ln = yang_dnode_get(dnode,
- "./access-list-num-extended"))
- != NULL)
- acl = yang_dnode_get_string(ln, NULL);
-
- assert(acl);
-
switch (condition) {
case 1:
- vty_out(vty, " match ip address %s\n", acl);
+ vty_out(vty, " match ip address %s\n",
+ yang_dnode_get_string(dnode, "./list-name"));
break;
case 3:
- vty_out(vty, " match ip next-hop %s\n", acl);
+ vty_out(vty, " match ip next-hop %s\n",
+ yang_dnode_get_string(dnode, "./list-name"));
break;
}
break;
@@ -697,7 +647,7 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
set_ip_nexthop, set_ip_nexthop_cmd,
"set ip next-hop A.B.C.D$addr",
SET_STR
@@ -715,7 +665,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_ip_nexthop, no_set_ip_nexthop_cmd,
"no set ip next-hop [A.B.C.D]",
NO_STR
@@ -731,7 +681,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd,
"set ipv6 next-hop local X:X::X:X$addr",
SET_STR
@@ -750,7 +700,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd,
"no set ipv6 next-hop local [X:X::X:X]",
NO_STR
@@ -767,7 +717,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_metric, set_metric_cmd,
"set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>",
SET_STR
@@ -813,7 +763,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_metric, no_set_metric_cmd,
"no set metric [(0-4294967295)]",
NO_STR
@@ -827,7 +777,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_tag, set_tag_cmd,
"set tag (1-4294967295)$tag",
SET_STR
@@ -844,7 +794,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_tag, no_set_tag_cmd,
"no set tag [(1-4294967295)]",
NO_STR
@@ -904,7 +854,7 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
rmap_onmatch_next, rmap_onmatch_next_cmd,
"on-match next",
"Exit policy on matches\n"
@@ -915,7 +865,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_onmatch_next,
no_rmap_onmatch_next_cmd,
"no on-match next",
@@ -928,7 +878,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
rmap_onmatch_goto, rmap_onmatch_goto_cmd,
"on-match goto (1-65535)$rm_num",
"Exit policy on matches\n"
@@ -941,7 +891,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd,
"no on-match goto",
NO_STR
@@ -954,13 +904,13 @@ DEFPY(
}
/* Cisco/GNU Zebra compatibility aliases */
-ALIAS(
+ALIAS_YANG(
rmap_onmatch_goto, rmap_continue_cmd,
"continue (1-65535)$rm_num",
"Continue on a different entry within the route-map\n"
"Route-map entry sequence number\n")
-ALIAS(
+ALIAS_YANG(
no_rmap_onmatch_goto, no_rmap_continue_cmd,
"no continue [(1-65535)]",
NO_STR
@@ -986,7 +936,7 @@ void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
rmap_call, rmap_call_cmd,
"call WORD$name",
"Jump to another Route-Map after match+set\n"
@@ -997,11 +947,12 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_call, no_rmap_call_cmd,
- "no call",
+ "no call [NAME]",
NO_STR
- "Jump to another Route-Map after match+set\n")
+ "Jump to another Route-Map after match+set\n"
+ "Target route-map name\n")
{
nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL);
@@ -1014,7 +965,7 @@ void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL));
}
-DEFPY(
+DEFPY_YANG(
rmap_description, rmap_description_cmd,
"description LINE...",
"Route-map comment\n"
@@ -1031,7 +982,7 @@ DEFPY(
return rv;
}
-DEFUN (no_rmap_description,
+DEFUN_YANG (no_rmap_description,
no_rmap_description_cmd,
"no description",
NO_STR
diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c
index f500a6c408..967f3fd4d4 100644
--- a/lib/routemap_northbound.c
+++ b/lib/routemap_northbound.c
@@ -516,77 +516,6 @@ static int lib_route_map_entry_match_condition_interface_destroy(
}
/*
- * XPath: /frr-route-map:lib/route-map/entry/match-condition/access-list-num
- */
-static int lib_route_map_entry_match_condition_access_list_num_modify(
- struct nb_cb_modify_args *args)
-{
- struct routemap_hook_context *rhc;
- const char *acl;
- int condition, rv;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- /* Check for hook function. */
- rv = CMD_SUCCESS;
- acl = yang_dnode_get_string(args->dnode, NULL);
- rhc = nb_running_get_entry(args->dnode, NULL, true);
- condition = yang_dnode_get_enum(args->dnode, "../condition");
- switch (condition) {
- case 1: /* ipv4-address-list */
- if (rmap_match_set_hook.match_ip_address == NULL)
- break;
- rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address;
- rhc->rhc_rule = "ip address";
- rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
- rv = rmap_match_set_hook.match_ip_address(
- NULL, rhc->rhc_rmi, "ip address", acl,
- RMAP_EVENT_FILTER_ADDED);
- break;
- case 3: /* ipv4-next-hop-list */
- if (rmap_match_set_hook.match_ip_next_hop == NULL)
- break;
- rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop;
- rhc->rhc_rule = "ip next-hop";
- rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
- rv = rmap_match_set_hook.match_ip_next_hop(
- NULL, rhc->rhc_rmi, "ip next-hop", acl,
- RMAP_EVENT_FILTER_ADDED);
- break;
- }
- if (rv != CMD_SUCCESS) {
- rhc->rhc_mhook = NULL;
- return NB_ERR_INCONSISTENCY;
- }
-
- return NB_OK;
-}
-
-static int lib_route_map_entry_match_condition_access_list_num_destroy(
- struct nb_cb_destroy_args *args)
-{
- return lib_route_map_entry_match_destroy(args);
-}
-
-/*
- * XPath:
- * /frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended
- */
-static int lib_route_map_entry_match_condition_access_list_num_extended_modify(
- struct nb_cb_modify_args *args)
-{
- return lib_route_map_entry_match_condition_access_list_num_modify(args);
-}
-
-static int lib_route_map_entry_match_condition_access_list_num_extended_destroy(
- struct nb_cb_destroy_args *args)
-{
- return lib_route_map_entry_match_condition_access_list_num_destroy(
- args);
-}
-
-/*
* XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name
*/
static int lib_route_map_entry_match_condition_list_name_modify(
@@ -1245,20 +1174,6 @@ const struct frr_yang_module_info frr_route_map_info = {
}
},
{
- .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num",
- .cbs = {
- .modify = lib_route_map_entry_match_condition_access_list_num_modify,
- .destroy = lib_route_map_entry_match_condition_access_list_num_destroy,
- }
- },
- {
- .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended",
- .cbs = {
- .modify = lib_route_map_entry_match_condition_access_list_num_extended_modify,
- .destroy = lib_route_map_entry_match_condition_access_list_num_extended_destroy,
- }
- },
- {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name",
.cbs = {
.modify = lib_route_map_entry_match_condition_list_name_modify,
diff --git a/lib/routing_nb.c b/lib/routing_nb.c
new file mode 100644
index 0000000000..0160354a7e
--- /dev/null
+++ b/lib/routing_nb.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ *
+ * 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
+ */
+#include "northbound.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+
+
+
+/* clang-format off */
+const struct frr_yang_module_info frr_routing_info = {
+ .name = "frr-routing",
+ .nodes = {
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_destroy,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/routing_nb.h b/lib/routing_nb.h
new file mode 100644
index 0000000000..d1b59ea29e
--- /dev/null
+++ b/lib/routing_nb.h
@@ -0,0 +1,24 @@
+#ifndef _FRR_ROUTING_NB_H_
+#define _FRR_ROUTING_NB_H_
+
+extern const struct frr_yang_module_info frr_routing_info;
+
+/* Mandatory callbacks. */
+int routing_control_plane_protocols_control_plane_protocol_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_destroy(
+ struct nb_cb_destroy_args *args);
+
+#define FRR_ROUTING_XPATH \
+ "/frr-routing:routing/control-plane-protocols/control-plane-protocol"
+
+#define FRR_ROUTING_KEY_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']"
+/*
+ * callbacks for routing to handle configuration events
+ * based on the control plane protocol
+ */
+DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args))
+
+#endif /* _FRR_ROUTING_NB_H_ */
diff --git a/lib/routing_nb_config.c b/lib/routing_nb_config.c
new file mode 100644
index 0000000000..b789e8494e
--- /dev/null
+++ b/lib/routing_nb_config.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ *
+ * 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
+ */
+
+#include "northbound.h"
+#include "libfrr.h"
+#include "vrf.h"
+#include "lib_errors.h"
+#include "routing_nb.h"
+
+
+DEFINE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args))
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol
+ */
+
+int routing_control_plane_protocols_control_plane_protocol_create(
+ struct nb_cb_create_args *args)
+{
+ struct vrf *vrf;
+ const char *vrfname;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (hook_call(routing_conf_event, args))
+ return NB_ERR_VALIDATION;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ vrfname = yang_dnode_get_string(args->dnode, "./vrf");
+ vrf = vrf_lookup_by_name(vrfname);
+ vrf = vrf ? vrf : vrf_get(VRF_UNKNOWN, vrfname);
+ if (!vrf) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "vrf creation %s failed", vrfname);
+ return NB_ERR;
+ }
+ nb_running_set_entry(args->dnode, vrf);
+ break;
+ };
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct vrf *vrf __attribute__((unused));
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrf = nb_running_unset_entry(args->dnode);
+
+ return NB_OK;
+}
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 45d9008796..21fddcd01d 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -264,8 +264,7 @@ int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr,
* up */
char buf[1][INET_ADDRSTRLEN];
zlog_info(
- "setsockopt_ipv4_multicast attempting to drop and "
- "re-add (fd %d, mcast %s, ifindex %u)",
+ "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);
@@ -306,8 +305,7 @@ int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr,
* up */
char buf[1][INET_ADDRSTRLEN];
zlog_info(
- "setsockopt_ipv4_multicast attempting to drop and "
- "re-add (fd %d, mcast %s, ifindex %u)",
+ "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);
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 63d8a8c69b..d77229797c 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -27,6 +27,7 @@
#include "log.h"
#include "jhash.h"
#include "lib_errors.h"
+#include "printfrr.h"
DEFINE_MTYPE_STATIC(LIB, SOCKUNION, "Socket union")
@@ -406,8 +407,7 @@ int sockopt_v6only(int family, int sock)
sizeof(int));
if (ret < 0) {
flog_err(EC_LIB_SOCKET,
- "can't set sockopt IPV6_V6ONLY "
- "to socket %d",
+ "can't set sockopt IPV6_V6ONLY to socket %d",
sock);
return -1;
}
@@ -666,3 +666,49 @@ void sockunion_init(union sockunion *su)
{
memset(su, 0, sizeof(union sockunion));
}
+
+printfrr_ext_autoreg_p("SU", printfrr_psu)
+static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
+ int prec, const void *ptr)
+{
+ const union sockunion *su = ptr;
+ struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
+ bool include_port = false;
+ bool endflags = false;
+ ssize_t consumed = 2;
+
+ while (!endflags) {
+ switch (fmt[consumed++]) {
+ case 'p':
+ include_port = true;
+ break;
+ default:
+ consumed--;
+ endflags = true;
+ break;
+ }
+ };
+
+ switch (sockunion_family(su)) {
+ case AF_UNSPEC:
+ bprintfrr(&fb, "(unspec)");
+ break;
+ case AF_INET:
+ inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz);
+ fb.pos += strlen(fb.buf);
+ if (include_port)
+ bprintfrr(&fb, ":%d", su->sin.sin_port);
+ break;
+ case AF_INET6:
+ inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz);
+ fb.pos += strlen(fb.buf);
+ if (include_port)
+ bprintfrr(&fb, ":%d", su->sin6.sin6_port);
+ break;
+ default:
+ bprintfrr(&fb, "(af %d)", sockunion_family(su));
+ }
+
+ fb.pos[0] = '\0';
+ return consumed;
+}
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 7091c1b5e7..72f12b77ca 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -103,6 +103,10 @@ extern union sockunion *sockunion_dup(const union sockunion *);
extern void sockunion_free(union sockunion *);
extern void sockunion_init(union sockunion *);
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pSU" (union sockunion *)
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c
index 66b735919b..8ffa0e9709 100644
--- a/lib/srcdest_table.c
+++ b/lib/srcdest_table.c
@@ -317,3 +317,13 @@ static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt,
srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz);
return 2;
}
+
+struct route_table *srcdest_srcnode_table(struct route_node *rn)
+{
+ if (rnode_is_dstnode(rn)) {
+ struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn);
+
+ return srn->src_table;
+ }
+ return NULL;
+}
diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h
index 7982260777..79afef9bb0 100644
--- a/lib/srcdest_table.h
+++ b/lib/srcdest_table.h
@@ -100,6 +100,8 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn)
return route_table_get_info(srcdest_rnode_table(rn));
}
+extern struct route_table *srcdest_srcnode_table(struct route_node *rn);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/srte.h b/lib/srte.h
new file mode 100644
index 0000000000..d468c1cac9
--- /dev/null
+++ b/lib/srte.h
@@ -0,0 +1,56 @@
+/*
+ * SR-TE definitions
+ * Copyright 2020 NetDef Inc.
+ * Sascha Kattelmann
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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
+ */
+
+#ifndef _FRR_SRTE_H
+#define _FRR_SRTE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SRTE_POLICY_NAME_MAX_LENGTH 64
+
+enum zebra_sr_policy_status {
+ ZEBRA_SR_POLICY_UP = 0,
+ ZEBRA_SR_POLICY_DOWN,
+};
+
+static inline int sr_policy_compare(const struct ipaddr *a_endpoint,
+ const struct ipaddr *b_endpoint,
+ uint32_t a_color, uint32_t b_color)
+{
+ int ret;
+
+ ret = ipaddr_cmp(a_endpoint, b_endpoint);
+ if (ret < 0)
+ return -1;
+ if (ret > 0)
+ return 1;
+
+ return a_color - b_color;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_SRTE_H */
diff --git a/lib/stream.c b/lib/stream.c
index 17520f978e..768114e69b 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -55,15 +55,19 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO")
* using stream_put..._at() functions.
*/
#define STREAM_WARN_OFFSETS(S) \
- flog_warn(EC_LIB_STREAM, \
- "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
- (void *)(S), (unsigned long)(S)->size, \
- (unsigned long)(S)->getp, (unsigned long)(S)->endp)
+ do { \
+ flog_warn(EC_LIB_STREAM, \
+ "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
+ (void *)(S), (unsigned long)(S)->size, \
+ (unsigned long)(S)->getp, (unsigned long)(S)->endp); \
+ zlog_backtrace(LOG_WARNING); \
+ } while (0)
#define STREAM_VERIFY_SANE(S) \
do { \
- if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) \
+ 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)
@@ -582,6 +586,43 @@ uint32_t stream_get_ipv4(struct stream *s)
return l;
}
+bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip)
+{
+ uint16_t ipa_len;
+
+ STREAM_VERIFY_SANE(s);
+
+ /* Get address type. */
+ if (STREAM_READABLE(s) < sizeof(uint16_t)) {
+ STREAM_BOUND_WARN2(s, "get ipaddr");
+ return false;
+ }
+ ip->ipa_type = stream_getw(s);
+
+ /* Get address value. */
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ ipa_len = IPV4_MAX_BYTELEN;
+ break;
+ case IPADDR_V6:
+ ipa_len = IPV6_MAX_BYTELEN;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown ip address-family: %u", __func__,
+ ip->ipa_type);
+ return false;
+ }
+ if (STREAM_READABLE(s) < ipa_len) {
+ STREAM_BOUND_WARN2(s, "get ipaddr");
+ return false;
+ }
+ memcpy(&ip->ip, s->data + s->getp, ipa_len);
+ s->getp += ipa_len;
+
+ return true;
+}
+
float stream_getf(struct stream *s)
{
union {
@@ -848,6 +889,27 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
return sizeof(uint32_t);
}
+bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip)
+{
+ stream_putw(s, ip->ipa_type);
+
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ stream_put_in_addr(s, &ip->ipaddr_v4);
+ break;
+ case IPADDR_V6:
+ stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown ip address-family: %u", __func__,
+ ip->ipa_type);
+ return false;
+ }
+
+ return true;
+}
+
/* Put in_addr at location in the stream. */
int stream_put_in_addr_at(struct stream *s, size_t putp,
const struct in_addr *addr)
diff --git a/lib/stream.h b/lib/stream.h
index 245f35db51..1250b6944d 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -189,6 +189,7 @@ 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 *, uint32_t);
extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr);
+extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip);
extern int stream_put_in_addr_at(struct stream *s, size_t putp,
const struct in_addr *addr);
extern int stream_put_in6_addr_at(struct stream *s, size_t putp,
@@ -219,6 +220,7 @@ extern uint64_t stream_getq(struct stream *);
extern uint64_t stream_getq_from(struct stream *, size_t);
bool stream_getq2(struct stream *s, uint64_t *q);
extern uint32_t stream_get_ipv4(struct stream *);
+extern bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip);
/* IEEE-754 floats */
extern float stream_getf(struct stream *);
@@ -439,6 +441,12 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out)
(P) = _pval; \
} while (0)
+#define STREAM_GET_IPADDR(S, P) \
+ do { \
+ if (!stream_get_ipaddr((S), (P))) \
+ goto stream_failure; \
+ } while (0)
+
#define STREAM_GET(P, STR, SIZE) \
do { \
if (!stream_get2((P), (STR), (SIZE))) \
diff --git a/lib/subdir.am b/lib/subdir.am
index 57b2cea832..1feaa56d13 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -108,6 +108,8 @@ lib_libfrr_la_SOURCES = \
lib/printf/printf-pos.c \
lib/printf/vfprintf.c \
lib/printf/glue.c \
+ lib/routing_nb.c \
+ lib/routing_nb_config.c \
# end
nodist_lib_libfrr_la_SOURCES = \
@@ -117,13 +119,11 @@ nodist_lib_libfrr_la_SOURCES = \
yang/frr-route-types.yang.c \
yang/frr-vrf.yang.c \
yang/frr-routing.yang.c \
+ yang/frr-nexthop.yang.c \
yang/ietf/ietf-routing-types.yang.c \
yang/ietf/ietf-interfaces.yang.c \
yang/frr-module-translator.yang.c \
yang/frr-nexthop.yang.c \
- yang/frr-igmp.yang.c \
- yang/frr-pim.yang.c \
- yang/frr-pim-rp.yang.c \
# end
vtysh_scan += \
@@ -242,6 +242,7 @@ pkginclude_HEADERS += \
lib/sockunion.h \
lib/spf_backoff.h \
lib/srcdest_table.h \
+ lib/srte.h \
lib/stream.h \
lib/systemd.h \
lib/table.h \
@@ -266,6 +267,7 @@ pkginclude_HEADERS += \
lib/zlog.h \
lib/zlog_targets.h \
lib/pbr.h \
+ lib/routing_nb.h \
# end
diff --git a/lib/thread.c b/lib/thread.c
index 9b2f5661ac..19e4827283 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -305,8 +305,7 @@ DEFUN (show_thread_cpu,
filter = parse_filter(argv[idx]->arg);
if (!filter) {
vty_out(vty,
- "Invalid filter \"%s\" specified; must contain at least"
- "one of 'RWTEXB'\n",
+ "Invalid filter \"%s\" specified; must contain at leastone of 'RWTEXB'\n",
argv[idx]->arg);
return CMD_WARNING;
}
@@ -393,8 +392,7 @@ DEFUN (clear_thread_cpu,
filter = parse_filter(argv[idx]->arg);
if (!filter) {
vty_out(vty,
- "Invalid filter \"%s\" specified; must contain at least"
- "one of 'RWTEXB'\n",
+ "Invalid filter \"%s\" specified; must contain at leastone of 'RWTEXB'\n",
argv[idx]->arg);
return CMD_WARNING;
}
@@ -636,6 +634,36 @@ struct timeval thread_timer_remain(struct thread *thread)
return remain;
}
+static int 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;
+}
+
+char *thread_timer_to_hhmmss(char *buf, int buf_size,
+ struct thread *t_timer)
+{
+ if (t_timer) {
+ time_hhmmss(buf, buf_size,
+ thread_timer_remain_second(t_timer));
+ } else {
+ snprintf(buf, buf_size, "--:--:--");
+ }
+ return buf;
+}
+
/* Get new thread. */
static struct thread *thread_get(struct thread_master *m, uint8_t type,
int (*func)(struct thread *), void *arg,
@@ -698,17 +726,16 @@ static void thread_free(struct thread_master *master, struct thread *thread)
static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
nfds_t count, const struct timeval *timer_wait)
{
- /* If timer_wait is null here, that means poll() should block
- * indefinitely,
- * unless the thread_master has overridden it by setting
+ /*
+ * If timer_wait is null here, that means poll() should block
+ * indefinitely, unless the thread_master has overridden it by setting
* ->selectpoll_timeout.
+ *
* If the value is positive, it specifies the maximum number of
- * milliseconds
- * to wait. If the timeout is -1, it specifies that we should never wait
- * and
- * always return immediately even if no event is detected. If the value
- * is
- * zero, the behavior is default. */
+ * milliseconds to wait. If the timeout is -1, it specifies that
+ * we should never wait and always return immediately even if no
+ * event is detected. If the value is zero, the behavior is default.
+ */
int timeout = -1;
/* number of file descriptors with events */
@@ -832,7 +859,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
frr_with_mutex(&m->mtx) {
if (t_ptr && *t_ptr)
- // thread is already scheduled; don't reschedule
+ /* thread is already scheduled; don't reschedule */
return NULL;
thread = thread_get(m, type, func, arg, debugargpass);
@@ -912,7 +939,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m,
frr_with_mutex(&m->mtx) {
if (t_ptr && *t_ptr)
- // thread is already scheduled; don't reschedule
+ /* thread is already scheduled; don't reschedule */
break;
thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass);
@@ -1019,11 +1046,12 @@ static void do_thread_cancel(struct thread_master *master)
struct cancel_req *cr;
struct listnode *ln;
for (ALL_LIST_ELEMENTS_RO(master->cancel_req, ln, cr)) {
- /* If this is an event object cancellation, linear search
- * through event
- * list deleting any events which have the specified argument.
- * We also
- * need to check every thread in the ready queue. */
+ /*
+ * If this is an event object cancellation, linear search
+ * through event list deleting any events which have the
+ * specified argument. We also need to check every thread
+ * in the ready queue.
+ */
if (cr->eventobj) {
struct thread *t;
@@ -1047,11 +1075,12 @@ static void do_thread_cancel(struct thread_master *master)
continue;
}
- /* The pointer varies depending on whether the cancellation
- * request was
- * made asynchronously or not. If it was, we need to check
- * whether the
- * thread even exists anymore before cancelling it. */
+ /*
+ * The pointer varies depending on whether the cancellation
+ * request was made asynchronously or not. If it was, we
+ * need to check whether the thread even exists anymore
+ * before cancelling it.
+ */
thread = (cr->thread) ? cr->thread : *cr->threadref;
if (!thread)
@@ -1276,18 +1305,21 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
ready++;
- /* Unless someone has called thread_cancel from another pthread,
- * the only
- * thing that could have changed in m->handler.pfds while we
- * were
- * asleep is the .events field in a given pollfd. Barring
- * thread_cancel()
- * that value should be a superset of the values we have in our
- * copy, so
- * there's no need to update it. Similarily, barring deletion,
- * the fd
- * should still be a valid index into the master's pfds. */
- if (pfds[i].revents & (POLLIN | POLLHUP)) {
+ /*
+ * Unless someone has called thread_cancel from another
+ * pthread, the only thing that could have changed in
+ * m->handler.pfds while we were asleep is the .events
+ * field in a given pollfd. Barring thread_cancel() that
+ * value should be a superset of the values we have in our
+ * copy, so there's no need to update it. Similarily,
+ * barring deletion, the fd should still be a valid index
+ * into the master's pfds.
+ *
+ * We are including POLLERR here to do a READ event
+ * this is because the read should fail and the
+ * read function should handle it appropriately
+ */
+ if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN,
pfds[i].revents, i);
}
@@ -1399,11 +1431,10 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
*
* - If there are events pending, set the poll() timeout to zero
* - If there are no events pending, but there are timers
- * pending, set the
- * timeout to the smallest remaining time on any timer
+ * pending, set the timeout to the smallest remaining time on
+ * any timer.
* - If there are neither timers nor events pending, but there
- * are file
- * descriptors pending, block indefinitely in poll()
+ * are file descriptors pending, block indefinitely in poll()
* - If nothing is pending, it's time for the application to die
*
* In every case except the last, we need to hit poll() at least
diff --git a/lib/thread.h b/lib/thread.h
index 412a4d93bf..c22b2105cd 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -140,6 +140,8 @@ struct cpu_thread_history {
/* Thread yield time. */
#define THREAD_YIELD_TIME_SLOT 10 * 1000L /* 10ms */
+#define THREAD_TIMER_STRLEN 12
+
/* Macros. */
#define THREAD_ARG(X) ((X)->arg)
#define THREAD_FD(X) ((X)->u.fd)
@@ -228,6 +230,8 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
/* only for use in logging functions! */
extern pthread_key_t thread_current;
+extern char *thread_timer_to_hhmmss(char *buf, int buf_size,
+ struct thread *t_timer);
#ifdef __cplusplus
}
diff --git a/lib/vrf.c b/lib/vrf.c
index fb64589287..2a3ce2a315 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -638,6 +638,7 @@ int vrf_handler_create(struct vty *vty, const char *vrfname,
ret = nb_cli_apply_changes(vty, xpath_list);
if (ret == CMD_SUCCESS) {
VTY_PUSH_XPATH(VRF_NODE, xpath_list);
+ nb_cli_pending_commit_check(vty);
vrfp = vrf_lookup_by_name(vrfname);
if (vrfp)
VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
@@ -652,8 +653,7 @@ int vrf_handler_create(struct vty *vty, const char *vrfname,
}
int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
- ns_id_t ns_id, ns_id_t internal_ns_id,
- ns_id_t rel_def_ns_id)
+ ns_id_t ns_id, ns_id_t internal_ns_id)
{
struct ns *ns = NULL;
@@ -691,8 +691,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
return CMD_SUCCESS;
if (vty)
vty_out(vty,
- "NS %s is already configured"
- " with VRF %u(%s)\n",
+ "NS %s is already configured with VRF %u(%s)\n",
ns->name, vrf2->vrf_id, vrf2->name);
else
zlog_info("NS %s is already configured with VRF %u(%s)",
@@ -701,7 +700,6 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
}
ns = ns_get_created(ns, pathname, ns_id);
ns->internal_ns_id = internal_ns_id;
- ns->relative_default_ns = rel_def_ns_id;
ns->vrf_ctxt = (void *)vrf;
vrf->ns_ctxt = (void *)ns;
/* update VRF netns NAME */
@@ -732,7 +730,7 @@ DEFUN_NOSH(vrf_exit,
return CMD_SUCCESS;
}
-DEFUN_NOSH (vrf,
+DEFUN_YANG_NOSH (vrf,
vrf_cmd,
"vrf NAME",
"Select a VRF to configure\n"
@@ -744,7 +742,7 @@ DEFUN_NOSH (vrf,
return vrf_handler_create(vty, vrfname, NULL);
}
-DEFUN (no_vrf,
+DEFUN_YANG (no_vrf,
no_vrf_cmd,
"no vrf NAME",
NO_STR
@@ -758,10 +756,8 @@ DEFUN (no_vrf,
vrfp = vrf_lookup_by_name(vrfname);
- if (vrfp == NULL) {
- vty_out(vty, "%% VRF %s does not exist\n", vrfname);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (vrfp == NULL)
+ return CMD_SUCCESS;
if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) {
vty_out(vty, "%% Only inactive VRFs can be deleted\n");
@@ -799,9 +795,7 @@ DEFUN_NOSH (vrf_netns,
frr_with_privs(vrf_daemon_privs) {
ret = vrf_netns_handler_create(vty, vrf, pathname,
- NS_UNKNOWN,
- NS_UNKNOWN,
- NS_UNKNOWN);
+ NS_UNKNOWN, NS_UNKNOWN);
}
return ret;
}
diff --git a/lib/vrf.h b/lib/vrf.h
index a8514d74ed..83ed16b48e 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -315,7 +315,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name,
*/
extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
char *pathname, ns_id_t ext_ns_id,
- ns_id_t ns_id, ns_id_t rel_def_ns_id);
+ ns_id_t ns_id);
/* used internally to enable or disable VRF.
* Notify a change in the VRF ID of the VRF
diff --git a/lib/vty.c b/lib/vty.c
index ffef05e4dc..184c7604b8 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1244,8 +1244,7 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
if (vty->sb_len != TELNET_NAWS_SB_LEN)
flog_err(
EC_LIB_SYSTEM_CALL,
- "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %lu",
+ "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu",
TELNET_NAWS_SB_LEN,
(unsigned long)vty->sb_len);
else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
@@ -1261,8 +1260,7 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
| vty->sb_buf[4]);
#ifdef TELNET_OPTION_DEBUG
vty_out(vty,
- "TELNET NAWS window size negotiation completed: "
- "width %d, height %d\n",
+ "TELNET NAWS window size negotiation completed: width %d, height %d\n",
vty->width, vty->height);
#endif
}
@@ -2633,6 +2631,9 @@ int vty_config_node_exit(struct vty *vty)
{
vty->xpath_index = 0;
+ /* Perform pending commit if any. */
+ nb_cli_pending_commit_check(vty);
+
/* Check if there's a pending confirmed commit. */
if (vty->t_confirmed_commit_timeout) {
vty_out(vty,
diff --git a/lib/vty.h b/lib/vty.h
index 5d5676199b..623f97ab03 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -134,6 +134,14 @@ struct vty {
/* Base candidate configuration. */
struct nb_config *candidate_config_base;
+ /* Dynamic transaction information. */
+ struct timeval backoff_start;
+ size_t backoff_cmd_count;
+ struct thread *t_pending_commit;
+ char *pending_cmds_buf;
+ size_t pending_cmds_buflen;
+ size_t pending_cmds_bufpos;
+
/* Confirmed-commit timeout and rollback configuration. */
struct thread *t_confirmed_commit_timeout;
struct nb_config *confirmed_commit_rollback;
diff --git a/lib/yang.c b/lib/yang.c
index 0714ddf7bb..6ab9492d52 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -53,22 +53,30 @@ static const char *yang_module_imp_clb(const char *mod_name,
{
struct yang_module_embed *e;
- if (submod_name || submod_rev)
- return NULL;
-
for (e = embeds; e; e = e->next) {
- if (strcmp(e->mod_name, mod_name))
- continue;
- if (mod_rev && strcmp(e->mod_rev, mod_rev))
- continue;
+ if (e->sub_mod_name && submod_name) {
+ if (strcmp(e->sub_mod_name, submod_name))
+ continue;
+
+ if (submod_rev && strcmp(e->sub_mod_rev, submod_rev))
+ continue;
+ } else {
+ if (strcmp(e->mod_name, mod_name))
+ continue;
+
+ if (mod_rev && strcmp(e->mod_rev, mod_rev))
+ continue;
+ }
*format = e->format;
return e->data;
}
- flog_warn(EC_LIB_YANG_MODULE_LOAD,
- "YANG model \"%s@%s\" not embedded, trying external file",
- mod_name, mod_rev ? mod_rev : "*");
+ flog_warn(
+ EC_LIB_YANG_MODULE_LOAD,
+ "YANG model \"%s@%s\" \"%s@%s\"not embedded, trying external file",
+ mod_name, mod_rev ? mod_rev : "*",
+ submod_name ? submod_name : "*", submod_rev ? submod_rev : "*");
return NULL;
}
@@ -749,3 +757,147 @@ void yang_terminate(void)
ly_ctx_destroy(ly_native_ctx, NULL);
}
+
+const struct lyd_node *yang_dnode_get_parent(const struct lyd_node *dnode,
+ const char *name)
+{
+ const struct lyd_node *orig_dnode = dnode;
+
+ while (orig_dnode) {
+ switch (orig_dnode->schema->nodetype) {
+ case LYS_LIST:
+ case LYS_CONTAINER:
+ if (!strcmp(orig_dnode->schema->name, name))
+ return orig_dnode;
+ break;
+ default:
+ break;
+ }
+
+ orig_dnode = orig_dnode->parent;
+ }
+
+ return NULL;
+}
+
+/* API to check if the given node is last node in the list */
+static bool yang_is_last_list_dnode(const struct lyd_node *dnode)
+{
+ return (((dnode->next == NULL)
+ || (dnode->next
+ && (strcmp(dnode->next->schema->name, dnode->schema->name)
+ != 0)))
+ && dnode->prev
+ && ((dnode->prev == dnode)
+ || (strcmp(dnode->prev->schema->name, dnode->schema->name)
+ != 0)));
+}
+
+/* API to check if the given node is last node in the data tree level */
+static bool yang_is_last_level_dnode(const struct lyd_node *dnode)
+{
+ const struct lyd_node *parent;
+ const struct lys_node_list *snode;
+ const struct lyd_node *key_leaf;
+ uint8_t keys_size;
+
+ switch (dnode->schema->nodetype) {
+ case LYS_LIST:
+ assert(dnode->parent);
+ parent = dnode->parent;
+ snode = (struct lys_node_list *)parent->schema;
+ key_leaf = dnode->prev;
+ for (keys_size = 1; keys_size < snode->keys_size; keys_size++)
+ key_leaf = key_leaf->prev;
+ if (key_leaf->prev == dnode)
+ return true;
+ break;
+ case LYS_CONTAINER:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+const struct lyd_node *
+yang_get_subtree_with_no_sibling(const struct lyd_node *dnode)
+{
+ bool parent = true;
+ const struct lyd_node *node;
+ const struct lys_node_container *snode;
+
+ node = dnode;
+ if (node->schema->nodetype != LYS_LIST)
+ return node;
+
+ while (parent) {
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ snode = (struct lys_node_container *)node->schema;
+ if ((!snode->presence)
+ && yang_is_last_level_dnode(node)) {
+ if (node->parent
+ && (node->parent->schema->module
+ == dnode->schema->module))
+ node = node->parent;
+ else
+ parent = false;
+ } else
+ parent = false;
+ break;
+ case LYS_LIST:
+ if (yang_is_last_list_dnode(node)
+ && yang_is_last_level_dnode(node)) {
+ if (node->parent
+ && (node->parent->schema->module
+ == dnode->schema->module))
+ node = node->parent;
+ else
+ parent = false;
+ } else
+ parent = false;
+ break;
+ default:
+ parent = false;
+ break;
+ }
+ }
+ return node;
+}
+
+uint32_t yang_get_list_pos(const struct lyd_node *node)
+{
+ return lyd_list_pos(node);
+}
+
+uint32_t yang_get_list_elements_count(const struct lyd_node *node)
+{
+ unsigned int count;
+ struct lys_node *schema;
+
+ if (!node
+ || ((node->schema->nodetype != LYS_LIST)
+ && (node->schema->nodetype != LYS_LEAFLIST))) {
+ return 0;
+ }
+
+ schema = node->schema;
+ count = 0;
+ do {
+ if (node->schema == schema)
+ ++count;
+ node = node->next;
+ } while (node);
+ return count;
+}
+
+
+const struct lyd_node *yang_dnode_get_child(const struct lyd_node *dnode)
+{
+ if (dnode)
+ return dnode->child;
+ return NULL;
+}
diff --git a/lib/yang.h b/lib/yang.h
index 85ef0d758c..cc048c44e8 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -34,7 +34,7 @@ extern "C" {
#endif
/* Maximum XPath length. */
-#define XPATH_MAXLEN 512
+#define XPATH_MAXLEN 1024
/* Maximum list key length. */
#define LIST_MAXKEYS 8
@@ -48,6 +48,8 @@ extern "C" {
struct yang_module_embed {
struct yang_module_embed *next;
const char *mod_name, *mod_rev;
+ const char *sub_mod_name;
+ const char *sub_mod_rev;
const char *data;
LYS_INFORMAT format;
};
@@ -551,6 +553,57 @@ extern void yang_init(bool embedded_modules);
*/
extern void yang_terminate(void);
+/*
+ * API to return the parent dnode having a given schema-node name
+ * Use case: One has to access the parent dnode's private pointer
+ * for a given child node.
+ * For that there is a need to find parent dnode first.
+ *
+ * dnode The starting node to work on
+ *
+ * name The name of container/list schema-node
+ *
+ * Returns The dnode matched with the given name
+ */
+extern const struct lyd_node *
+yang_dnode_get_parent(const struct lyd_node *dnode, const char *name);
+
+
+/*
+ * In some cases there is a need to auto delete the parent nodes
+ * if the given node is last in the list.
+ * It tries to delete all the parents in a given tree in a given module.
+ * The use case is with static routes and route maps
+ * example : ip route 1.1.1.1/32 ens33
+ * ip route 1.1.1.1/32 ens34
+ * After this no ip route 1.1.1.1/32 ens34 came, now staticd
+ * has to find out upto which level it has to delete the dnodes.
+ * For this case it has to send delete nexthop
+ * After this no ip route 1.1.1.1/32 ens33 came, now staticd has to
+ * clear nexthop, path and route nodes.
+ * The same scheme is required for routemaps also
+ * dnode The starting node to work on
+ *
+ * Returns The final parent node selected for deletion
+ */
+extern const struct lyd_node *
+yang_get_subtree_with_no_sibling(const struct lyd_node *dnode);
+
+/* To get the relative position of a node in list */
+extern uint32_t yang_get_list_pos(const struct lyd_node *node);
+
+/* To get the number of elements in a list
+ *
+ * dnode : The head of list
+ * Returns : The number of dnodes present in the list
+ */
+extern uint32_t yang_get_list_elements_count(const struct lyd_node *node);
+
+
+/* To get the immediate child of a dnode */
+const struct lyd_node *yang_dnode_get_child(const struct lyd_node *dnode);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index c31ba3fcc0..4c658c1bfb 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -792,6 +792,29 @@ struct yang_data *yang_data_new_empty(const char *xpath)
return yang_data_new(xpath, NULL);
}
+bool yang_dnode_get_empty(const struct lyd_node *dnode, const char *xpath_fmt,
+ ...)
+{
+ va_list ap;
+ char xpath[XPATH_MAXLEN];
+ const struct lyd_node_leaf_list *dleaf;
+
+ assert(dnode);
+
+ va_start(ap, xpath_fmt);
+ vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
+ va_end(ap);
+
+ dnode = yang_dnode_get(dnode, xpath);
+ if (dnode) {
+ dleaf = (const struct lyd_node_leaf_list *)dnode;
+ if (dleaf->value_type == LY_TYPE_EMPTY)
+ return true;
+ }
+
+ return false;
+}
+
/*
* Derived type: IP prefix.
*/
@@ -1191,3 +1214,63 @@ const char *yang_nexthop_type2str(uint32_t ntype)
break;
}
}
+
+
+const char *yang_afi_safi_value2identity(afi_t afi, safi_t safi)
+{
+ if (afi == AFI_IP && safi == SAFI_UNICAST)
+ return "frr-routing:ipv4-unicast";
+ if (afi == AFI_IP6 && safi == SAFI_UNICAST)
+ return "frr-routing:ipv6-unicast";
+ if (afi == AFI_IP && safi == SAFI_MULTICAST)
+ return "frr-routing:ipv4-multicast";
+ if (afi == AFI_IP6 && safi == SAFI_MULTICAST)
+ return "frr-routing:ipv6-multicast";
+ if (afi == AFI_IP && safi == SAFI_MPLS_VPN)
+ return "frr-routing:l3vpn-ipv4-unicast";
+ if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN)
+ return "frr-routing:l3vpn-ipv6-unicast";
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN)
+ return "frr-routing:l2vpn-evpn";
+ if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST)
+ return "frr-routing:ipv4-labeled-unicast";
+ if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST)
+ return "frr-routing:ipv6-labeled-unicast";
+
+ return NULL;
+}
+
+void yang_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi)
+{
+ if (strmatch(key, "frr-routing:ipv4-unicast")) {
+ *afi = AFI_IP;
+ *safi = SAFI_UNICAST;
+ } else if (strmatch(key, "frr-routing:ipv6-unicast")) {
+ *afi = AFI_IP6;
+ *safi = SAFI_UNICAST;
+ } else if (strmatch(key, "frr-routing:ipv4-multicast")) {
+ *afi = AFI_IP;
+ *safi = SAFI_MULTICAST;
+ } else if (strmatch(key, "frr-routing:ipv6-multicast")) {
+ *afi = AFI_IP6;
+ *safi = SAFI_MULTICAST;
+ } else if (strmatch(key, "frr-routing:l3vpn-ipv4-unicast")) {
+ *afi = AFI_IP;
+ *safi = SAFI_MPLS_VPN;
+ } else if (strmatch(key, "frr-routing:l3vpn-ipv6-unicast")) {
+ *afi = AFI_IP6;
+ *safi = SAFI_MPLS_VPN;
+ } else if (strmatch(key, "frr-routing:ipv4-labeled-unicast")) {
+ *afi = AFI_IP;
+ *safi = SAFI_LABELED_UNICAST;
+ } else if (strmatch(key, "frr-routing:ipv6-labeled-unicast")) {
+ *afi = AFI_IP6;
+ *safi = SAFI_LABELED_UNICAST;
+ } else if (strmatch(key, "frr-routing:l2vpn-evpn")) {
+ *afi = AFI_L2VPN;
+ *safi = SAFI_EVPN;
+ } else {
+ *afi = AFI_UNSPEC;
+ *safi = SAFI_UNSPEC;
+ }
+}
diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h
index ba2cf5139c..d781dfb1e4 100644
--- a/lib/yang_wrappers.h
+++ b/lib/yang_wrappers.h
@@ -120,6 +120,8 @@ extern void yang_get_default_string_buf(char *buf, size_t size,
/* empty */
extern struct yang_data *yang_data_new_empty(const char *xpath);
+extern bool yang_dnode_get_empty(const struct lyd_node *dnode,
+ const char *xpath_fmt, ...);
/* ip prefix */
extern void yang_str2prefix(const char *value, union prefixptr prefix);
@@ -191,6 +193,9 @@ extern struct yang_data *yang_data_new_date_and_time(const char *xpath,
/* nexthop enum2str */
extern const char *yang_nexthop_type2str(uint32_t ntype);
+const char *yang_afi_safi_value2identity(afi_t afi, safi_t safi);
+void yang_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/zclient.c b/lib/zclient.c
index 793864243c..808aa18bbe 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -39,6 +39,7 @@
#include "pbr.h"
#include "nexthop_group.h"
#include "lib_errors.h"
+#include "srte.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
@@ -436,7 +437,8 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
vrf_id);
/* We need router-id information. */
- zebra_message_send(zclient, ZEBRA_ROUTER_ID_ADD, vrf_id);
+ zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP,
+ vrf_id);
/* We need interface information. */
zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, vrf_id);
@@ -503,7 +505,8 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
vrf_id);
/* We need router-id information. */
- zebra_message_send(zclient, ZEBRA_ROUTER_ID_DELETE, vrf_id);
+ zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_DELETE, AFI_IP,
+ vrf_id);
zebra_message_send(zclient, ZEBRA_INTERFACE_DELETE, vrf_id);
@@ -554,6 +557,18 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
}
}
+int zclient_send_router_id_update(struct zclient *zclient,
+ zebra_message_types_t type, afi_t afi,
+ vrf_id_t vrf_id)
+{
+ struct stream *s = zclient->obuf;
+ stream_reset(s);
+ zclient_create_header(s, type, vrf_id);
+ stream_putw(s, afi);
+ stream_putw_at(s, 0, stream_get_endp(s));
+ return zclient_send_message(zclient);
+}
+
/* Send request to zebra daemon to start or stop RA. */
void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id,
struct interface *ifp, int enable,
@@ -886,9 +901,9 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp,
* Encode a single zapi nexthop
*/
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
- uint32_t api_flags)
+ uint32_t api_flags, uint32_t api_message)
{
- int ret = 0;
+ int i, ret = 0;
int nh_flags = api_nh->flags;
stream_putl(s, api_nh->vrf_id);
@@ -950,9 +965,22 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
stream_put(s, &(api_nh->rmac),
sizeof(struct ethaddr));
+ /* Color for Segment Routing TE. */
+ if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE))
+ stream_putl(s, api_nh->srte_color);
+
/* Index of backup nexthop */
- if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- stream_putc(s, api_nh->backup_idx);
+ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ /* Validate backup count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ ret = -1;
+ goto done;
+ }
+
+ stream_putc(s, api_nh->backup_num);
+ for (i = 0; i < api_nh->backup_num; i++)
+ stream_putc(s, api_nh->backup_idx[i]);
+ }
done:
return ret;
@@ -977,7 +1005,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
stream_putw(s, api->instance);
stream_putl(s, api->flags);
- stream_putc(s, api->message);
+ stream_putl(s, api->message);
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
@@ -1038,7 +1066,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
return -1;
}
- if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_encode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1082,7 +1112,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
return -1;
}
- if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_encode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1109,9 +1141,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
* Decode a single zapi nexthop object
*/
static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
- uint32_t api_flags)
+ uint32_t api_flags, uint32_t api_message)
{
- int ret = -1;
+ int i, ret = -1;
STREAM_GETL(s, api_nh->vrf_id);
STREAM_GETC(s, api_nh->type);
@@ -1162,9 +1194,20 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
STREAM_GET(&(api_nh->rmac), s,
sizeof(struct ethaddr));
+ /* Color for Segment Routing TE. */
+ if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE))
+ STREAM_GETL(s, api_nh->srte_color);
+
/* Backup nexthop index */
- if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- STREAM_GETC(s, api_nh->backup_idx);
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ STREAM_GETC(s, api_nh->backup_num);
+
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
+ for (i = 0; i < api_nh->backup_num; i++)
+ STREAM_GETC(s, api_nh->backup_idx[i]);
+ }
/* Success */
ret = 0;
@@ -1192,7 +1235,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETW(s, api->instance);
STREAM_GETL(s, api->flags);
- STREAM_GETC(s, api->message);
+ STREAM_GETL(s, api->message);
STREAM_GETC(s, api->safi);
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
@@ -1267,7 +1310,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
- if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_decode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1285,7 +1330,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
for (i = 0; i < api->backup_nexthop_num; i++) {
api_nh = &api->backup_nexthops[i];
- if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_decode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1472,6 +1519,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
n->vrf_id = znh->vrf_id;
n->ifindex = znh->ifindex;
n->gate = znh->gate;
+ n->srte_color = znh->srte_color;
/*
* This function currently handles labels
@@ -1483,7 +1531,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP);
- n->backup_idx = znh->backup_idx;
+ n->backup_num = znh->backup_num;
+ memcpy(n->backup_idx, znh->backup_idx, n->backup_num);
}
return n;
@@ -1519,8 +1568,12 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
}
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ if (nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
- znh->backup_idx = nh->backup_idx;
+ znh->backup_num = nh->backup_num;
+ memcpy(znh->backup_idx, nh->backup_idx, znh->backup_num);
}
return 0;
@@ -1584,6 +1637,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
memset(nhr, 0, sizeof(*nhr));
+ STREAM_GETL(s, nhr->message);
STREAM_GETW(s, nhr->prefix.family);
STREAM_GETC(s, nhr->prefix.prefixlen);
switch (nhr->prefix.family) {
@@ -1596,6 +1650,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
default:
break;
}
+ if (CHECK_FLAG(nhr->message, ZAPI_MESSAGE_SRTE))
+ STREAM_GETL(s, nhr->srte_color);
STREAM_GETC(s, nhr->type);
STREAM_GETW(s, nhr->instance);
@@ -1604,7 +1660,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
STREAM_GETC(s, nhr->nexthop_num);
for (i = 0; i < nhr->nexthop_num; i++) {
- if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0)
+ if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0)
return -1;
}
@@ -1924,8 +1980,7 @@ static int link_params_set_value(struct stream *s, struct if_link_params *iflp)
if (i < bwclassnum)
flog_err(
EC_LIB_ZAPI_MISSMATCH,
- "%s: received %d > %d (MAX_CLASS_TYPE) bw entries"
- " - outdated library?",
+ "%s: received %d > %d (MAX_CLASS_TYPE) bw entries - outdated library?",
__func__, bwclassnum, MAX_CLASS_TYPE);
}
STREAM_GETL(s, iflp->admin_grp);
@@ -2801,6 +2856,92 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
return zclient_send_message(zclient);
}
+int zebra_send_sr_policy(struct zclient *zclient, int cmd,
+ struct zapi_sr_policy *zp)
+{
+ if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0)
+ return -1;
+ return zclient_send_message(zclient);
+}
+
+int zapi_sr_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp)
+{
+ struct zapi_srte_tunnel *zt = &zp->segment_list;
+
+ stream_reset(s);
+
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+ stream_putl(s, zp->color);
+ stream_put_ipaddr(s, &zp->endpoint);
+ stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH);
+
+ stream_putc(s, zt->type);
+ stream_putl(s, zt->local_label);
+
+ if (zt->label_num > MPLS_MAX_LABELS) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: label %u: can't encode %u labels (maximum is %u)",
+ __func__, zt->local_label, zt->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
+ }
+ stream_putw(s, zt->label_num);
+
+ for (int i = 0; i < zt->label_num; i++)
+ stream_putl(s, zt->labels[i]);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
+int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp)
+{
+ memset(zp, 0, sizeof(*zp));
+
+ struct zapi_srte_tunnel *zt = &zp->segment_list;
+
+ STREAM_GETL(s, zp->color);
+ STREAM_GET_IPADDR(s, &zp->endpoint);
+ STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH);
+
+ /* segment list of active candidate path */
+ STREAM_GETC(s, zt->type);
+ STREAM_GETL(s, zt->local_label);
+ STREAM_GETW(s, zt->label_num);
+ if (zt->label_num > MPLS_MAX_LABELS) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: label %u: can't decode %u labels (maximum is %u)",
+ __func__, zt->local_label, zt->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
+ }
+ for (int i = 0; i < zt->label_num; i++)
+ STREAM_GETL(s, zt->labels[i]);
+
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
+int zapi_sr_policy_notify_status_decode(struct stream *s,
+ struct zapi_sr_policy *zp)
+{
+ memset(zp, 0, sizeof(*zp));
+
+ STREAM_GETL(s, zp->color);
+ STREAM_GET_IPADDR(s, &zp->endpoint);
+ STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH);
+ STREAM_GETL(s, zp->status);
+
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
int zebra_send_mpls_labels(struct zclient *zclient, int cmd,
struct zapi_labels *zl)
{
@@ -2840,7 +2981,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl)
for (int i = 0; i < zl->nexthop_num; i++) {
znh = &zl->nexthops[i];
- if (zapi_nexthop_encode(s, znh, 0) < 0)
+ if (zapi_nexthop_encode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2859,7 +3000,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl)
for (int i = 0; i < zl->backup_nexthop_num; i++) {
znh = &zl->backup_nexthops[i];
- if (zapi_nexthop_encode(s, znh, 0) < 0)
+ if (zapi_nexthop_encode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2935,7 +3076,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl)
for (int i = 0; i < zl->nexthop_num; i++) {
znh = &zl->nexthops[i];
- if (zapi_nexthop_decode(s, znh, 0) < 0)
+ if (zapi_nexthop_decode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2956,7 +3097,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl)
for (int i = 0; i < zl->backup_nexthop_num; i++) {
znh = &zl->backup_nexthops[i];
- if (zapi_nexthop_decode(s, znh, 0) < 0)
+ if (zapi_nexthop_decode(s, znh, 0, 0) < 0)
return -1;
}
}
@@ -3499,6 +3640,16 @@ static int zclient_read(struct thread *thread)
(*zclient->local_es_del)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_LOCAL_ES_EVI_ADD:
+ if (zclient->local_es_evi_add)
+ (*zclient->local_es_evi_add)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_LOCAL_ES_EVI_DEL:
+ if (zclient->local_es_evi_del)
+ (*zclient->local_es_evi_del)(command, zclient, length,
+ vrf_id);
+ break;
case ZEBRA_VNI_ADD:
if (zclient->local_vni_add)
(*zclient->local_vni_add)(command, zclient, length,
@@ -3613,6 +3764,10 @@ static int zclient_read(struct thread *thread)
(*zclient->opaque_unregister_handler)(command, zclient,
length, vrf_id);
break;
+ case ZEBRA_SR_POLICY_NOTIFY_STATUS:
+ if (zclient->sr_policy_notify_status)
+ (*zclient->sr_policy_notify_status)(command, zclient,
+ length, vrf_id);
default:
break;
}
@@ -3798,3 +3953,23 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api)
stream_failure:
return 0;
}
+
+int zclient_send_neigh_discovery_req(struct zclient *zclient,
+ const struct interface *ifp,
+ const struct prefix *p)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id);
+ stream_putl(s, ifp->ifindex);
+
+ stream_putc(s, p->family);
+ stream_putc(s, p->prefixlen);
+ stream_put(s, &p->u.prefix, prefix_blen(p));
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ return zclient_send_message(zclient);
+}
diff --git a/lib/zclient.h b/lib/zclient.h
index 3ded2f55d7..dab384d5ec 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -37,6 +37,7 @@
#include "pw.h"
#include "mlag.h"
+#include "srte.h"
#ifdef __cplusplus
extern "C" {
@@ -89,6 +90,8 @@ enum zserv_client_capabilities {
/* Macro to check if there GR enabled. */
#define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES)
+#define ZEBRA_SR_POLICY_NAME_MAX_LENGTH 100
+
extern struct sockaddr_storage zclient_addr;
extern socklen_t zclient_addr_len;
@@ -143,6 +146,9 @@ typedef enum {
ZEBRA_MPLS_LABELS_ADD,
ZEBRA_MPLS_LABELS_DELETE,
ZEBRA_MPLS_LABELS_REPLACE,
+ ZEBRA_SR_POLICY_SET,
+ ZEBRA_SR_POLICY_DELETE,
+ ZEBRA_SR_POLICY_NOTIFY_STATUS,
ZEBRA_IPMR_ROUTE_STATS,
ZEBRA_LABEL_MANAGER_CONNECT,
ZEBRA_LABEL_MANAGER_CONNECT_ASYNC,
@@ -157,6 +163,10 @@ typedef enum {
ZEBRA_ADVERTISE_ALL_VNI,
ZEBRA_LOCAL_ES_ADD,
ZEBRA_LOCAL_ES_DEL,
+ ZEBRA_REMOTE_ES_VTEP_ADD,
+ ZEBRA_REMOTE_ES_VTEP_DEL,
+ ZEBRA_LOCAL_ES_EVI_ADD,
+ ZEBRA_LOCAL_ES_EVI_DEL,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
ZEBRA_L3VNI_ADD,
@@ -204,6 +214,7 @@ typedef enum {
ZEBRA_OPAQUE_MESSAGE,
ZEBRA_OPAQUE_REGISTER,
ZEBRA_OPAQUE_UNREGISTER,
+ ZEBRA_NEIGH_DISCOVER,
} zebra_message_types_t;
enum zebra_error_types {
@@ -321,6 +332,8 @@ struct zclient {
int (*fec_update)(int, struct zclient *, uint16_t);
int (*local_es_add)(ZAPI_CALLBACK_ARGS);
int (*local_es_del)(ZAPI_CALLBACK_ARGS);
+ int (*local_es_evi_add)(ZAPI_CALLBACK_ARGS);
+ int (*local_es_evi_del)(ZAPI_CALLBACK_ARGS);
int (*local_vni_add)(ZAPI_CALLBACK_ARGS);
int (*local_vni_del)(ZAPI_CALLBACK_ARGS);
int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS);
@@ -345,6 +358,7 @@ struct zclient {
int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS);
int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS);
int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS);
+ int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS);
};
/* Zebra API message flag. */
@@ -362,7 +376,8 @@ struct zclient {
* the table being used is not in the VRF. You must pass the
* default vrf, else this will be ignored.
*/
-#define ZAPI_MESSAGE_TABLEID 0x80
+#define ZAPI_MESSAGE_TABLEID 0x0080
+#define ZAPI_MESSAGE_SRTE 0x0100
#define ZSERV_VERSION 6
/* Zserv protocol message header */
@@ -394,8 +409,12 @@ struct zapi_nexthop {
uint32_t weight;
- /* Index of backup nexthop */
- uint8_t backup_idx;
+ /* Backup nexthops, for IP-FRR, TI-LFA, etc */
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
+
+ /* SR-TE color. */
+ uint32_t srte_color;
};
/*
@@ -458,7 +477,7 @@ struct zapi_route {
#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40
/* The older XXX_MESSAGE flags live here */
- uint8_t message;
+ uint32_t message;
/*
* This is an enum but we are going to treat it as a uint8_t
@@ -487,6 +506,9 @@ struct zapi_route {
vrf_id_t vrf_id;
uint32_t tableid;
+
+ /* SR-TE color (used for nexthop updates only). */
+ uint32_t srte_color;
};
struct zapi_labels {
@@ -509,6 +531,21 @@ struct zapi_labels {
struct zapi_nexthop backup_nexthops[MULTIPATH_NUM];
};
+struct zapi_srte_tunnel {
+ enum lsp_types_t type;
+ mpls_label_t local_label;
+ uint8_t label_num;
+ mpls_label_t labels[MPLS_MAX_LABELS];
+};
+
+struct zapi_sr_policy {
+ uint32_t color;
+ struct ipaddr endpoint;
+ char name[SRTE_POLICY_NAME_MAX_LENGTH];
+ struct zapi_srte_tunnel segment_list;
+ int status;
+};
+
struct zapi_pw {
char ifname[IF_NAMESIZE];
ifindex_t ifindex;
@@ -600,6 +637,11 @@ zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note)
#define ZEBRA_MACIP_TYPE_ROUTER_FLAG 0x04 /* Router Flag - proxy NA */
#define ZEBRA_MACIP_TYPE_OVERRIDE_FLAG 0x08 /* Override Flag */
#define ZEBRA_MACIP_TYPE_SVI_IP 0x10 /* SVI MAC-IP */
+#define ZEBRA_MACIP_TYPE_PROXY_ADVERT 0x20 /* Not locally active */
+#define ZEBRA_MACIP_TYPE_SYNC_PATH 0x40 /* sync path */
+/* XXX - flags is an u8; that needs to be changed to u32 if you need
+ * to allocate past 0x80
+ */
enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 };
@@ -647,6 +689,9 @@ extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id,
extern void zclient_send_reg_requests(struct zclient *, vrf_id_t);
extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t);
+extern int zclient_send_router_id_update(struct zclient *zclient,
+ zebra_message_types_t type, afi_t afi,
+ vrf_id_t vrf_id);
extern void zclient_send_interface_radv_req(struct zclient *zclient,
vrf_id_t vrf_id,
@@ -763,6 +808,14 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size,
extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
uint32_t end);
+extern int zebra_send_sr_policy(struct zclient *zclient, int cmd,
+ struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_encode(struct stream *s, int cmd,
+ struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_notify_status_decode(struct stream *s,
+ struct zapi_sr_policy *zp);
+
extern int zebra_send_mpls_labels(struct zclient *zclient, int cmd,
struct zapi_labels *zl);
extern int zapi_labels_encode(struct stream *s, int cmd,
@@ -779,7 +832,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command,
const struct prefix *p, bool exact_match,
vrf_id_t vrf_id);
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
- uint32_t api_flags);
+ uint32_t api_flags, uint32_t api_message);
extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
@@ -904,6 +957,10 @@ enum zapi_opaque_registry {
*/
extern int zclient_send_hello(struct zclient *client);
+extern int zclient_send_neigh_discovery_req(struct zclient *zclient,
+ const struct interface *ifp,
+ const struct prefix *p);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/zlog.c b/lib/zlog.c
index 45726755f8..8dfd20371b 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -246,10 +246,10 @@ void zlog_tls_buffer_init(void)
fchown(mmfd, zlog_uid, zlog_gid);
#ifdef HAVE_POSIX_FALLOCATE
- if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) < 0) {
-#else
- if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
+ if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0)
+ /* note next statement is under above if() */
#endif
+ if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
zlog_err("failed to allocate thread log buffer \"%s\": %s",
mmpath, strerror(errno));
goto out_anon_unlink;
diff --git a/m4/ax_python.m4 b/m4/ax_python.m4
index 9f43ea0ab1..91d12b99b4 100644
--- a/m4/ax_python.m4
+++ b/m4/ax_python.m4
@@ -186,7 +186,8 @@ AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_MSG_RESULT([yes])
PYTHON_CFLAGS="`\"$pycfg\" --includes`"
- if test x"${py_ver}" = x"3.8" || test x"{py_ver}" = x"3.9"; then
+ minor_ver=${py_ver#*\.}
+ if test $((minor_ver)) -gt 7; then
PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`"
else
PYTHON_LIBS="`\"$pycfg\" --ldflags`"
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
index f242c2e367..0c5513b892 100644
--- a/nhrpd/nhrp_route.c
+++ b/nhrpd/nhrp_route.c
@@ -170,8 +170,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type,
prefix2str(&api.prefix, buf[0], sizeof(buf[0]));
zlog_debug(
- "Zebra send: route %s %s nexthop %s metric %u"
- " count %d dev %s",
+ "Zebra send: route %s %s nexthop %s metric %u count %d dev %s",
add ? "add" : "del", buf[0],
nexthop ? inet_ntop(api.prefix.family, &api_nh->gate,
buf[1], sizeof(buf[1]))
@@ -199,6 +198,10 @@ int nhrp_route_read(ZAPI_CALLBACK_ARGS)
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
return 0;
+ /* ignore our routes */
+ if (api.type == ZEBRA_ROUTE_NHRP)
+ return 0;
+
sockunion_family(&nexthop_addr) = AF_UNSPEC;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
api_nh = &api.nexthops[0];
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index 1f6cc9d527..b339790492 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -875,7 +875,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
bool old_entry_updated = false;
struct ospf6_path *path, *o_path, *ecmp_path;
struct listnode *anode;
- char adv_router[16];
memset(&prefix, 0, sizeof(prefix));
@@ -940,10 +939,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
if (listcount(route->paths) > 1) {
for (ALL_LIST_ELEMENTS_RO(route->paths, anode,
o_path)) {
- inet_ntop(AF_INET,
- &o_path->origin.adv_router,
- adv_router,
- sizeof(adv_router));
if (o_path->origin.id == lsa->header->id
&& o_path->origin.adv_router ==
lsa->header->adv_router) {
@@ -951,9 +946,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
if (is_debug)
zlog_debug(
- "%s: old entry found in paths, adv_router %s",
+ "%s: old entry found in paths, adv_router %pI4",
__func__,
- adv_router);
+ &o_path->origin.adv_router);
break;
}
@@ -1185,14 +1180,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
listnode_add_sort(old_route->paths, ecmp_path);
if (is_debug) {
- prefix2str(&route->prefix, buf, sizeof(buf));
- inet_ntop(AF_INET,
- &ecmp_path->origin.adv_router,
- adv_router, sizeof(adv_router));
zlog_debug(
- "%s: route %s cost %u another path %s added with nh %u, effective paths %u nh %u",
- __func__, buf, old_route->path.cost,
- adv_router,
+ "%s: route %pFX cost %u another path %pI4 added with nh %u, effective paths %u nh %u",
+ __func__, &route->prefix,
+ old_route->path.cost,
+ &ecmp_path->origin.adv_router,
listcount(ecmp_path->nh_list),
old_route->paths
? listcount(old_route->paths)
@@ -1239,12 +1231,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
if (old_entry_updated == false) {
if (is_debug) {
- inet_ntop(AF_INET, &route->path.origin.adv_router,
- adv_router, sizeof(adv_router));
zlog_debug(
- "%s: Install route: %s cost %u nh %u adv_router %s ",
+ "%s: Install route: %s cost %u nh %u adv_router %pI4",
__func__, buf, route->path.cost,
- listcount(route->nh_list), adv_router);
+ listcount(route->nh_list),
+ &route->path.origin.adv_router);
}
path = ospf6_path_dup(&route->path);
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index 9fe077b544..713ce26ecb 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -379,22 +379,6 @@ void ospf6_area_show(struct vty *vty, struct ospf6_area *oa)
vty_out(vty, "SPF has not been run\n");
}
-
-#define OSPF6_CMD_AREA_GET(str, oa) \
- { \
- char *ep; \
- uint32_t area_id = htonl(strtoul(str, &ep, 10)); \
- if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \
- vty_out(vty, "Malformed Area-ID: %s\n", str); \
- return CMD_SUCCESS; \
- } \
- int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \
- : OSPF6_AREA_FMT_DOTTEDQUAD; \
- oa = ospf6_area_lookup(area_id, ospf6); \
- if (oa == NULL) \
- oa = ospf6_area_create(area_id, ospf6, format); \
- }
-
DEFUN (area_range,
area_range_cmd,
"area <A.B.C.D|(0-4294967295)> range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>]",
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index 5648b1dfec..f6287660d6 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -28,7 +28,7 @@ struct ospf6_area {
struct ospf6 *ospf6;
/* Area-ID */
- uint32_t area_id;
+ in_addr_t area_id;
#define OSPF6_AREA_FMT_DOTTEDQUAD 1
#define OSPF6_AREA_FMT_DECIMAL 2
@@ -117,6 +117,21 @@ struct ospf6_area {
#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT))
#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB))
+#define OSPF6_CMD_AREA_GET(str, oa) \
+ { \
+ char *ep; \
+ uint32_t area_id = htonl(strtoul(str, &ep, 10)); \
+ if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \
+ vty_out(vty, "Malformed Area-ID: %s\n", str); \
+ return CMD_SUCCESS; \
+ } \
+ int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \
+ : OSPF6_AREA_FMT_DOTTEDQUAD; \
+ oa = ospf6_area_lookup(area_id, ospf6); \
+ if (oa == NULL) \
+ oa = ospf6_area_create(area_id, ospf6, format); \
+ }
+
/* prototypes */
extern int ospf6_area_cmp(void *va, void *vb);
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index cea4dd93e5..5562529ea8 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -244,11 +244,9 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
continue;
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&old_route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s cost old %u new %u is not same, replace route",
- __func__, buf, o_path->cost,
+ "%s: route %pFX cost old %u new %u is not same, replace route",
+ __func__, &old_route->prefix, o_path->cost,
route->path.cost);
}
@@ -308,11 +306,9 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
}
} else {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&old_route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s old cost %u new cost %u, delete old entry.",
- __func__, buf,
+ "%s: route %pFX old cost %u new cost %u, delete old entry.",
+ __func__, &old_route->prefix,
old_route->path.cost,
route->path.cost);
}
@@ -339,11 +335,10 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
&& (old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&old_route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: old route %s path cost %u e2 %u",
- __func__, buf, old_route->path.cost,
+ "%s: old route %pFX path cost %u e2 %u",
+ __func__, &old_route->prefix,
+ old_route->path.cost,
old_route->path.u.cost_e2);
}
route_found = true;
@@ -562,7 +557,6 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_as_external_lsa *external;
struct prefix prefix;
struct ospf6_route *route, *nroute, *route_to_del;
- char buf[PREFIX2STR_BUFFER];
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
@@ -612,8 +606,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
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);
+ zlog_debug("AS-External route %pFX not found", &prefix);
}
ospf6_route_delete(route_to_del);
@@ -621,10 +614,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&prefix, buf, sizeof(buf));
zlog_debug(
- "%s: Current route %s cost %u e2 %u, route to del cost %u e2 %u",
- __func__, buf, route->path.cost, route->path.u.cost_e2,
+ "%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u",
+ __func__, &prefix, route->path.cost, route->path.u.cost_e2,
route_to_del->path.cost, route_to_del->path.u.cost_e2);
}
@@ -668,11 +660,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(
AS_EXTERNAL)) {
- prefix2str(&prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s to delete is not same, cost %u del cost %u. skip",
- __func__, buf,
+ "%s: route %pFX to delete is not same, cost %u del cost %u. skip",
+ __func__, &prefix,
route->path.cost,
route_to_del->path
.cost);
@@ -681,10 +671,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&prefix, buf, sizeof(buf));
zlog_debug(
- "%s: route %s path found with cost %u nh %u to remove.",
- __func__, buf, route->path.cost,
+ "%s: route %pFX path found with cost %u nh %u to remove.",
+ __func__, &prefix, route->path.cost,
listcount(o_path->nh_list));
}
@@ -723,16 +712,14 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: AS-External %u route %s update paths %u nh %u",
+ "%s: AS-External %u route %pFX update paths %u nh %u",
__func__,
(route->path.type
== OSPF6_PATH_TYPE_EXTERNAL1)
? 1
: 2,
- buf, listcount(route->paths),
+ &route->prefix, listcount(route->paths),
route->nh_list ? listcount(
route->nh_list)
: 0);
@@ -784,10 +771,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
|| (route->path.u.cost_e2
!= route_to_del->path.u.cost_e2))) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&prefix, buf, sizeof(buf));
zlog_debug(
- "%s: route %s to delete is not same, cost %u del cost %u. skip",
- __func__, buf, route->path.cost,
+ "%s: route %pFX to delete is not same, cost %u del cost %u. skip",
+ __func__, &prefix, route->path.cost,
route_to_del->path.cost);
}
continue;
@@ -800,14 +786,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
continue;
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
- prefix2str(&route->prefix, buf, sizeof(buf));
zlog_debug(
- "%s: AS-External %u route remove %s cost %u(%u) nh %u",
+ "%s: AS-External %u route remove %pFX cost %u(%u) nh %u",
__func__,
route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
? 1
: 2,
- buf, route->path.cost, route->path.u.cost_e2,
+ &route->prefix, route->path.cost, route->path.u.cost_e2,
listcount(route->nh_list));
}
ospf6_route_remove(route, ospf6->route_table);
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
index 4e7a0050aa..916e59baf0 100644
--- a/ospf6d/ospf6_bfd.c
+++ b/ospf6d/ospf6_bfd.c
@@ -308,7 +308,7 @@ static void ospf6_bfd_if_param_set(struct ospf6_interface *oi, uint32_t min_rx,
int command = 0;
bfd_set_param((struct bfd_info **)&(oi->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
if (command)
ospf6_bfd_reg_dereg_all_nbr(oi, command);
}
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 1209997514..d10329a93b 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -1966,8 +1966,7 @@ static int ospf6_ifp_up(struct interface *ifp)
{
if (IS_OSPF6_DEBUG_ZEBRA(RECV))
zlog_debug(
- "Zebra Interface state change: "
- "%s index %d flags %llx metric %d mtu %d bandwidth %d",
+ "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);
@@ -1980,8 +1979,7 @@ static int ospf6_ifp_down(struct interface *ifp)
{
if (IS_OSPF6_DEBUG_ZEBRA(RECV))
zlog_debug(
- "Zebra Interface state change: "
- "%s index %d flags %llx metric %d mtu %d bandwidth %d",
+ "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);
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index 05ba698a1b..6cbfe04c44 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -90,10 +90,10 @@ struct ospf6_interface {
uint8_t mtu_ignore;
/* Decision of DR Election */
- uint32_t drouter;
- uint32_t bdrouter;
- uint32_t prev_drouter;
- uint32_t prev_bdrouter;
+ in_addr_t drouter;
+ in_addr_t bdrouter;
+ in_addr_t prev_drouter;
+ in_addr_t prev_bdrouter;
/* Linklocal LSA Database: includes Link-LSA */
struct ospf6_lsdb *lsdb;
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index b700899ccf..ef5d1d0583 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -1400,11 +1400,9 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
continue;
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
- prefix2str(&old_route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s cost old %u new %u is not same, replace route",
- __func__, buf, o_path->cost,
+ "%s: route %pFX cost old %u new %u is not same, replace route",
+ __func__, &old_route->prefix, o_path->cost,
route->path.cost);
}
@@ -1458,11 +1456,9 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
}
} else {
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
- prefix2str(&old_route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s old cost %u new cost %u, delete old entry.",
- __func__, buf,
+ "%s: route %pFX old cost %u new cost %u, delete old entry.",
+ __func__, &old_route->prefix,
old_route->path.cost,
route->path.cost);
}
@@ -1515,11 +1511,9 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
listnode_add_sort(old_route->paths, ecmp_path);
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
- prefix2str(&route->prefix, buf,
- sizeof(buf));
zlog_debug(
- "%s: route %s %p another path added with nh %u, effective paths %u nh %u",
- __func__, buf,
+ "%s: route %pFX %p another path added with nh %u, effective paths %u nh %u",
+ __func__, &route->prefix,
(void *)old_route,
listcount(ecmp_path->nh_list),
old_route->paths ? listcount(
diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h
index 672e288bf3..9c29681dee 100644
--- a/ospf6d/ospf6_intra.h
+++ b/ospf6d/ospf6_intra.h
@@ -23,8 +23,8 @@
/* Debug option */
extern unsigned char conf_debug_ospf6_brouter;
-extern uint32_t conf_debug_ospf6_brouter_specific_router_id;
-extern uint32_t conf_debug_ospf6_brouter_specific_area_id;
+extern in_addr_t conf_debug_ospf6_brouter_specific_router_id;
+extern in_addr_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
@@ -86,7 +86,7 @@ struct ospf6_router_lsdesc {
uint16_t metric; /* output cost */
uint32_t interface_id;
uint32_t neighbor_interface_id;
- uint32_t neighbor_router_id;
+ in_addr_t neighbor_router_id;
};
#define OSPF6_ROUTER_LSDESC_POINTTOPOINT 1
@@ -125,7 +125,7 @@ struct ospf6_network_lsa {
/* Link State Description in Router-LSA */
#define OSPF6_NETWORK_LSDESC_FIX_SIZE 4U
struct ospf6_network_lsdesc {
- uint32_t router_id;
+ in_addr_t router_id;
};
#define NETWORK_LSDESC_GET_NBR_ROUTERID(x) \
(((struct ospf6_network_lsdesc *)(x))->router_id)
@@ -146,7 +146,7 @@ struct ospf6_intra_prefix_lsa {
uint16_t prefix_num;
uint16_t ref_type;
uint32_t ref_id;
- uint32_t ref_adv_router;
+ in_addr_t ref_adv_router;
/* followed by ospf6 prefix(es) */
};
diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h
index 5519dd1b80..a85d7b0603 100644
--- a/ospf6d/ospf6_lsa.h
+++ b/ospf6d/ospf6_lsa.h
@@ -80,8 +80,8 @@
struct ospf6_lsa_header {
uint16_t age; /* LS age */
uint16_t type; /* LS type */
- uint32_t id; /* Link State ID */
- uint32_t adv_router; /* Advertising Router */
+ in_addr_t id; /* Link State ID */
+ in_addr_t adv_router; /* Advertising Router */
uint32_t seqnum; /* LS sequence number */
uint16_t checksum; /* LS checksum */
uint16_t length; /* LSA length */
diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h
index d24b7f8942..7ec8cb785f 100644
--- a/ospf6d/ospf6_message.h
+++ b/ospf6d/ospf6_message.h
@@ -49,8 +49,8 @@ struct ospf6_header {
uint8_t version;
uint8_t type;
uint16_t length;
- uint32_t router_id;
- uint32_t area_id;
+ in_addr_t router_id;
+ in_addr_t area_id;
uint16_t checksum;
uint8_t instance_id;
uint8_t reserved;
@@ -66,8 +66,8 @@ struct ospf6_hello {
uint8_t options[3];
uint16_t hello_interval;
uint16_t dead_interval;
- uint32_t drouter;
- uint32_t bdrouter;
+ in_addr_t drouter;
+ in_addr_t bdrouter;
/* Followed by Router-IDs */
};
@@ -94,8 +94,8 @@ struct ospf6_dbdesc {
struct ospf6_lsreq_entry {
uint16_t reserved; /* Must Be Zero */
uint16_t type; /* LS type */
- uint32_t id; /* Link State ID */
- uint32_t adv_router; /* Advertising Router */
+ in_addr_t id; /* Link State ID */
+ in_addr_t adv_router; /* Advertising Router */
};
/* Link State Update */
diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h
index e221e9d82c..1a45a1966a 100644
--- a/ospf6d/ospf6_neighbor.h
+++ b/ospf6d/ospf6_neighbor.h
@@ -48,7 +48,7 @@ struct ospf6_neighbor {
struct timeval last_changed;
/* Neighbor Router ID */
- uint32_t router_id;
+ in_addr_t router_id;
/* Neighbor Interface ID */
ifindex_t ifindex;
@@ -56,10 +56,10 @@ struct ospf6_neighbor {
/* Router Priority of this neighbor */
uint8_t priority;
- uint32_t drouter;
- uint32_t bdrouter;
- uint32_t prev_drouter;
- uint32_t prev_bdrouter;
+ in_addr_t drouter;
+ in_addr_t bdrouter;
+ in_addr_t prev_drouter;
+ in_addr_t prev_bdrouter;
/* Options field (Capability) */
char options[3];
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index 13b01a3487..95ba983e6b 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -64,8 +64,8 @@ struct ospf6_nexthop {
/* Path */
struct ospf6_ls_origin {
uint16_t type;
- uint32_t id;
- uint32_t adv_router;
+ in_addr_t id;
+ in_addr_t adv_router;
};
struct ospf6_path {
@@ -82,7 +82,7 @@ struct ospf6_path {
uint8_t prefix_options;
/* Associated Area */
- uint32_t area_id;
+ in_addr_t area_id;
/* Path-type */
uint8_t type;
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 80bff5795f..e75c132956 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -658,8 +658,7 @@ static int ospf6_spf_calculation_thread(struct thread *t)
(long long)runtime.tv_usec);
zlog_info(
- "SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, "
- "Reason: %s\n",
+ "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);
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index dd672dd1c5..50687a7290 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -390,8 +390,7 @@ DEFUN(ospf6_router_id,
for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
if (oa->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect,"
- " save config and restart ospf6d\n");
+ "For this router-id change to take effect, save config and restart ospf6d\n");
return CMD_SUCCESS;
}
}
@@ -417,8 +416,7 @@ DEFUN(no_ospf6_router_id,
for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
if (oa->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect,"
- " save config and restart ospf6d\n");
+ "For this router-id change to take effect, save config and restart ospf6d\n");
return CMD_SUCCESS;
}
}
@@ -644,11 +642,12 @@ DEFUN (no_ospf6_distance_source,
DEFUN (ospf6_interface_area,
ospf6_interface_area_cmd,
- "interface IFNAME area A.B.C.D",
+ "interface IFNAME area <A.B.C.D|(0-4294967295)>",
"Enable routing on an IPv6 interface\n"
IFNAME_STR
"Specify the OSPF6 area ID\n"
"OSPF6 area ID in IPv4 address notation\n"
+ "OSPF6 area ID in decimal notation\n"
)
{
VTY_DECLVAR_CONTEXT(ospf6, o);
@@ -657,7 +656,6 @@ DEFUN (ospf6_interface_area,
struct ospf6_area *oa;
struct ospf6_interface *oi;
struct interface *ifp;
- uint32_t area_id;
/* find/create ospf6 interface */
ifp = if_get_by_name(argv[idx_ifname]->arg, VRF_DEFAULT);
@@ -671,15 +669,7 @@ DEFUN (ospf6_interface_area,
}
/* parse Area-ID */
- if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) {
- vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg);
- return CMD_SUCCESS;
- }
-
- /* find/create ospf6 area */
- oa = ospf6_area_lookup(area_id, o);
- if (oa == NULL)
- oa = ospf6_area_create(area_id, o, OSPF6_AREA_FMT_DOTTEDQUAD);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa);
/* attach interface to area */
listnode_add(oa->if_list, oi); /* sort ?? */
@@ -703,12 +693,13 @@ DEFUN (ospf6_interface_area,
DEFUN (no_ospf6_interface_area,
no_ospf6_interface_area_cmd,
- "no interface IFNAME area A.B.C.D",
+ "no interface IFNAME area <A.B.C.D|(0-4294967295)>",
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"
+ "OSPF6 area ID in decimal notation\n"
)
{
int idx_ifname = 2;
@@ -731,10 +722,8 @@ DEFUN (no_ospf6_interface_area,
}
/* parse Area-ID */
- if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) {
- vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg);
- return CMD_SUCCESS;
- }
+ if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1)
+ area_id = htonl(strtoul(argv[idx_ipv4]->arg, NULL, 10));
/* Verify Area */
if (oi->area == NULL) {
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 18c0697025..806b4da1cf 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -26,7 +26,7 @@
struct ospf6_master {
- uint32_t zebra_router_id;
+ in_addr_t zebra_router_id;
};
/* ospf6->config_flags */
@@ -41,10 +41,10 @@ struct ospf6 {
vrf_id_t vrf_id;
/* my router id */
- uint32_t router_id;
+ in_addr_t router_id;
/* static router id */
- uint32_t router_id_static;
+ in_addr_t router_id_static;
struct in_addr router_id_zebra;
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index eb3323997f..3dbc476172 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -348,8 +348,7 @@ static int ospf_abr_nssa_am_elected(struct ospf_area *area)
if (IS_ROUTER_LSA_NT(rlsa)) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_nssa_am_elected: "
- "router %s asserts Nt",
+ "ospf_abr_nssa_am_elected: router %s asserts Nt",
inet_ntoa(lsa->data->id));
return 0;
}
@@ -391,15 +390,13 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf)
if (IS_DEBUG_OSPF(nssa, NSSA))
zlog_debug(
- "ospf_abr_nssa_check_status: "
- "checking area %s",
+ "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");
+ "ospf_abr_nssa_check_status: not ABR");
area->NSSATranslatorState =
OSPF_NSSA_TRANSLATE_DISABLED;
} else {
@@ -409,8 +406,7 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf)
/* TODO: check previous state and flush? */
if (IS_DEBUG_OSPF(nssa, NSSA))
zlog_debug(
- "ospf_abr_nssa_check_status: "
- "never translate");
+ "ospf_abr_nssa_check_status: never translate");
area->NSSATranslatorState =
OSPF_NSSA_TRANSLATE_DISABLED;
break;
@@ -422,8 +418,7 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf)
*/
if (IS_DEBUG_OSPF(nssa, NSSA))
zlog_debug(
- "ospf_abr_nssa_check_status: "
- "translate always");
+ "ospf_abr_nssa_check_status: translate always");
area->NSSATranslatorState =
OSPF_NSSA_TRANSLATE_ENABLED;
break;
@@ -435,15 +430,13 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf)
OSPF_NSSA_TRANSLATE_ENABLED;
if (IS_DEBUG_OSPF(nssa, NSSA))
zlog_debug(
- "ospf_abr_nssa_check_status: "
- "elected translator");
+ "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");
+ "ospf_abr_nssa_check_status: not elected");
}
break;
}
@@ -639,8 +632,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
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",
+ "ospf_abr_translate_nssa(): LSA Id %s, Forward address is 0, NO Translation",
inet_ntoa(lsa->data->id));
return 1;
}
@@ -652,8 +644,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
if (old) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_translate_nssa(): "
- "found old translated LSA Id %s, refreshing",
+ "ospf_abr_translate_nssa(): found old translated LSA Id %s, refreshing",
inet_ntoa(old->data->id));
/* refresh */
@@ -661,8 +652,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
if (!new) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_translate_nssa(): "
- "could not refresh translated LSA Id %s",
+ "ospf_abr_translate_nssa(): could not refresh translated LSA Id %s",
inet_ntoa(old->data->id));
}
} else {
@@ -673,8 +663,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
if (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",
+ "ospf_abr_translate_nssa(): Could not translate Type-7 for %s to Type-5",
inet_ntoa(lsa->data->id));
return 1;
}
@@ -719,8 +708,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_network_to_area(): "
- "old metric: %d, new metric: %d",
+ "ospf_abr_announce_network_to_area(): old metric: %d, new metric: %d",
GET_METRIC(sl->metric), cost);
if ((GET_METRIC(sl->metric) == full_cost)
@@ -728,15 +716,13 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost,
/* unchanged. simply reapprove it */
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_network_to_area(): "
- "old summary approved");
+ "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");
+ "ospf_abr_announce_network_to_area(): refreshing summary");
set_metric(old, full_cost);
lsa = ospf_lsa_refresh(area->ospf, old);
@@ -758,8 +744,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost,
} else {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_network_to_area(): "
- "creating new summary");
+ "ospf_abr_announce_network_to_area(): creating new summary");
lsa = ospf_summary_lsa_originate(p, full_cost, area);
/* This will flood through area. */
@@ -776,8 +761,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost,
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");
+ "ospf_abr_announce_network_to_area(): flooding new version of summary");
}
if (IS_DEBUG_OSPF_EVENT)
@@ -875,8 +859,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "ospf_abr_announce_network(): prefix %s/%d was denied by import-list",
inet_ntoa(p->prefix), p->prefixlen);
continue;
}
@@ -884,8 +867,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "ospf_abr_announce_network(): prefix %s/%d was denied by prefix-list",
inet_ntoa(p->prefix), p->prefixlen);
continue;
}
@@ -894,8 +876,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
&& area->no_summary) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_network(): "
- "area %s is stub and no_summary",
+ "ospf_abr_announce_network(): area %s is stub and no_summary",
inet_ntoa(area->area_id));
continue;
}
@@ -903,8 +884,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "ospf_abr_announce_network(): this is inter-area route to %s/%d",
inet_ntoa(p->prefix), p->prefixlen);
if (!OSPF_IS_AREA_BACKBONE(area))
@@ -915,8 +895,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "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))
@@ -976,8 +955,7 @@ static void ospf_abr_process_nssa_translates(struct ospf *ospf)
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_process_nssa_translates(): "
- "looking at area %s",
+ "ospf_abr_process_nssa_translates(): looking at area %s",
inet_ntoa(area->area_id));
LSDB_LOOP (NSSA_LSDB(area), rn, lsa)
@@ -1018,24 +996,21 @@ static void ospf_abr_process_network_rt(struct ospf *ospf,
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");
+ "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");
+ "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");
+ "ospf_abr_process_network_rt(): this is a discard entry, skipping");
continue;
}
@@ -1065,8 +1040,7 @@ static void ospf_abr_process_network_rt(struct ospf *ospf,
&& !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");
+ "ospf_abr_process_network_rt(): this is route is not backbone one, skipping");
continue;
}
@@ -1078,8 +1052,7 @@ static void ospf_abr_process_network_rt(struct ospf *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");
+ "ospf_abr_process_network_rt(): ALT ABR: No BB connection, skip not intra-area routes");
continue;
}
@@ -1112,8 +1085,7 @@ static void ospf_abr_announce_rtr_to_area(struct prefix_ipv4 *p, uint32_t cost,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_network_to_area(): "
- "old metric: %d, new metric: %d",
+ "ospf_abr_announce_network_to_area(): old metric: %d, new metric: %d",
GET_METRIC(slsa->metric), cost);
}
@@ -1144,8 +1116,7 @@ static void ospf_abr_announce_rtr_to_area(struct prefix_ipv4 *p, uint32_t cost,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_rtr_to_area(): "
- "flooding new version of summary");
+ "ospf_abr_announce_rtr_to_area(): flooding new version of summary");
/*
zlog_info ("ospf_abr_announce_rtr_to_area(): creating new
@@ -1185,8 +1156,7 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "ospf_abr_announce_rtr(): area %s doesn't support external routing",
inet_ntoa(area->area_id));
continue;
}
@@ -1194,8 +1164,7 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "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,
@@ -1205,8 +1174,7 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p,
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",
+ "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);
}
@@ -1255,8 +1223,7 @@ static void ospf_abr_process_router_rt(struct ospf *ospf,
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");
+ "ospf_abr_process_router_rt(): This is not an ASBR, skipping");
continue;
}
@@ -1272,8 +1239,7 @@ static void ospf_abr_process_router_rt(struct ospf *ospf,
if (or != best) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_process_router_rt(): "
- "This route is not the best among possible, skipping");
+ "ospf_abr_process_router_rt(): This route is not the best among possible, skipping");
continue;
}
@@ -1283,16 +1249,14 @@ static void ospf_abr_process_router_rt(struct ospf *ospf,
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");
+ "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");
+ "ospf_abr_process_router_rt(): This route has LS_INFINITY metric, skipping");
continue;
}
@@ -1302,8 +1266,7 @@ static void ospf_abr_process_router_rt(struct ospf *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");
+ "ospf_abr_process_network_rt(): ALT ABR: No BB connection, skip not intra-area routes");
continue;
}
@@ -1333,8 +1296,7 @@ ospf_abr_unapprove_translates(struct ospf *ospf) /* For NSSA Translations */
UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_unapprove_translates(): "
- "approved unset on link id %s",
+ "ospf_abr_unapprove_translates(): approved unset on link id %s",
inet_ntoa(lsa->data->id));
}
@@ -1355,15 +1317,13 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf)
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_unapprove_summaries(): "
- "considering area %s",
+ "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",
+ "ospf_abr_unapprove_summaries(): approved unset on summary link id %s",
inet_ntoa(lsa->data->id));
UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
}
@@ -1372,8 +1332,7 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf)
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",
+ "ospf_abr_unapprove_summaries(): approved unset on asbr-summary link id %s",
inet_ntoa(lsa->data->id));
UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
}
@@ -1428,8 +1387,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf)
OSPF_AREA_RANGE_ADVERTISE)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_aggregates():"
- " discarding suppress-ranges");
+ "ospf_abr_announce_aggregates(): discarding suppress-ranges");
continue;
}
@@ -1439,8 +1397,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_aggregates():"
- " this is range: %s/%d",
+ "ospf_abr_announce_aggregates(): this is range: %s/%d",
inet_ntoa(p.u.prefix4),
p.prefixlen);
@@ -1477,9 +1434,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf)
area)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_aggregates(): Skipping "
- "announcement of BB aggregate into"
- " a transit area");
+ "ospf_abr_announce_aggregates(): Skipping announcement of BB aggregate into a transit area");
continue;
}
ospf_abr_announce_network_to_area(
@@ -1526,8 +1481,7 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
OSPF_AREA_RANGE_ADVERTISE)) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_send_nssa_aggregates():"
- " discarding suppress-ranges");
+ "ospf_abr_send_nssa_aggregates(): discarding suppress-ranges");
continue;
}
@@ -1537,8 +1491,7 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_abr_send_nssa_aggregates():"
- " this is range: %s/%d",
+ "ospf_abr_send_nssa_aggregates(): this is range: %s/%d",
inet_ntoa(p.prefix), p.prefixlen);
if (CHECK_FLAG(range->flags,
@@ -1598,8 +1551,7 @@ static void ospf_abr_announce_stub_defaults(struct ospf *ospf)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_stub_defaults(): "
- "announcing 0.0.0.0/0 to area %s",
+ "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);
}
@@ -1614,8 +1566,7 @@ static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf,
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",
+ "ospf_abr_remove_unapproved_translates(): removing unapproved translates, ID: %s",
inet_ntoa(lsa->data->id));
/* FLUSH THROUGHOUT AS */
@@ -1656,8 +1607,7 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf)
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",
+ "ospf_abr_remove_unapproved_summaries(): looking at area %s",
inet_ntoa(area->area_id));
LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa)
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 49730063b9..73ce606504 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -889,8 +889,7 @@ int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserv,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "API: Add LSA-type(%d)/Opaque-type(%d) into"
- " apiserv(%p), total#(%d)",
+ "API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)",
lsa_type, opaque_type, (void *)apiserv,
listcount(apiserv->opaque_types));
@@ -920,8 +919,7 @@ int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "API: Del LSA-type(%d)/Opaque-type(%d)"
- " from apiserv(%p), total#(%d)",
+ "API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)",
lsa_type, opaque_type, (void *)apiserv,
listcount(apiserv->opaque_types));
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 8747dd1f53..d2a30477b0 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -194,20 +194,17 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf,
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.");
+ 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.");
+ 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.");
+ 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;
@@ -219,8 +216,7 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf,
if (rn == NULL)
{
if (IS_DEBUG_OSPF (lsa, LSA))
- zlog_debug ("ospf_ase_calculate(): "
- "Couldn't find a route to the forwarding address.");
+ zlog_debug ("ospf_ase_calculate(): Couldn't find a route to the forwarding address.");
return NULL;
}
@@ -229,8 +225,7 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf,
if ((asbr_route = rn->info) == NULL)
{
if (IS_DEBUG_OSPF (lsa, LSA))
- zlog_debug ("ospf_ase_calculate(): "
- "Somehow OSPF route to ASBR is lost");
+ zlog_debug ("ospf_ase_calculate(): Somehow OSPF route to ASBR is lost");
return NULL;
}
}
@@ -393,8 +388,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa)
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");
+ "Route[External]: Forwarding address is our router address");
return 0;
}
@@ -407,8 +401,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa)
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");
+ "Route[External]: Can't find route to forwarding address");
if (rn)
route_unlock_node(rn);
return 0;
diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c
index b9e78f4cd3..d2c5090f2f 100644
--- a/ospfd/ospf_bfd.c
+++ b/ospfd/ospf_bfd.c
@@ -381,7 +381,7 @@ static void ospf_bfd_if_param_set(struct interface *ifp, uint32_t min_rx,
params = IF_DEF_PARAMS(ifp);
bfd_set_param((struct bfd_info **)&(params->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
if (command)
ospf_bfd_reg_dereg_all_nbr(ifp, command);
}
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index 3dcb2b481d..dcc479def6 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -53,10 +53,11 @@ unsigned long conf_debug_ospf_nssa = 0;
unsigned long conf_debug_ospf_te = 0;
unsigned long conf_debug_ospf_ext = 0;
unsigned long conf_debug_ospf_sr = 0;
+unsigned long conf_debug_ospf_defaultinfo = 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_event;
unsigned long term_debug_ospf_ism = 0;
unsigned long term_debug_ospf_nsm = 0;
unsigned long term_debug_ospf_lsa = 0;
@@ -65,6 +66,7 @@ unsigned long term_debug_ospf_nssa = 0;
unsigned long term_debug_ospf_te = 0;
unsigned long term_debug_ospf_ext = 0;
unsigned long term_debug_ospf_sr = 0;
+unsigned long term_debug_ospf_defaultinfo;
const char *ospf_redist_string(unsigned int route_type)
{
@@ -1447,6 +1449,33 @@ DEFUN (no_debug_ospf_sr,
return CMD_SUCCESS;
}
+DEFUN (debug_ospf_default_info,
+ debug_ospf_default_info_cmd,
+ "debug ospf default-information",
+ DEBUG_STR
+ OSPF_STR
+ "OSPF default information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(defaultinfo, DEFAULTINFO);
+ TERM_DEBUG_ON(defaultinfo, DEFAULTINFO);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_default_info,
+ no_debug_ospf_default_info_cmd,
+ "no debug ospf default-information",
+ NO_STR
+ DEBUG_STR
+ OSPF_STR
+ "OSPF default information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(defaultinfo, DEFAULTINFO);
+ TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO);
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_ospf,
no_debug_ospf_cmd,
"no debug ospf",
@@ -1475,6 +1504,7 @@ DEFUN (no_debug_ospf,
DEBUG_OFF(zebra, ZEBRA);
DEBUG_OFF(zebra, ZEBRA_INTERFACE);
DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE);
+ DEBUG_OFF(defaultinfo, DEFAULTINFO);
for (i = 0; i < 5; i++)
DEBUG_PACKET_OFF(i, flag);
@@ -1501,6 +1531,7 @@ DEFUN (no_debug_ospf,
TERM_DEBUG_OFF(zebra, ZEBRA);
TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE);
TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE);
+ TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO);
return CMD_SUCCESS;
}
@@ -1595,6 +1626,9 @@ static int show_debugging_ospf_common(struct vty *vty, struct ospf *ospf)
" OSPF Zebra redistribute debugging is on\n");
}
+ if (IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == OSPF_DEBUG_DEFAULTINFO)
+ vty_out(vty, "OSPF default information is on\n");
+
/* Show debug status for NSSA. */
if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA)
vty_out(vty, " OSPF NSSA debugging is on\n");
@@ -1797,6 +1831,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &debug_ospf_nssa_cmd);
install_element(ENABLE_NODE, &debug_ospf_te_cmd);
install_element(ENABLE_NODE, &debug_ospf_sr_cmd);
+ install_element(ENABLE_NODE, &debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -1805,6 +1840,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_te_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd);
install_element(ENABLE_NODE, &debug_ospf_packet_cmd);
@@ -1834,6 +1870,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &debug_ospf_nssa_cmd);
install_element(CONFIG_NODE, &debug_ospf_te_cmd);
install_element(CONFIG_NODE, &debug_ospf_sr_cmd);
+ install_element(CONFIG_NODE, &debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
@@ -1841,6 +1878,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_te_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd);
install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd);
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index 6b2ebb125a..8c01977ff8 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -59,6 +59,7 @@
#define OSPF_DEBUG_TE 0x04
#define OSPF_DEBUG_EXT 0x08
#define OSPF_DEBUG_SR 0x10
+#define OSPF_DEBUG_DEFAULTINFO 0x20
/* Macro for setting debug option. */
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
@@ -104,6 +105,8 @@
#define IS_DEBUG_OSPF_SR IS_DEBUG_OSPF(sr, SR)
+#define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO)
+
#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)
@@ -122,6 +125,7 @@ extern unsigned long term_debug_ospf_nssa;
extern unsigned long term_debug_ospf_te;
extern unsigned long term_debug_ospf_ext;
extern unsigned long term_debug_ospf_sr;
+extern unsigned long term_debug_ospf_defaultinfo;
/* Message Strings. */
extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c
index 86088a7137..6dd5d78bd0 100644
--- a/ospfd/ospf_ext.c
+++ b/ospfd/ospf_ext.c
@@ -491,8 +491,7 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
return rc;
if (p != NULL) {
- osr_debug("EXT (%s): Schedule new prefix %pFX with index %u "
- "on interface %s", __func__, p, index, ifp->name);
+ osr_debug("EXT (%s): Schedule new prefix %pFX with index %u on interface %s", __func__, p, index, ifp->name);
/* Set first Extended Prefix then the Prefix SID information */
set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
@@ -1006,8 +1005,7 @@ static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
lsa_header_set(s, options, lsa_type, lsa_id, router_id);
osr_debug(
- "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
- "Prefix Opaque LSA instance",
+ "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Prefix Opaque LSA instance",
__func__, lsa_type, &lsa_id);
/* Set opaque-LSA body fields. */
@@ -1064,8 +1062,7 @@ static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
lsa_id.s_addr = htonl(tmp);
osr_debug(
- "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
- "Link Opaque LSA instance",
+ "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Link Opaque LSA instance",
__func__, lsa_type, &lsa_id);
/* Set opaque-LSA header fields. */
@@ -1130,8 +1127,7 @@ static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
ospf_flood_through_area(area, NULL /*nbr */, new);
osr_debug(
- "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA"
- "Extended Prefix Opaque LSA: Area(%pI4), Link(%s)",
+ "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSAExtended Prefix Opaque LSA: Area(%pI4), Link(%s)",
__func__, new->data->type, &new->data->id,
&area->area_id, exti->ifp->name);
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
@@ -1178,8 +1174,7 @@ static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
ospf_flood_through_area(area, NULL /*nbr */, new);
osr_debug(
- "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA "
- "Extended Link Opaque LSA: Area(%pI4), Link(%s)",
+ "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA Extended Link Opaque LSA: Area(%pI4), Link(%s)",
__func__, new->data->type, &new->data->id,
&area->area_id, exti->ifp->name);
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
@@ -1237,8 +1232,7 @@ static int ospf_ext_pref_lsa_originate(void *arg)
/* Ok, let's try to originate an LSA */
osr_debug(
- "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u "
- "for Itf %s", __func__, exti->instance,
+ "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u for Itf %s", __func__, exti->instance,
exti->ifp ? exti->ifp->name : "");
ospf_ext_pref_lsa_originate1(area, exti);
}
@@ -1300,8 +1294,7 @@ static int ospf_ext_link_lsa_originate(void *arg)
/* Ok, let's try to originate an LSA */
osr_debug(
- "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u "
- "for Itf %s through the Area %pI4", __func__,
+ "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u for Itf %s through the Area %pI4", __func__,
exti->instance, exti->ifp ? exti->ifp->name : "-",
&area->area_id);
ospf_ext_link_lsa_originate1(area, exti);
@@ -1625,8 +1618,7 @@ static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty,
top = (struct ext_subtlv_rmt_itf_addr *)tlvh;
vty_out(vty,
- " Remote Interface Address Sub-TLV: Length %u\n "
- "Address: %s\n",
+ " Remote Interface Address Sub-TLV: Length %u\n Address: %s\n",
ntohs(top->header.length), inet_ntoa(top->value));
return TLV_SIZE(tlvh);
@@ -1639,8 +1631,7 @@ static uint16_t show_vty_ext_link_adj_sid(struct vty *vty,
struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh;
vty_out(vty,
- " Adj-SID Sub-TLV: Length %u\n\tFlags: "
- "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n",
+ " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n",
ntohs(top->header.length), top->flags, top->mtid, top->weight,
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
: "Index",
@@ -1659,9 +1650,7 @@ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty,
(struct ext_subtlv_lan_adj_sid *)tlvh;
vty_out(vty,
- " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
- "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
- "%s\n\t%s: %u\n",
+ " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %s\n\t%s: %u\n",
ntohs(top->header.length), top->flags, top->mtid, top->weight,
inet_ntoa(top->neighbor_id),
CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
@@ -1749,8 +1738,7 @@ static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty,
(struct ext_subtlv_prefix_sid *)tlvh;
vty_out(vty,
- " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: "
- "%u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n",
+ " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n",
ntohs(top->header.length), top->algorithm, top->flags,
top->mtid,
CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label"
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 818e7e72bc..58afb2b392 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -507,8 +507,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
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",
+ "ospf_flood_through_interface(): DR/BDR NOT SEND to int %s",
IF_NAME(oi));
return 1;
}
@@ -522,8 +521,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
if (oi->state == ISM_Backup) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_flood_through_interface(): "
- "ISM_Backup NOT SEND to int %s",
+ "ospf_flood_through_interface(): ISM_Backup NOT SEND to int %s",
IF_NAME(oi));
return 1;
}
@@ -538,8 +536,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi,
/* 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",
+ "ospf_flood_through_interface(): DR/BDR sending upd to int %s",
IF_NAME(oi));
/* RFC2328 Section 13.3
diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c
index f1ba8a31e8..87929e4369 100644
--- a/ospfd/ospf_ia.c
+++ b/ospfd/ospf_ia.c
@@ -88,8 +88,7 @@ static void ospf_ia_network_route(struct ospf *ospf, struct route_table *rt,
if ((or = rn1->info)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_ia_network_route(): "
- "Found a route to the same network");
+ "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. */
@@ -151,8 +150,7 @@ static void ospf_ia_router_route(struct ospf *ospf, struct route_table *rtrs,
if (or) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_ia_router_route(): "
- "a route to the same ABR through the same area exists");
+ "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);
@@ -329,8 +327,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt,
backbone paths */
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_update_network_route(): "
- "Allowing Shortcut ABR to add new route");
+ "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;
@@ -367,8 +364,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt,
or->u.std.area_id)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_update_network_route(): Shortcut: "
- "this intra-area path is not backbone");
+ "ospf_update_network_route(): Shortcut: this intra-area path is not backbone");
return;
}
} else /* Not Shortcut ABR */
@@ -376,8 +372,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt,
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");
+ "ospf_update_network_route(): route is not BB-associated");
return; /* We can update only BB routes */
}
}
@@ -392,16 +387,14 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt,
if (or->cost == cost) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_update_network_route(): "
- "new route is same distance, adding nexthops");
+ "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_update_network_route(): new route is better, overriding nexthops");
ospf_route_subst_nexthops(or, abr_or->paths);
or->cost = cost;
@@ -649,8 +642,7 @@ void ospf_ia_routing(struct ospf *ospf, struct route_table *rt,
*/
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_ia_routing(): "
- "Active BB connection not found");
+ "ospf_ia_routing(): Active BB connection not found");
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node,
area))
OSPF_EXAMINE_SUMMARIES_ALL(area, rt,
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 1622b2fd25..7977a2a9f4 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -839,8 +839,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf,
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");
+ "ospf_vl_new(): Alarm: cannot create more than OSPF_MAX_VL_COUNT virtual links");
return NULL;
}
@@ -1118,8 +1117,7 @@ void ospf_vl_up_check(struct ospf_area *area, struct in_addr rid,
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");
+ "ospf_vl_up_check: VL cost change, scheduling router lsa refresh");
if (ospf->backbone)
ospf_router_lsa_update_area(
ospf->backbone);
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index d089ea76cd..376310e4ff 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -525,8 +525,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi)
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",
+ "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;
@@ -541,8 +540,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *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",
+ "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);
@@ -551,8 +549,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi)
else {
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
zlog_debug(
- "LSA[Type1]: Interface %s has no DR. "
- "Adding stub interface",
+ "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;
@@ -1084,8 +1081,7 @@ static struct ospf_lsa *ospf_network_lsa_refresh(struct ospf_lsa *lsa)
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[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);
}
@@ -1761,8 +1757,7 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf,
== NULL) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_nssa_translate_originate(): Could not originate "
- "Translated Type-5 for %s",
+ "ospf_nssa_translate_originate(): Could not originate Translated Type-5 for %s",
inet_ntoa(ei.p.prefix));
return NULL;
}
@@ -1796,8 +1791,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
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",
+ "ospf_translated_nssa_originate(): Could not translate Type-7, Id %s, to Type-5",
inet_ntoa(type7->data->id));
return NULL;
}
@@ -1806,8 +1800,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
if (IS_DEBUG_OSPF_NSSA) {
zlog_debug(
- "ospf_translated_nssa_originate(): "
- "translated Type 7, installed:");
+ "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",
@@ -1816,9 +1809,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_lsa_translated_nssa_originate(): "
- "Could not install LSA "
- "id %s",
+ "ospf_lsa_translated_nssa_originate(): Could not install LSA id %s",
inet_ntoa(type7->data->id));
return NULL;
}
@@ -1885,8 +1876,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
if (!type7) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
- "ospf_translated_nssa_refresh(): no Type-7 found for "
- "Type-5 LSA Id %s",
+ "ospf_translated_nssa_refresh(): no Type-7 found for Type-5 LSA Id %s",
inet_ntoa(type5->data->id));
return NULL;
}
@@ -1895,8 +1885,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
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",
+ "ospf_translated_nssa_refresh(): No translated Type-5 found for Type-7 with Id %s",
inet_ntoa(type7->data->id));
return NULL;
}
@@ -1908,8 +1897,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
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",
+ "ospf_translated_nssa_refresh(): Could not translate Type-7 for %s to Type-5",
inet_ntoa(type7->data->id));
return NULL;
}
@@ -2032,18 +2020,22 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
static struct external_info *ospf_default_external_info(struct ospf *ospf)
{
int type;
- struct route_node *rn;
struct prefix_ipv4 p;
+ struct external_info *default_ei;
+ int ret = 0;
p.family = AF_INET;
p.prefix.s_addr = 0;
p.prefixlen = 0;
+ default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE,
+ ospf->instance, &p);
+ if (!default_ei)
+ return NULL;
+
/* First, lookup redistributed default route. */
for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) {
struct list *ext_list;
- struct listnode *node;
- struct ospf_external *ext;
if (type == ZEBRA_ROUTE_OSPF)
continue;
@@ -2052,17 +2044,10 @@ static struct external_info *ospf_default_external_info(struct ospf *ospf)
if (!ext_list)
continue;
- for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) {
- rn = route_node_lookup(ext->external_info,
- (struct prefix *)&p);
- if (rn != NULL) {
- route_unlock_node(rn);
- assert(rn->info);
- if (ospf_redistribute_check(ospf, rn->info,
- NULL))
- return rn->info;
- }
- }
+ ret = ospf_external_default_routemap_apply_walk(ospf, ext_list,
+ default_ei);
+ if (ret)
+ return default_ei;
}
return NULL;
@@ -2269,8 +2254,7 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf,
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[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 */);
@@ -2616,8 +2600,7 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) {
zlog_debug(
- "ospf_lsa_install() Premature Aging "
- "lsa 0x%p, seqnum 0x%x",
+ "ospf_lsa_install() Premature Aging lsa 0x%p, seqnum 0x%x",
(void *)lsa,
ntohl(lsa->data->ls_seqnum));
ospf_lsa_header_dump(lsa->data);
@@ -2625,8 +2608,7 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
} 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_install() got an lsa with seq 0x80000000 that was not self originated. Ignoring\n");
ospf_lsa_header_dump(lsa->data);
}
return old;
@@ -3368,8 +3350,7 @@ struct in_addr ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb,
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",
+ "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;
@@ -3386,8 +3367,7 @@ struct in_addr ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb,
if (lsa) {
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
zlog_debug(
- "ospf_lsa_unique_id(): "
- "Can't get Link State ID for %s/%d",
+ "ospf_lsa_unique_id(): Can't get Link State ID for %s/%d",
inet_ntoa(p->prefix),
p->prefixlen);
/* id.s_addr = 0; */
@@ -3550,8 +3530,7 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa)
if (IS_DEBUG_OSPF(lsa, LSA_REFRESH))
zlog_debug(
- "LSA[Refresh:Type%d:%s]: ospf_refresher_register_lsa(): "
- "setting refresh_list on lsa %p (slod %d)",
+ "LSA[Refresh:Type%d:%s]: ospf_refresher_register_lsa(): setting refresh_list on lsa %p (slod %d)",
lsa->data->type, inet_ntoa(lsa->data->id),
(void *)lsa, index);
}
@@ -3609,8 +3588,7 @@ int ospf_lsa_refresh_walker(struct thread *t)
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",
+ "LSA[Refresh]: ospf_lsa_refresh_walker(): refresh index %d",
i);
refresh_list = ospf->lsa_refresh_queue.qs[i];
@@ -3624,8 +3602,7 @@ int ospf_lsa_refresh_walker(struct thread *t)
lsa)) {
if (IS_DEBUG_OSPF(lsa, LSA_REFRESH))
zlog_debug(
- "LSA[Refresh:Type%d:%s]: ospf_lsa_refresh_walker(): "
- "refresh lsa %p (slot %d)",
+ "LSA[Refresh:Type%d:%s]: ospf_lsa_refresh_walker(): refresh lsa %p (slot %d)",
lsa->data->type,
inet_ntoa(lsa->data->id),
(void *)lsa, i);
diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c
index b8e2dac70e..3a1547978a 100644
--- a/ospfd/ospf_network.c
+++ b/ospfd/ospf_network.c
@@ -53,9 +53,7 @@ int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p,
if (ret < 0)
flog_err(
EC_LIB_SOCKET,
- "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?",
+ "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 {
@@ -78,8 +76,7 @@ int ospf_if_drop_allspfrouters(struct ospf *top, struct prefix *p,
ifindex);
if (ret < 0)
flog_err(EC_LIB_SOCKET,
- "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, "
- "ifindex %u, AllSPFRouters): %s",
+ "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 {
@@ -104,9 +101,7 @@ int ospf_if_add_alldrouters(struct ospf *top, struct prefix *p,
if (ret < 0)
flog_err(
EC_LIB_SOCKET,
- "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?",
+ "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
@@ -127,8 +122,7 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p,
ifindex);
if (ret < 0)
flog_err(EC_LIB_SOCKET,
- "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, "
- "ifindex %u, AllDRouters): %s",
+ "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
@@ -167,8 +161,7 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex);
if (ret < 0)
flog_err(EC_LIB_SOCKET,
- "can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, "
- "ifindex %u): %s",
+ "can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, ifindex %u): %s",
top->fd, inet_ntoa(p->u.prefix4), ifindex,
safe_strerror(errno));
#endif
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index 47688babbf..dffbfb7d17 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -683,8 +683,7 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state)
if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
zlog_info(
- "%s:[%s:%s], %s -> %s): "
- "scheduling new router-LSA origination",
+ "%s:[%s:%s], %s -> %s): scheduling new router-LSA origination",
__func__, inet_ntoa(nbr->router_id),
ospf_get_name(oi->ospf),
lookup_msg(ospf_nsm_state_msg, old_state, NULL),
@@ -785,8 +784,7 @@ int ospf_nsm_event(struct thread *thread)
*/
flog_err(
EC_OSPF_FSM_INVALID_STATE,
- "NSM[%s:%s:%s]: %s (%s): "
- "Warning: action tried to change next_state to %s",
+ "NSM[%s:%s:%s]: %s (%s): Warning: action tried to change next_state to %s",
IF_NAME(nbr->oi), inet_ntoa(nbr->router_id),
ospf_get_name(nbr->oi->ospf),
lookup_msg(ospf_nsm_state_msg, nbr->state,
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 35fa5da74b..061ada5b16 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -1781,8 +1781,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
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]",
+ "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;
@@ -1799,8 +1798,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d"
- " ms later: [opaque-type=%u]",
+ "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)));
@@ -1919,8 +1917,7 @@ static int ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t)
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...",
+ "Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...",
oipt->opaque_type);
oipt->status = PROC_SUSPEND;
@@ -1930,8 +1927,7 @@ static int ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "Timer[Type10-LSA]: Re-originate Opaque-LSAs"
- " (opaque-type=%u) for Area %s",
+ "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);
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index a39d19cc5a..49cd42d030 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -53,6 +53,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_zebra.h"
/*
* OSPF Fragmentation / fragmented writes
@@ -604,8 +605,7 @@ static void ospf_write_frags(int fd, struct ospf_packet *op, struct ip *iph,
if (ret < 0)
flog_err(
EC_LIB_SOCKET,
- "*** ospf_write_frags: sendmsg failed to %s,"
- " id %d, off %d, len %d, mtu %u failed with %s",
+ "*** 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));
@@ -798,16 +798,14 @@ static int ospf_write(struct thread *thread)
sockopt_iphdrincl_swab_systoh(&iph);
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_write to %s, "
- "id %d, off %d, len %d, interface %s, mtu %u:",
+ "ospf_write to %s, id %d, off %d, len %d, interface %s, mtu %u:",
inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off,
iph.ip_len, oi->ifp->name, oi->ifp->mtu);
if (ret < 0)
flog_err(
EC_LIB_SOCKET,
- "*** sendmsg in ospf_write failed to %s, "
- "id %d, off %d, len %d, interface %s, mtu %u: %s",
+ "*** 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));
@@ -897,8 +895,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh,
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.",
+ "ospf_header[%s/%s]: selforiginated, dropping.",
lookup_msg(ospf_packet_type_str, ospfh->type,
NULL),
inet_ntoa(iph->ip_src));
@@ -927,8 +924,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh,
/* Compare Router Dead Interval. */
if (OSPF_IF_PARAM(oi, v_wait) != ntohl(hello->dead_interval)) {
flog_warn(EC_OSPF_PACKET,
- "Packet %s [Hello:RECV]: RouterDeadInterval mismatch "
- "(expected %u, but received %u).",
+ "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));
@@ -941,8 +937,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh,
!= ntohs(hello->hello_interval)) {
flog_warn(
EC_OSPF_PACKET,
- "Packet %s [Hello:RECV]: HelloInterval mismatch "
- "(expected %u, but received %u).",
+ "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));
@@ -1213,8 +1208,7 @@ static void ospf_db_desc_proc(struct stream *s, struct ospf_interface *oi,
*/
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "Packet [DD:RECV]: LSA received Type %d, "
- "ID %s is not recent.",
+ "Packet [DD:RECV]: LSA received Type %d, ID %s is not recent.",
lsah->type, inet_ntoa(lsah->id));
ospf_lsa_discard(new);
}
@@ -1402,8 +1396,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh,
if (CHECK_FLAG(oi->ospf->config,
OSPF_LOG_ADJACENCY_DETAIL))
zlog_info(
- "Packet[DD]: Neighbor %s: Initial DBD from Slave, "
- "ignoring.",
+ "Packet[DD]: Neighbor %s: Initial DBD from Slave, ignoring.",
inet_ntoa(nbr->router_id));
break;
}
@@ -1527,8 +1520,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh,
if (IS_SET_DD_MS(nbr->dd_flags)) {
/* Master should discard duplicate DD packet. */
zlog_info(
- "Packet[DD]: Neighbor %s duplicated, "
- "packet discarded.",
+ "Packet[DD]: Neighbor %s duplicated, packet discarded.",
inet_ntoa(nbr->router_id));
break;
} else {
@@ -1848,8 +1840,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
if (nbr->state < NSM_Exchange) {
if (IS_DEBUG_OSPF(nsm, NSM_EVENTS))
zlog_debug(
- "Link State Update: "
- "Neighbor[%s] state %s is less than Exchange",
+ "Link State Update: Neighbor[%s] state %s is less than Exchange",
inet_ntoa(ospfh->router_id),
lookup_msg(ospf_nsm_state_msg, nbr->state,
NULL));
@@ -1948,8 +1939,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
char buf3[INET_ADDRSTRLEN];
flog_err(EC_OSPF_ROUTER_LSA_MISMATCH,
- "Incoming Router-LSA from %s with "
- "Adv-ID[%s] != LS-ID[%s]",
+ "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,
@@ -1959,8 +1949,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
INET_ADDRSTRLEN));
flog_err(
EC_OSPF_DOMAIN_CORRUPT,
- "OSPF domain compromised by attack or corruption. "
- "Verify correct operation of -ALL- OSPF routers.");
+ "OSPF domain compromised by attack or corruption. Verify correct operation of -ALL- OSPF routers.");
DISCARD_LSA(lsa, 0);
}
@@ -2032,8 +2021,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
if (current == NULL) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "LSA[%s]: Previously originated Opaque-LSA,"
- "not found in the LSDB.",
+ "LSA[%s]: Previously originated Opaque-LSA,not found in the LSDB.",
dump_lsa_key(lsa));
SET_FLAG(lsa->flags, OSPF_LSA_SELF);
@@ -2254,8 +2242,7 @@ static void ospf_ls_ack(struct ip *iph, struct ospf_header *ospfh,
if (nbr->state < NSM_Exchange) {
if (IS_DEBUG_OSPF(nsm, NSM_EVENTS))
zlog_debug(
- "Link State Acknowledgment: "
- "Neighbor[%s] state %s is less than Exchange",
+ "Link State Acknowledgment: Neighbor[%s] state %s is less than Exchange",
inet_ntoa(ospfh->router_id),
lookup_msg(ospf_nsm_state_msg, nbr->state,
NULL));
@@ -2322,8 +2309,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd,
if ((unsigned int)ret < sizeof(struct ip)) {
flog_warn(
EC_OSPF_PACKET,
- "ospf_recv_packet: discarding runt packet of length %d "
- "(ip header size is %u)",
+ "ospf_recv_packet: discarding runt packet of length %d (ip header size is %u)",
ret, (unsigned int)sizeof(iph));
return NULL;
}
@@ -2369,8 +2355,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd,
if (ret != ip_len) {
flog_warn(
EC_OSPF_PACKET,
- "ospf_recv_packet read length mismatch: ip_len is %d, "
- "but recvmsg returned %d",
+ "ospf_recv_packet read length mismatch: ip_len is %d, but recvmsg returned %d",
ip_len, ret);
return NULL;
}
@@ -3141,8 +3126,7 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf)
if (ret < 0) {
if (IS_DEBUG_OSPF_PACKET(0, RECV))
zlog_debug(
- "ospf_read[%s]: Header check failed, "
- "dropping.",
+ "ospf_read[%s]: Header check failed, dropping.",
inet_ntoa(iph->ip_src));
return OSPF_READ_CONTINUE;
}
@@ -3992,17 +3976,13 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update,
if (!warned) {
flog_warn(
EC_OSPF_LARGE_LSA,
- "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 ..'");
+ "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!",
+ "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));
@@ -4020,9 +4000,7 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update,
if (size > OSPF_MAX_PACKET_SIZE) {
flog_warn(EC_OSPF_LARGE_LSA,
- "ospf_ls_upd_packet_new: oversized LSA id:%s too big,"
- " %d bytes, packet size %ld, dropping it completely."
- " OSPF routing is broken!",
+ "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);
@@ -4153,8 +4131,7 @@ static int ospf_ls_upd_send_queue_event(struct thread *thread)
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",
+ "ospf_ls_upd_send_queue: update lists not cleared, %d nodes to try again, raising new event",
again);
oi->t_ls_upd_event = NULL;
thread_add_event(master, ospf_ls_upd_send_queue_event, oi, 0,
@@ -4343,21 +4320,10 @@ void ospf_ls_ack_send_delayed(struct ospf_interface *oi)
* punt-to-CPU set on them. This may overload the CPU control path that
* can be avoided if the MAC was known apriori.
*/
-#define OSPF_PING_NBR_STR_MAX (BUFSIZ)
void ospf_proactively_arp(struct ospf_neighbor *nbr)
{
- char ping_nbr[OSPF_PING_NBR_STR_MAX];
- int ret;
-
if (!nbr)
return;
- snprintf(ping_nbr, sizeof(ping_nbr),
- "ping -c 1 -I %s %s > /dev/null 2>&1 &", nbr->oi->ifp->name,
- inet_ntoa(nbr->address.u.prefix4));
-
- ret = system(ping_nbr);
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("Executed %s %s", ping_nbr,
- ((ret == 0) ? "successfully" : "but failed"));
+ ospf_zebra_send_arp(nbr->oi->ifp, &nbr->address);
}
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index a661c80a91..776f50b33a 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -322,8 +322,7 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
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");
+ "ospf_intra_add_router: this router is neither ASBR nor ABR, skipping it");
return;
}
@@ -506,8 +505,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_intra_add_stub(): "
- "another route to the same prefix found with cost %u",
+ "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
@@ -923,16 +921,14 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
if (or->path_type == OSPF_PATH_INTRA_AREA) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_add_discard_route(): "
- "an intra-area route exists");
+ "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");
+ "ospf_add_discard_route(): discard entry already installed");
return 0;
}
@@ -941,8 +937,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_add_discard_route(): "
- "adding %s/%d",
+ "ospf_add_discard_route(): adding %s/%d",
inet_ntoa(p->prefix), p->prefixlen);
new_or = ospf_route_new();
@@ -967,8 +962,7 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt,
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_delete_discard_route(): "
- "deleting %s/%d",
+ "ospf_delete_discard_route(): deleting %s/%d",
inet_ntoa(p->prefix), p->prefixlen);
rn = route_node_lookup(rt, (struct prefix *)p);
@@ -985,16 +979,14 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt,
if (or->path_type == OSPF_PATH_INTRA_AREA) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_delete_discard_route(): "
- "an intra-area route exists");
+ "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");
+ "ospf_delete_discard_route(): not a discard entry");
return;
}
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 64c59c9abf..91fc20475d 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -540,8 +540,7 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
if (IS_DEBUG_OSPF_EVENT) {
zlog_debug(
- "%s: considering link:%s "
- "type:%d link_id:%s link_data:%s",
+ "%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,
@@ -676,8 +675,7 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area,
return 1;
} else
zlog_info(
- "ospf_nexthop_calculation(): "
- "vl_data for VL link not found");
+ "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 */
@@ -1183,8 +1181,7 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area,
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",
+ "ospf_spf_calculate: Skip area %s's calculation due to empty router_lsa_self",
inet_ntoa(area->area_id));
return;
}
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index 1a65bfa411..f2330d6bdd 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -1022,8 +1022,7 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
if ((srn != NULL) && (srn->instance != 0)
&& (srn->instance != ntohl(lsah->id.s_addr))) {
flog_err(EC_OSPF_SR_INVALID_LSA_ID,
- "SR (%s): Abort! Wrong "
- "LSA ID 4.0.0.%u for SR node %pI4/%u",
+ "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4/%u",
__func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
&lsah->adv_router, srn->instance);
return;
@@ -1049,32 +1048,25 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
}
/* update LSA ID */
srn->instance = ntohl(lsah->id.s_addr);
- /* Copy SRGB */
- srn->srgb.range_size = srgb.range_size;
- srn->srgb.lower_bound = srgb.lower_bound;
- /* Set Algorithm */
- if (algo != NULL) {
- int i;
- for (i = 0; i < ntohs(algo->header.length); i++)
- srn->algo[i] = algo->value[0];
- for (; i < ALGORITHM_COUNT; i++)
- srn->algo[i] = SR_ALGORITHM_UNSET;
- } else {
- srn->algo[0] = SR_ALGORITHM_SPF;
- }
- /* set MSD */
- srn->msd = msd;
- return;
}
- /* Check if SRGB has changed */
- if ((srn->srgb.range_size == srgb.range_size)
- && (srn->srgb.lower_bound == srgb.lower_bound))
- return;
+ /* Set Algorithm */
+ if (algo != NULL) {
+ int i;
+ for (i = 0; i < ntohs(algo->header.length); i++)
+ srn->algo[i] = algo->value[0];
+ for (; i < ALGORITHM_COUNT; i++)
+ srn->algo[i] = SR_ALGORITHM_UNSET;
+ } else {
+ srn->algo[0] = SR_ALGORITHM_SPF;
+ }
- /* Update SRGB ... */
+ srn->msd = msd;
+
+ /* Copy SRGB */
srn->srgb.range_size = srgb.range_size;
srn->srgb.lower_bound = srgb.lower_bound;
+
/* ... and NHLFE if it is a neighbor SR node */
if (srn->neighbor == OspfSR.self)
hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn);
@@ -1638,8 +1630,7 @@ void ospf_sr_config_write_router(struct vty *vty)
for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node,
srp)) {
vty_out(vty,
- " segment-routing prefix %s/%u "
- "index %u%s\n",
+ " segment-routing prefix %s/%u index %u%s\n",
inet_ntoa(srp->prefv4.prefix),
srp->prefv4.prefixlen, srp->sid,
CHECK_FLAG(srp->flags,
@@ -1665,8 +1656,7 @@ DEFUN(ospf_sr_enable,
if (ospf->vrf_id != VRF_DEFAULT) {
vty_out(vty,
- "Segment Routing is only supported in default "
- "VRF\n");
+ "Segment Routing is only supported in default VRF\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -1954,8 +1944,7 @@ DEFUN (sr_prefix_sid,
*/
listnode_add(OspfSR.self->ext_prefix, new);
zlog_info(
- "Interface for prefix %pFX not found. Deferred LSA "
- "flooding",
+ "Interface for prefix %pFX not found. Deferred LSA flooding",
&p);
return CMD_SUCCESS;
}
@@ -2245,11 +2234,9 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
if (!json) {
sbuf_push(&sbuf, 0,
- "\n\n Prefix or Link Node or Adj. SID "
- " Label Operation Interface Nexthop\n");
+ "\n\n Prefix or Link Node or Adj. SID Label Operation Interface Nexthop\n");
sbuf_push(&sbuf, 0,
- "------------------ --------------------- "
- "-------------------- --------- ---------------\n");
+ "------------------ --------------------- -------------------- --------- ---------------\n");
}
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
if (json) {
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 4093837ba7..e8cc50c8d0 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -313,8 +313,7 @@ DEFPY (ospf_router_id,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect,"
- " save config and restart ospfd\n");
+ "For this router-id change to take effect, save config and restart ospfd\n");
return CMD_SUCCESS;
}
@@ -347,8 +346,7 @@ DEFUN_HIDDEN (ospf_router_id_old,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect,"
- " save config and restart ospfd\n");
+ "For this router-id change to take effect, save config and restart ospfd\n");
return CMD_SUCCESS;
}
@@ -381,8 +379,7 @@ DEFPY (no_ospf_router_id,
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
if (area->full_nbrs) {
vty_out(vty,
- "For this router-id change to take effect,"
- " save config and restart ospfd\n");
+ "For this router-id change to take effect, save config and restart ospfd\n");
return CMD_SUCCESS;
}
@@ -700,6 +697,8 @@ DEFUN (ospf_area_range,
str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE);
+ ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
+ format);
if (argc > 5) {
cost = strtoul(argv[idx_cost]->arg, NULL, 10);
ospf_area_range_cost_set(ospf, area_id, &p, cost);
@@ -1178,9 +1177,9 @@ DEFUN (no_ospf_area_vlink,
"no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
NO_STR
VLINK_HELPSTR_IPADDR
- "Enable authentication on this virtual link\n" \
- "Use message-digest authentication\n" \
- "Use null authentication\n" \
+ "Enable authentication on this virtual link\n"
+ "Use message-digest authentication\n"
+ "Use null authentication\n"
VLINK_HELPSTR_AUTH_MD5
VLINK_HELPSTR_AUTH_SIMPLE)
{
@@ -1385,8 +1384,7 @@ DEFUN (ospf_area_shortcut,
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\n");
+ "Shortcut area setting will take effect only when the router is configured as Shortcut ABR\n");
return CMD_SUCCESS;
}
@@ -1718,8 +1716,7 @@ DEFUN (ospf_area_default_cost,
p.prefixlen = 0;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_stub_defaults(): "
- "announcing 0.0.0.0/0 to area %s",
+ "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);
@@ -1762,8 +1759,7 @@ DEFUN (no_ospf_area_default_cost,
p.prefixlen = 0;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "ospf_abr_announce_stub_defaults(): "
- "announcing 0.0.0.0/0 to area %s",
+ "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);
@@ -2712,8 +2708,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area,
area->act_ints);
} else
vty_out(vty,
- " Number of interfaces in this area: Total: %d, "
- "Active: %d\n",
+ " Number of interfaces in this area: Total: %d, Active: %d\n",
listcount(area->oiflist), area->act_ints);
if (area->external_routing == OSPF_AREA_NSSA) {
@@ -2871,8 +2866,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area,
} else {
/* Show number of fully adjacent neighbors. */
vty_out(vty,
- " Number of fully adjacent neighbors in this area:"
- " %d\n",
+ " Number of fully adjacent neighbors in this area: %d\n",
area->full_nbrs);
/* Show authentication type. */
@@ -2886,8 +2880,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area,
if (!OSPF_IS_AREA_BACKBONE(area))
vty_out(vty,
- " Number of full virtual adjacencies going through"
- " this area: %d\n",
+ " Number of full virtual adjacencies going through this area: %d\n",
area->full_vls);
/* Show SPF calculation times. */
@@ -3173,8 +3166,7 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf,
"injectingExternalRoutingInformation");
else
vty_out(vty,
- " This router is an ASBR "
- "(injecting external routing information)\n");
+ " This router is an ASBR (injecting external routing information)\n");
}
/* Show Number of AS-external-LSAs. */
@@ -10205,8 +10197,7 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf)
->auth_crypt,
n2, ck))
vty_out(vty,
- " area %s virtual-link %s"
- " message-digest-key %d md5 %s\n",
+ " area %s virtual-link %s message-digest-key %d md5 %s\n",
buf, inet_ntoa(vl_data->vl_peer),
ck->key_id, ck->auth_key);
}
@@ -10385,8 +10376,7 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
/* 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\n");
+ "! Important: ensure reference bandwidth is consistent across all routers\n");
vty_out(vty, " auto-cost reference-bandwidth %d\n",
ospf->ref_bandwidth);
}
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 644ea7f922..2b8769a4a8 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -56,6 +56,7 @@ DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table")
DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute")
DEFINE_MTYPE_STATIC(OSPFD, OSPF_DIST_ARGS, "OSPF Distribute arguments")
+
/* Zebra structure to hold current status. */
struct zclient *zclient = NULL;
@@ -275,7 +276,7 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
count++;
if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) {
- char buf[2][INET_ADDRSTRLEN];
+ char buf[2][PREFIX2STR_BUFFER];
struct interface *ifp;
ifp = if_lookup_by_index(path->ifindex, ospf->vrf_id);
@@ -397,6 +398,101 @@ struct ospf_external *ospf_external_add(struct ospf *ospf, uint8_t type,
return ext;
}
+/*
+ * Walk all the ei received from zebra for a route type and apply
+ * default route-map.
+ */
+bool ospf_external_default_routemap_apply_walk(struct ospf *ospf,
+ struct list *ext_list,
+ struct external_info *default_ei)
+{
+ struct listnode *node;
+ struct ospf_external *ext;
+ struct route_node *rn;
+ struct external_info *ei = NULL;
+ int ret = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) {
+ if (!ext->external_info)
+ continue;
+
+ for (rn = route_top(ext->external_info); rn;
+ rn = route_next(rn)) {
+ ei = rn->info;
+ if (!ei)
+ continue;
+ ret = ospf_external_info_apply_default_routemap(
+ ospf, ei, default_ei);
+ if (ret)
+ break;
+ }
+ }
+
+ if (ret && ei) {
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Default originate routemap permit ei: %s",
+ inet_ntoa(ei->p.prefix));
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Function to originate or flush default after applying
+ * route-map on all ei.
+ */
+static int ospf_external_lsa_default_routemap_timer(struct thread *thread)
+{
+ struct list *ext_list;
+ struct ospf *ospf = THREAD_ARG(thread);
+ struct prefix_ipv4 p;
+ int type;
+ int ret = 0;
+ struct ospf_lsa *lsa;
+ struct external_info *default_ei;
+
+ p.family = AF_INET;
+ p.prefixlen = 0;
+ p.prefix.s_addr = INADDR_ANY;
+
+ /* Get the default extenal info. */
+ default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE,
+ ospf->instance, &p);
+ if (!default_ei) {
+ /* Nothing to be done here. */
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Default originate info not present");
+ return 0;
+ }
+
+ /* For all the ei apply route-map */
+ for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) {
+ ext_list = ospf->external[type];
+ if (!ext_list || type == ZEBRA_ROUTE_OSPF)
+ continue;
+
+ ret = ospf_external_default_routemap_apply_walk(ospf, ext_list,
+ default_ei);
+ if (ret)
+ break;
+ }
+
+ /* Get the default LSA. */
+ lsa = ospf_external_info_find_lsa(ospf, &p);
+
+ /* If permit then originate default. */
+ if (ret && !lsa)
+ ospf_external_lsa_originate(ospf, default_ei);
+ else if (ret && lsa && IS_LSA_MAXAGE(lsa))
+ ospf_external_lsa_refresh(ospf, lsa, default_ei, true);
+ else if (!ret && lsa)
+ ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &default_ei->p, 0);
+
+ return 1;
+}
+
+
void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance)
{
struct ospf_external *ext;
@@ -414,6 +510,12 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance)
XFREE(MTYPE_OSPF_EXTERNAL, ext);
}
+
+ /*
+ * Check if default needs to be flushed too.
+ */
+ thread_add_event(master, ospf_external_lsa_default_routemap_timer, ospf,
+ 0, &ospf->t_default_routemap_timer);
}
/* Update NHLFE for Prefix SID */
@@ -770,8 +872,7 @@ static int ospf_external_lsa_originate_check(struct ospf *ospf,
/* 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",
+ "LSA[Type5:%s]: Not originate AS-external-LSA, Prefix belongs multicast",
inet_ntoa(ei->p.prefix));
return 0;
}
@@ -780,8 +881,7 @@ static int ospf_external_lsa_originate_check(struct ospf *ospf,
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");
+ "LSA[Type5:0.0.0.0]: Not originate AS-external-LSA for default");
return 0;
}
@@ -801,6 +901,132 @@ int ospf_distribute_check_connected(struct ospf *ospf, struct external_info *ei)
return 1;
}
+
+/* Apply default route-map on ei received. */
+int ospf_external_info_apply_default_routemap(struct ospf *ospf,
+ struct external_info *ei,
+ struct external_info *default_ei)
+{
+ struct ospf_redist *red;
+ int type = default_ei->type;
+ struct prefix_ipv4 *p = &ei->p;
+ struct route_map_set_values save_values;
+
+
+ if (!ospf_external_lsa_originate_check(ospf, default_ei))
+ return 0;
+
+ save_values = default_ei->route_map_set;
+ ospf_reset_route_map_set_values(&default_ei->route_map_set);
+
+ /* apply route-map if needed */
+ red = ospf_redist_lookup(ospf, type, ospf->instance);
+ if (red && ROUTEMAP_NAME(red)) {
+ route_map_result_t ret;
+
+ ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p,
+ RMAP_OSPF, ei);
+
+ if (ret == RMAP_DENYMATCH) {
+ ei->route_map_set = save_values;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+ * Default originated is based on route-map condition then
+ * apply route-map on received external info. Originate or
+ * flush based on route-map condition.
+ */
+static bool ospf_external_lsa_default_routemap_apply(struct ospf *ospf,
+ struct external_info *ei,
+ int cmd)
+{
+ struct external_info *default_ei;
+ struct prefix_ipv4 p;
+ struct ospf_lsa *lsa;
+ int ret;
+
+ p.family = AF_INET;
+ p.prefixlen = 0;
+ p.prefix.s_addr = INADDR_ANY;
+
+
+ /* Get the default extenal info. */
+ default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE,
+ ospf->instance, &p);
+ if (!default_ei) {
+ /* Nothing to be done here. */
+ return false;
+ }
+
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Apply default originate routemap on ei: %s cmd: %d",
+ inet_ntoa(ei->p.prefix), cmd);
+
+ ret = ospf_external_info_apply_default_routemap(ospf, ei, default_ei);
+
+ /* If deny then nothing to be done both in add and del case. */
+ if (!ret) {
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Default originte routemap deny for ei: %s",
+ inet_ntoa(ei->p.prefix));
+ return false;
+ }
+
+ /* Get the default LSA. */
+ lsa = ospf_external_info_find_lsa(ospf, &p);
+
+ /* If this is add route and permit then ooriginate default. */
+ if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
+ /* If permit and default already advertise then return. */
+ if (lsa && !IS_LSA_MAXAGE(lsa)) {
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Defult lsa already originated");
+ return true;
+ }
+
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug("Originating/Refreshing default lsa");
+
+ if (lsa && IS_LSA_MAXAGE(lsa))
+ /* Refresh lsa.*/
+ ospf_external_lsa_refresh(ospf, lsa, default_ei, true);
+ else
+ /* If permit and default not advertised then advertise.
+ */
+ ospf_external_lsa_originate(ospf, default_ei);
+
+ } else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) {
+ /* If deny and lsa is not originated then nothing to be done.*/
+ if (!lsa) {
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug(
+ "Default lsa not originated, not flushing");
+ return true;
+ }
+
+ if (IS_DEBUG_OSPF_DEFAULT_INFO)
+ zlog_debug(
+ "Running default route-map again as ei: %s deleted",
+ inet_ntoa(ei->p.prefix));
+ /*
+ * if this route delete was permitted then we need to check
+ * there are any other external info which can still trigger
+ * default route origination else flush it.
+ */
+ thread_add_event(master,
+ ospf_external_lsa_default_routemap_timer, ospf,
+ 0, &ospf->t_default_routemap_timer);
+ }
+
+ return true;
+}
+
/* return 1 if external LSA must be originated, 0 otherwise */
int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei,
int *changed)
@@ -812,6 +1038,10 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei,
unsigned short instance = is_prefix_default(&ei->p) ? 0 : ei->instance;
route_tag_t saved_tag = 0;
+ /* Default is handled differently. */
+ if (type == DEFAULT_ROUTE)
+ return 1;
+
if (changed)
*changed = 0;
@@ -1004,8 +1234,24 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
}
}
}
+
+ /*
+ * Check if default-information originate is
+ * with some routemap prefix/access list match.
+ */
+ ospf_external_lsa_default_routemap_apply(ospf, ei, cmd);
+
} else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */
{
+ /*
+ * Check if default-information originate is
+ * with some routemap prefix/access list match.
+ * Apply before ei is deleted.
+ */
+ ei = ospf_external_info_lookup(ospf, rt_type, api.instance, &p);
+ if (ei)
+ ospf_external_lsa_default_routemap_apply(ospf, ei, cmd);
+
ospf_external_info_delete(ospf, rt_type, api.instance, p);
if (is_prefix_default(&p))
ospf_external_lsa_refresh_default(ospf);
@@ -1014,6 +1260,7 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
ifindex /*, nexthop */);
}
+
return 0;
}
@@ -1494,3 +1741,8 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance)
prefix_list_add_hook(ospf_prefix_list_update);
prefix_list_delete_hook(ospf_prefix_list_update);
}
+
+void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
+{
+ zclient_send_neigh_discovery_req(zclient, ifp, p);
+}
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
index 253d2e0a3f..6a79f39fa4 100644
--- a/ospfd/ospf_zebra.h
+++ b/ospfd/ospf_zebra.h
@@ -41,6 +41,7 @@ struct ospf_distance {
};
/* Prototypes */
+struct ospf_route;
extern void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *,
struct ospf_route *);
extern void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *,
@@ -92,5 +93,13 @@ extern int ospf_distance_unset(struct vty *, struct ospf *, const char *,
extern void ospf_zebra_init(struct thread_master *, unsigned short);
extern void ospf_zebra_vrf_register(struct ospf *ospf);
extern void ospf_zebra_vrf_deregister(struct ospf *ospf);
+bool ospf_external_default_routemap_apply_walk(
+ struct ospf *ospf, struct list *ext_list,
+ struct external_info *default_ei);
+int ospf_external_info_apply_default_routemap(struct ospf *ospf,
+ struct external_info *ei,
+ struct external_info *default_ei);
+extern void ospf_zebra_send_arp(const struct interface *ifp,
+ const struct prefix *p);
#endif /* _ZEBRA_OSPF_ZEBRA_H */
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index ea9c7c58c8..f9cc474d5c 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -686,6 +686,7 @@ static void ospf_finish_final(struct ospf *ospf)
OSPF_TIMER_OFF(ospf->t_lsa_refresher);
OSPF_TIMER_OFF(ospf->t_opaque_lsa_self);
OSPF_TIMER_OFF(ospf->t_sr_update);
+ OSPF_TIMER_OFF(ospf->t_default_routemap_timer);
LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa)
ospf_discard_from_db(ospf, ospf->lsdb, lsa);
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 5e91e6f8e6..cdeaa38dc0 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -249,6 +249,8 @@ struct ospf {
struct thread *t_write;
#define OSPF_WRITE_INTERFACE_COUNT_DEFAULT 20
+ struct thread *t_default_routemap_timer;
+
int write_oi_count; /* Num of packets sent per thread invocation */
struct thread *t_read;
int fd;
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c
index edc3f1d8da..10a75a9f54 100644
--- a/pbrd/pbr_map.c
+++ b/pbrd/pbr_map.c
@@ -444,6 +444,59 @@ static void pbr_map_add_interfaces(struct pbr_map *pbrm)
}
}
+/* Decodes a standardized DSCP into its representative value */
+uint8_t pbr_map_decode_dscp_enum(const char *name)
+{
+ /* Standard Differentiated Services Field Codepoints */
+ if (!strcmp(name, "cs0"))
+ return 0;
+ if (!strcmp(name, "cs1"))
+ return 8;
+ if (!strcmp(name, "cs2"))
+ return 16;
+ if (!strcmp(name, "cs3"))
+ return 24;
+ if (!strcmp(name, "cs4"))
+ return 32;
+ if (!strcmp(name, "cs5"))
+ return 40;
+ if (!strcmp(name, "cs6"))
+ return 48;
+ if (!strcmp(name, "cs7"))
+ return 56;
+ if (!strcmp(name, "af11"))
+ return 10;
+ if (!strcmp(name, "af12"))
+ return 12;
+ if (!strcmp(name, "af13"))
+ return 14;
+ if (!strcmp(name, "af21"))
+ return 18;
+ if (!strcmp(name, "af22"))
+ return 20;
+ if (!strcmp(name, "af23"))
+ return 22;
+ if (!strcmp(name, "af31"))
+ return 26;
+ if (!strcmp(name, "af32"))
+ return 28;
+ if (!strcmp(name, "af33"))
+ return 30;
+ if (!strcmp(name, "af41"))
+ return 34;
+ if (!strcmp(name, "af42"))
+ return 36;
+ if (!strcmp(name, "af43"))
+ return 38;
+ if (!strcmp(name, "ef"))
+ return 46;
+ if (!strcmp(name, "voice-admit"))
+ return 44;
+
+ /* No match? Error out */
+ return -1;
+}
+
struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
{
struct pbr_map *pbrm;
@@ -547,7 +600,7 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms)
{
- if (!pbrms->src && !pbrms->dst && !pbrms->mark)
+ if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield)
pbrms->reason |= PBR_MAP_INVALID_EMPTY;
}
@@ -603,7 +656,7 @@ bool pbr_map_check_valid(const char *name)
return pbrm->valid;
}
-void pbr_map_schedule_policy_from_nhg(const char *nh_group)
+void pbr_map_schedule_policy_from_nhg(const char *nh_group, bool installed)
{
struct pbr_map_sequence *pbrms;
struct pbr_map *pbrm;
@@ -618,7 +671,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group)
if (pbrms->nhgrp_name
&& (strcmp(nh_group, pbrms->nhgrp_name) == 0)) {
- pbrms->nhs_installed = true;
+ pbrms->nhs_installed = installed;
pbr_map_check(pbrms, false);
}
@@ -626,7 +679,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group)
if (pbrms->nhg
&& (strcmp(nh_group, pbrms->internal_nhg_name)
== 0)) {
- pbrms->nhs_installed = true;
+ pbrms->nhs_installed = installed;
pbr_map_check(pbrms, false);
}
diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h
index 41f1703954..64c090d2e8 100644
--- a/pbrd/pbr_map.h
+++ b/pbrd/pbr_map.h
@@ -89,6 +89,7 @@ struct pbr_map_sequence {
*/
struct prefix *src;
struct prefix *dst;
+ uint8_t dsfield;
uint32_t mark;
/*
@@ -168,6 +169,8 @@ extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp);
extern void pbr_map_interface_delete(struct pbr_map *pbrm,
struct interface *ifp);
+extern uint8_t pbr_map_decode_dscp_enum(const char *name);
+
/* Update maps installed on interface */
extern void pbr_map_policy_interface_update(const struct interface *ifp,
bool state_up);
@@ -194,7 +197,8 @@ extern void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed);
extern void pbr_map_check_nh_group_change(const char *nh_group);
extern void pbr_map_reason_string(unsigned int reason, char *buf, int size);
-extern void pbr_map_schedule_policy_from_nhg(const char *nh_group);
+extern void pbr_map_schedule_policy_from_nhg(const char *nh_group,
+ bool installed);
extern void pbr_map_install(struct pbr_map *pbrm);
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index 98be958fce..31da656793 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -328,27 +328,29 @@ static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
}
#endif
+static void
+pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache *pnhgc,
+ uint32_t table_id, bool installed)
+{
+ if (pnhgc->table_id == table_id) {
+ DEBUGD(&pbr_dbg_nht, "%s: %s: Table ID (%u) matches %s",
+ __func__, (installed ? "install" : "remove"), table_id,
+ pnhgc->name);
+
+ pnhgc->installed = installed;
+ pnhgc->valid = installed;
+ pbr_map_schedule_policy_from_nhg(pnhgc->name, pnhgc->installed);
+ }
+}
+
static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b,
void *data)
{
struct pbr_nexthop_group_cache *pnhgc =
(struct pbr_nexthop_group_cache *)b->data;
- uint32_t *table_id = (uint32_t *)data;
-
- if (pnhgc->table_id == *table_id) {
- DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", __func__,
- *table_id, pnhgc->name);
+ uint32_t table_id = *(uint32_t *)data;
- /*
- * If the table has been re-handled by zebra
- * and we are already installed no need to do
- * anything here.
- */
- if (!pnhgc->installed) {
- pnhgc->installed = true;
- pbr_map_schedule_policy_from_nhg(pnhgc->name);
- }
- }
+ pbr_nht_find_nhg_from_table_update(pnhgc, table_id, true);
}
void pbr_nht_route_installed_for_table(uint32_t table_id)
@@ -360,7 +362,11 @@ void pbr_nht_route_installed_for_table(uint32_t table_id)
static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b,
void *data)
{
- ;
+ struct pbr_nexthop_group_cache *pnhgc =
+ (struct pbr_nexthop_group_cache *)b->data;
+ uint32_t table_id = *(uint32_t *)data;
+
+ pbr_nht_find_nhg_from_table_update(pnhgc, table_id, false);
}
void pbr_nht_route_removed_for_table(uint32_t table_id)
@@ -852,12 +858,15 @@ static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data)
*/
pnhgc->valid = !!pnhi.valid;
- if (pnhgc->valid) {
- pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
+ pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc);
+
+ if (pnhgc->valid)
pbr_nht_install_nexthop_group(pnhgc, nhg);
- /* Don't need copied nexthops anymore */
- nexthops_free(nhg.nexthop);
- }
+ else
+ pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0);
+
+ /* Don't need copied nexthops anymore */
+ nexthops_free(nhg.nexthop);
if (old_valid != pnhgc->valid)
pbr_map_check_nh_group_change(pnhgc->name);
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index cd9096cbc8..a73d885ea6 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -183,6 +183,91 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
return CMD_SUCCESS;
}
+DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
+ "[no] match dscp DSCP$dscp",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP DSCP field\n"
+ "DSCP value (below 64) or standard codepoint name\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ char dscpname[100];
+ uint8_t rawDscp;
+
+ /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */
+ bool isANumber = true;
+ for (int i = 0; i < (int)strlen(dscp); i++) {
+ /* Letters are not numbers */
+ if (!isdigit(dscp[i]))
+ isANumber = false;
+
+ /* Lowercase the dscp enum (if needed) */
+ if (isupper(dscp[i]))
+ dscpname[i] = tolower(dscp[i]);
+ else
+ dscpname[i] = dscp[i];
+ }
+ dscpname[strlen(dscp)] = '\0';
+
+ if (isANumber) {
+ /* dscp passed is a regular number */
+ long dscpAsNum = strtol(dscp, NULL, 0);
+
+ if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) {
+ /* Refuse to install on overflow */
+ vty_out(vty, "dscp (%s) must be less than 64\n", dscp);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rawDscp = dscpAsNum;
+ } else {
+ /* check dscp if it is an enum like cs0 */
+ rawDscp = pbr_map_decode_dscp_enum(dscpname);
+ if (rawDscp > PBR_DSFIELD_DSCP) {
+ vty_out(vty, "Invalid dscp value: %s\n", dscpname);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (!no) {
+ if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
+ return CMD_SUCCESS;
+
+ /* Set the DSCP bits of the DSField */
+ pbrms->dsfield =
+ (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
+ "[no] match ecn (0-3)$ecn",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP ECN field\n"
+ "Explicit Congestion Notification\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!no) {
+ if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
+ return CMD_SUCCESS;
+
+ /* Set the ECN bits of the DSField */
+ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
"[no] match mark (1-4294967295)$mark",
NO_STR
@@ -559,6 +644,12 @@ static void vty_show_pbrms(struct vty *vty,
if (pbrms->dst)
vty_out(vty, " DST Match: %s\n",
prefix2str(pbrms->dst, buf, sizeof(buf)));
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " DSCP Match: %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " ECN Match: %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
if (pbrms->mark)
vty_out(vty, " MARK Match: %u\n", pbrms->mark);
@@ -653,6 +744,12 @@ static void vty_json_pbrms(json_object *j, struct vty *vty,
prefix2str(pbrms->dst, buf, sizeof(buf)));
if (pbrms->mark)
json_object_int_add(jpbrm, "matchMark", pbrms->mark);
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ json_object_int_add(jpbrm, "matchDscp",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ json_object_int_add(jpbrm, "matchEcn",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
json_object_array_add(j, jpbrm);
}
@@ -946,6 +1043,14 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
vty_out(vty, " match dst-ip %s\n",
prefix2str(pbrms->dst, buff, sizeof(buff)));
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " match dscp %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " match ecn %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
+
if (pbrms->mark)
vty_out(vty, " match mark %u\n", pbrms->mark);
@@ -1026,6 +1131,8 @@ void pbr_vty_init(void)
install_element(INTERFACE_NODE, &pbr_policy_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index de2a99e269..d0099a46e3 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -536,6 +536,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
stream_putw(s, 0); /* src port */
pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
stream_putw(s, 0); /* dst port */
+ stream_putc(s, pbrms->dsfield);
stream_putl(s, pbrms->mark);
if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c
index 0df8ea6922..146b53fa8f 100644
--- a/pimd/pim_bfd.c
+++ b/pimd/pim_bfd.c
@@ -194,7 +194,7 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx,
if (!pim_ifp)
return;
- bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult,
+ bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult, NULL,
defaults, &command);
if (pim_ifp->bfd_info) {
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index d949c657bd..1c9005588f 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -1283,8 +1283,7 @@ int pim_bsm_process(struct interface *ifp, struct ip *ip_hdr, uint8_t *buf,
pim_inet4_dump("<bsr?>", bshdr->bsr_addr.addr, bsr_str,
sizeof(bsr_str));
if (bshdr->hm_len > 32) {
- zlog_warn("Bad hashmask length for IPv4; got %" PRIu8
- ", expected value in range 0-32",
+ zlog_warn("Bad hashmask length for IPv4; got %hhu, expected value in range 0-32",
bshdr->hm_len);
pim->bsm_dropped++;
return -1;
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 6919ed9a6d..db3f0b8b23 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -4979,8 +4979,7 @@ static void pim_show_mlag_help_string(struct vty *vty, bool uj)
if (!uj) {
vty_out(vty, "Owner codes:\n");
vty_out(vty,
- "L: EVPN-MLAG Entry, I:PIM-MLAG Entry, "
- "P: Peer Entry\n");
+ "L: EVPN-MLAG Entry, I:PIM-MLAG Entry, P: Peer Entry\n");
}
}
@@ -6785,12 +6784,13 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty,
if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) {
vty_out(vty, "%% Inconsistent address and mask: %s\n",
- group);
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
if (result == PIM_GROUP_BAD_ADDRESS) {
- vty_out(vty, "%% Bad group address specified: %s\n", group);
+ vty_out(vty, "%% Bad group address specified: %s\n",
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -7159,7 +7159,8 @@ static int pim_no_rp_cmd_worker(struct pim_instance *pim, struct vty *vty,
int result = pim_rp_del_config(pim, rp, group, plist);
if (result == PIM_GROUP_BAD_ADDRESS) {
- vty_out(vty, "%% Bad group address specified: %s\n", group);
+ vty_out(vty, "%% Bad group address specified: %s\n",
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -8404,8 +8405,7 @@ DEFUN_HIDDEN (interface_ip_pim_ssm,
}
vty_out(vty,
- "WARN: Enabled PIM SM on interface; configure PIM SSM "
- "range if needed\n");
+ "WARN: Enabled PIM SM on interface; configure PIM SSM range if needed\n");
return CMD_SUCCESS;
}
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index b79fb689dc..88bcc48f80 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -341,7 +341,7 @@ pim_sec_addr_find(struct pim_interface *pim_ifp, struct prefix *addr)
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
- if (prefix_cmp(&sec_addr->addr, addr)) {
+ if (prefix_cmp(&sec_addr->addr, addr) == 0) {
return sec_addr;
}
}
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index 89fd2bd215..212c77c039 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -139,8 +139,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
if (PIM_I_am_DualActive(pim_ifp)) {
if (PIM_DEBUG_MLAG)
zlog_debug(
- "%s: if-chnanel-%s is deleted from a Dual "
- "active Interface",
+ "%s: if-chnanel-%s is deleted from a Dual active Interface",
__func__, ch->sg_str);
/* Post Delete only if it is the last Dual-active Interface */
if (ch->upstream->dualactive_ifchannel_count == 1) {
@@ -214,8 +213,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
else {
if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
- "%s: Avoiding deletion of upstream with ref_count %d "
- "from ifchannel(%s): %s",
+ "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
__func__, ch->upstream->ref_count,
ch->interface->name, ch->sg_str);
}
@@ -622,8 +620,7 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
}
if (PIM_DEBUG_MLAG)
zlog_debug(
- "%s: New Dual active if-chnanel is added to upstream:%s "
- "count:%d, flags:0x%x",
+ "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
__func__, up->sg_str,
up->dualactive_ifchannel_count, up->flags);
}
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 851b00b2ac..a8612f91fa 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -351,9 +351,7 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
*/
if (query_version != pim_ifp->igmp_version) {
zlog_warn(
- "Recv IGMP query v%d from %s on %s but we are using v%d, please "
- "configure all PIM routers on this subnet to use the same "
- "IGMP version",
+ "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
query_version, from_str, ifp->name,
pim_ifp->igmp_version);
return 0;
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
index cac6fab271..9e78b76008 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -231,9 +231,7 @@ static void mtrace_debug(struct pim_interface *pim_ifp,
ra = mtracep->rsp_addr;
zlog_debug(
- "Rx mtrace packet incoming on %s: "
- "hops=%d type=%d size=%d, grp=%s, src=%s,"
- " dst=%s rsp=%s ttl=%d qid=%ud",
+ "Rx mtrace packet incoming on %s: hops=%d type=%d size=%d, grp=%s, src=%s, dst=%s rsp=%s ttl=%d qid=%ud",
inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
sizeof(inc_str)),
mtracep->hops, mtracep->type, mtrace_len,
@@ -255,8 +253,7 @@ static void mtrace_debug(struct pim_interface *pim_ifp,
if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
if (PIM_DEBUG_MTRACE)
zlog_debug(
- "Mtrace response block of wrong"
- " length");
+ "Mtrace response block of wrong length");
responses = responses / sizeof(struct igmp_mtrace_rsp);
@@ -358,17 +355,14 @@ static int mtrace_send_packet(struct interface *ifp,
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Send mtrace request failed for %s on"
- "%s: group=%s msg_size=%zd: errno=%d: "
- " %s",
+ "Send mtrace request failed for %s on%s: group=%s msg_size=%zd: errno=%d: %s",
dst_str, ifp->name, group_str,
mtrace_buf_len, errno,
safe_strerror(errno));
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Send mtrace request failed for %s on"
- " %s: group=%s msg_size=%zd: sent=%zd",
+ "Send mtrace request failed for %s on %s: group=%s msg_size=%zd: sent=%zd",
dst_str, ifp->name, group_str,
mtrace_buf_len, sent);
}
@@ -418,8 +412,7 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
close(fd);
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Dropping mtrace packet, "
- "no route to destination");
+ "Dropping mtrace packet, no route to destination");
return -1;
}
@@ -448,8 +441,7 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
if (sent < 0) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Failed to forward mtrace packet:"
- " sendto errno=%d, %s",
+ "Failed to forward mtrace packet: sendto errno=%d, %s",
errno, safe_strerror(errno));
return -1;
}
@@ -480,8 +472,7 @@ static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
- "Dropping mtrace multicast packet "
- "len=%u to %s ttl=%u",
+ "Dropping mtrace multicast packet len=%u to %s ttl=%u",
ntohs(ip_hdr->ip_len),
inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
}
@@ -532,8 +523,7 @@ static int mtrace_send_mc_response(struct pim_instance *pim,
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
zlog_debug(
- "Dropping mtrace multicast response packet "
- "len=%u to %s",
+ "Dropping mtrace multicast response packet len=%u to %s",
(unsigned int)mtrace_len,
inet_ntoa(mtracep->rsp_addr));
}
@@ -595,8 +585,7 @@ static int mtrace_send_response(struct pim_instance *pim,
if (!pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1)) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Dropped response qid=%ud, no route to "
- "response address",
+ "Dropped response qid=%ud, no route to response address",
mtracep->qry_id);
return -1;
}
@@ -645,8 +634,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace packet from %s on %s: too short,"
- " len=%d, min=%zu",
+ "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
from_str, ifp->name, igmp_msg_len,
sizeof(struct igmp_mtrace));
return -1;
@@ -663,8 +651,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace packet from %s on %s: checksum"
- " mismatch: received=%x computed=%x",
+ "Recv mtrace packet from %s on %s: checksum mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
@@ -688,8 +675,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
- "Dropping multicast query "
- "on wrong interface");
+ "Dropping multicast query on wrong interface");
return -1;
}
/* Unicast query on wrong interface */
@@ -700,8 +686,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
if (PIM_DEBUG_MTRACE)
zlog_debug(
- "Dropping multicast query with "
- "duplicate source and id");
+ "Dropping multicast query with duplicate source and id");
return -1;
}
qry_id = mtracep->qry_id;
@@ -721,8 +706,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
} else {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace packet from %s on %s: "
- "invalid length %d",
+ "Recv mtrace packet from %s on %s: invalid length %d",
from_str, ifp->name, igmp_msg_len);
return -1;
}
@@ -732,8 +716,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
&& !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace packet from %s on %s:"
- " not link-local multicast %s",
+ "Recv mtrace packet from %s on %s: not link-local multicast %s",
from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
return -1;
}
@@ -866,8 +849,7 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace packet from %s on %s: too short,"
- " len=%d, min=%zu",
+ "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu",
from_str, ifp->name, igmp_msg_len,
sizeof(struct igmp_mtrace));
return -1;
@@ -884,8 +866,7 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
if (recv_checksum != checksum) {
if (PIM_DEBUG_MTRACE)
zlog_warn(
- "Recv mtrace response from %s on %s: checksum"
- " mismatch: received=%x computed=%x",
+ "Recv mtrace response from %s on %s: checksum mismatch: received=%x computed=%x",
from_str, ifp->name, recv_checksum, checksum);
return -1;
}
diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c
index 305dd5cf2c..2dfc0af1de 100644
--- a/pimd/pim_mlag.c
+++ b/pimd/pim_mlag.c
@@ -1082,6 +1082,14 @@ void pim_instance_mlag_terminate(struct pim_instance *pim)
pim->inst_mlag_intf_cnt = 0;
}
+void pim_mlag_terminate(void)
+{
+ stream_free(router->mlag_stream);
+ router->mlag_stream = NULL;
+ stream_fifo_free(router->mlag_fifo);
+ router->mlag_fifo = NULL;
+}
+
void pim_mlag_init(void)
{
pim_mlag_param_reset();
diff --git a/pimd/pim_mlag.h b/pimd/pim_mlag.h
index eb316695f7..b044c31c44 100644
--- a/pimd/pim_mlag.h
+++ b/pimd/pim_mlag.h
@@ -28,6 +28,7 @@
#include "pim_iface.h"
extern void pim_mlag_init(void);
+extern void pim_mlag_terminate(void);
extern void pim_instance_mlag_init(struct pim_instance *pim);
extern void pim_instance_mlag_terminate(struct pim_instance *pim);
extern void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp);
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 1611eac95d..93fe787a93 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -141,6 +141,9 @@ void pim_rp_free(struct pim_instance *pim)
{
if (pim->rp_list)
list_delete(&pim->rp_list);
+ if (pim->rp_table)
+ route_table_finish(pim->rp_table);
+ pim->rp_table = NULL;
}
/*
diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c
index 010ec7d745..043ccdb848 100644
--- a/pimd/pim_rpf.c
+++ b/pimd/pim_rpf.c
@@ -78,8 +78,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop,
pim_addr_dump("<nexthop?>", &nexthop->mrib_nexthop_addr,
nexthop_str, sizeof(nexthop_str));
zlog_debug(
- "%s: Using last lookup for %s at %lld, %" PRId64
- " addr %s",
+ "%s: Using last lookup for %s at %lld, %" PRId64" addr %s",
__func__, addr_str, nexthop->last_lookup_time,
pim->last_route_change_time, nexthop_str);
}
@@ -347,6 +346,7 @@ void pim_upstream_rpf_clear(struct pim_instance *pim,
struct pim_upstream *up)
{
if (up->rpf.source_nexthop.interface) {
+ pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED);
up->rpf.source_nexthop.interface = NULL;
up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
PIM_NET_INADDR_ANY;
diff --git a/pimd/pimd.c b/pimd/pimd.c
index 5ccbac32f2..6c354a3cc8 100644
--- a/pimd/pimd.c
+++ b/pimd/pimd.c
@@ -42,6 +42,7 @@
#include "pim_vxlan.h"
#include "pim_zlookup.h"
#include "pim_zebra.h"
+#include "pim_mlag.h"
const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS;
const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS;
@@ -109,6 +110,7 @@ void pim_router_init(void)
void pim_router_terminate(void)
{
+ pim_mlag_terminate();
XFREE(MTYPE_ROUTER, router);
}
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 121abea598..8540651544 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -127,6 +127,12 @@ clippy_scan += \
pimd/pim_cmd.c \
# end
+nodist_pimd_pimd_SOURCES = \
+ yang/frr-igmp.yang.c \
+ yang/frr-pim.yang.c \
+ yang/frr-pim-rp.yang.c \
+ # end
+
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la $(LIBCAP)
pimd_pimd_SOURCES = pimd/pim_main.c
diff --git a/python/makefile.py b/python/makefile.py
index 948d3f7391..fe20945ccc 100644
--- a/python/makefile.py
+++ b/python/makefile.py
@@ -73,11 +73,18 @@ while lines:
out_lines.append(line)
continue
- m = make_rule_re.match(line)
+ full_line = line
+ full_lines = lines[:]
+ while full_line.endswith('\\'):
+ full_line = full_line[:-1] + full_lines.pop(0)
+
+ m = make_rule_re.match(full_line)
if m is None:
out_lines.append(line)
continue
+ line, lines = full_line, full_lines
+
target, dep = m.group(1), m.group(2)
if target.endswith('.lo') or target.endswith('.o'):
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index 7e2394f473..5e64b7afdb 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -37,7 +37,7 @@
/*
* XPath: /frr-ripd:ripd/instance
*/
-DEFPY_NOSH (router_rip,
+DEFPY_YANG_NOSH (router_rip,
router_rip_cmd,
"router rip [vrf NAME]",
"Enable a routing process\n"
@@ -62,7 +62,7 @@ DEFPY_NOSH (router_rip,
return ret;
}
-DEFPY (no_router_rip,
+DEFPY_YANG (no_router_rip,
no_router_rip_cmd,
"no router rip [vrf NAME]",
NO_STR
@@ -100,7 +100,7 @@ void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/allow-ecmp
*/
-DEFPY (rip_allow_ecmp,
+DEFPY_YANG (rip_allow_ecmp,
rip_allow_ecmp_cmd,
"[no] allow-ecmp",
NO_STR
@@ -124,7 +124,7 @@ void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/default-information-originate
*/
-DEFPY (rip_default_information_originate,
+DEFPY_YANG (rip_default_information_originate,
rip_default_information_originate_cmd,
"[no] default-information originate",
NO_STR
@@ -150,7 +150,7 @@ void cli_show_rip_default_information_originate(struct vty *vty,
/*
* XPath: /frr-ripd:ripd/instance/default-metric
*/
-DEFPY (rip_default_metric,
+DEFPY_YANG (rip_default_metric,
rip_default_metric_cmd,
"default-metric (1-16)",
"Set a metric of redistribute routes\n"
@@ -162,7 +162,7 @@ DEFPY (rip_default_metric,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_rip_default_metric,
+DEFPY_YANG (no_rip_default_metric,
no_rip_default_metric_cmd,
"no default-metric [(1-16)]",
NO_STR
@@ -184,7 +184,7 @@ void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/distance/default
*/
-DEFPY (rip_distance,
+DEFPY_YANG (rip_distance,
rip_distance_cmd,
"distance (1-255)",
"Administrative distance\n"
@@ -196,7 +196,7 @@ DEFPY (rip_distance,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_rip_distance,
+DEFPY_YANG (no_rip_distance,
no_rip_distance_cmd,
"no distance [(1-255)]",
NO_STR
@@ -221,7 +221,7 @@ void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/distance/source
*/
-DEFPY (rip_distance_source,
+DEFPY_YANG (rip_distance_source,
rip_distance_source_cmd,
"[no] distance (1-255) A.B.C.D/M$prefix [WORD$acl]",
NO_STR
@@ -258,7 +258,7 @@ void cli_show_rip_distance_source(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/explicit-neighbor
*/
-DEFPY (rip_neighbor,
+DEFPY_YANG (rip_neighbor,
rip_neighbor_cmd,
"[no] neighbor A.B.C.D",
NO_STR
@@ -280,7 +280,7 @@ void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/network
*/
-DEFPY (rip_network_prefix,
+DEFPY_YANG (rip_network_prefix,
rip_network_prefix_cmd,
"[no] network A.B.C.D/M",
NO_STR
@@ -302,7 +302,7 @@ void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/interface
*/
-DEFPY (rip_network_if,
+DEFPY_YANG (rip_network_if,
rip_network_if_cmd,
"[no] network WORD",
NO_STR
@@ -324,7 +324,7 @@ void cli_show_rip_network_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/offset-list
*/
-DEFPY (rip_offset_list,
+DEFPY_YANG (rip_offset_list,
rip_offset_list_cmd,
"[no] offset-list WORD$acl <in|out>$direction (0-16)$metric [IFNAME]",
NO_STR
@@ -367,7 +367,7 @@ void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/passive-default
*/
-DEFPY (rip_passive_default,
+DEFPY_YANG (rip_passive_default,
rip_passive_default_cmd,
"[no] passive-interface default",
NO_STR
@@ -393,7 +393,7 @@ void cli_show_rip_passive_default(struct vty *vty, struct lyd_node *dnode,
* XPath: /frr-ripd:ripd/instance/passive-interface
* /frr-ripd:ripd/instance/non-passive-interface
*/
-DEFPY (rip_passive_interface,
+DEFPY_YANG (rip_passive_interface,
rip_passive_interface_cmd,
"[no] passive-interface IFNAME",
NO_STR
@@ -434,7 +434,7 @@ void cli_show_rip_non_passive_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/redistribute
*/
-DEFPY (rip_redistribute,
+DEFPY_YANG (rip_redistribute,
rip_redistribute_cmd,
"[no] redistribute " FRR_REDIST_STR_RIPD "$protocol [{metric (0-16)|route-map WORD}]",
NO_STR
@@ -477,7 +477,7 @@ void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/static-route
*/
-DEFPY (rip_route,
+DEFPY_YANG (rip_route,
rip_route_cmd,
"[no] route A.B.C.D/M",
NO_STR
@@ -499,7 +499,7 @@ void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/timers
*/
-DEFPY (rip_timers,
+DEFPY_YANG (rip_timers,
rip_timers_cmd,
"timers basic (5-2147483647)$update (5-2147483647)$timeout (5-2147483647)$garbage",
"Adjust routing timers\n"
@@ -518,7 +518,7 @@ DEFPY (rip_timers,
return nb_cli_apply_changes(vty, "./timers");
}
-DEFPY (no_rip_timers,
+DEFPY_YANG (no_rip_timers,
no_rip_timers_cmd,
"no timers basic [(5-2147483647) (5-2147483647) (5-2147483647)]",
NO_STR
@@ -547,7 +547,7 @@ void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/version
*/
-DEFPY (rip_version,
+DEFPY_YANG (rip_version,
rip_version_cmd,
"version (1-2)",
"Set routing protocol version\n"
@@ -560,7 +560,7 @@ DEFPY (rip_version,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_rip_version,
+DEFPY_YANG (no_rip_version,
no_rip_version_cmd,
"no version [(1-2)]",
NO_STR
@@ -596,7 +596,7 @@ void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
*/
-DEFPY (ip_rip_split_horizon,
+DEFPY_YANG (ip_rip_split_horizon,
ip_rip_split_horizon_cmd,
"[no] ip rip split-horizon [poisoned-reverse$poisoned_reverse]",
NO_STR
@@ -641,7 +641,7 @@ void cli_show_ip_rip_split_horizon(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast
*/
-DEFPY (ip_rip_v2_broadcast,
+DEFPY_YANG (ip_rip_v2_broadcast,
ip_rip_v2_broadcast_cmd,
"[no] ip rip v2-broadcast",
NO_STR
@@ -667,7 +667,7 @@ void cli_show_ip_rip_v2_broadcast(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive
*/
-DEFPY (ip_rip_receive_version,
+DEFPY_YANG (ip_rip_receive_version,
ip_rip_receive_version_cmd,
"ip rip receive version <{1$v1|2$v2}|none>",
IP_STR
@@ -694,7 +694,7 @@ DEFPY (ip_rip_receive_version,
return nb_cli_apply_changes(vty, "./frr-ripd:rip");
}
-DEFPY (no_ip_rip_receive_version,
+DEFPY_YANG (no_ip_rip_receive_version,
no_ip_rip_receive_version_cmd,
"no ip rip receive version [<{1|2}|none>]",
NO_STR
@@ -736,7 +736,7 @@ void cli_show_ip_rip_receive_version(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send
*/
-DEFPY (ip_rip_send_version,
+DEFPY_YANG (ip_rip_send_version,
ip_rip_send_version_cmd,
"ip rip send version <{1$v1|2$v2}|none>",
IP_STR
@@ -763,7 +763,7 @@ DEFPY (ip_rip_send_version,
return nb_cli_apply_changes(vty, "./frr-ripd:rip");
}
-DEFPY (no_ip_rip_send_version,
+DEFPY_YANG (no_ip_rip_send_version,
no_ip_rip_send_version_cmd,
"no ip rip send version [<{1|2}|none>]",
NO_STR
@@ -805,7 +805,7 @@ void cli_show_ip_rip_send_version(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme
*/
-DEFPY (ip_rip_authentication_mode,
+DEFPY_YANG (ip_rip_authentication_mode,
ip_rip_authentication_mode_cmd,
"ip rip authentication mode <md5$mode [auth-length <rfc|old-ripd>$auth_length]|text$mode>",
IP_STR
@@ -837,7 +837,7 @@ DEFPY (ip_rip_authentication_mode,
return nb_cli_apply_changes(vty, "./frr-ripd:rip");
}
-DEFPY (no_ip_rip_authentication_mode,
+DEFPY_YANG (no_ip_rip_authentication_mode,
no_ip_rip_authentication_mode_cmd,
"no ip rip authentication mode [<md5 [auth-length <rfc|old-ripd>]|text>]",
NO_STR
@@ -888,7 +888,7 @@ void cli_show_ip_rip_authentication_scheme(struct vty *vty,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password
*/
-DEFPY (ip_rip_authentication_string,
+DEFPY_YANG (ip_rip_authentication_string,
ip_rip_authentication_string_cmd,
"ip rip authentication string LINE$password",
IP_STR
@@ -916,7 +916,7 @@ DEFPY (ip_rip_authentication_string,
return nb_cli_apply_changes(vty, "./frr-ripd:rip");
}
-DEFPY (no_ip_rip_authentication_string,
+DEFPY_YANG (no_ip_rip_authentication_string,
no_ip_rip_authentication_string_cmd,
"no ip rip authentication string [LINE]",
NO_STR
@@ -943,7 +943,7 @@ void cli_show_ip_rip_authentication_string(struct vty *vty,
/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
*/
-DEFPY (ip_rip_authentication_key_chain,
+DEFPY_YANG (ip_rip_authentication_key_chain,
ip_rip_authentication_key_chain_cmd,
"ip rip authentication key-chain LINE$keychain",
IP_STR
@@ -965,7 +965,7 @@ DEFPY (ip_rip_authentication_key_chain,
return nb_cli_apply_changes(vty, "./frr-ripd:rip");
}
-DEFPY (no_ip_rip_authentication_key_chain,
+DEFPY_YANG (no_ip_rip_authentication_key_chain,
no_ip_rip_authentication_key_chain_cmd,
"no ip rip authentication key-chain [LINE]",
NO_STR
@@ -992,7 +992,7 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
/*
* XPath: /frr-ripd:clear-rip-route
*/
-DEFPY (clear_ip_rip,
+DEFPY_YANG (clear_ip_rip,
clear_ip_rip_cmd,
"clear ip rip [vrf WORD]",
CLEAR_STR
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 177f53db45..6ea657401f 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -118,8 +118,7 @@ void rip_interface_multicast_set(int sock, struct connected *connected)
if (setsockopt_ipv4_multicast_if(sock, addr, connected->ifp->ifindex)
< 0) {
zlog_warn(
- "Can't setsockopt IP_MULTICAST_IF on fd %d to "
- "ifindex %d for interface %s",
+ "Can't setsockopt IP_MULTICAST_IF on fd %d to ifindex %d for interface %s",
sock, connected->ifp->ifindex, connected->ifp->name);
}
diff --git a/ripd/ripd.c b/ripd/ripd.c
index fc53796bd2..ecadf8fb71 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -742,8 +742,7 @@ static void rip_packet_dump(struct rip_packet *packet, int size,
ntohs(md5->family),
ntohs(md5->type));
zlog_debug(
- " RIP-2 packet len %d Key ID %d"
- " Auth Data len %d",
+ " 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",
@@ -757,8 +756,7 @@ static void rip_packet_dump(struct rip_packet *packet, int size,
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",
+ " 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],
@@ -904,8 +902,7 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from,
|| (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE))) {
if (IS_RIP_DEBUG_EVENT)
zlog_debug(
- "RIPv2 MD5 authentication, strange authentication "
- "length field %d",
+ "RIPv2 MD5 authentication, strange authentication length field %d",
md5->auth_len);
return 0;
}
@@ -916,8 +913,7 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from,
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!",
+ "RIPv2 MD5 authentication, packet length field %d greater than received length %d!",
md5->packet_len, length);
return 0;
}
@@ -1645,8 +1641,7 @@ void rip_redistribute_delete(struct rip *rip, int type, int sub_type,
if (IS_RIP_DEBUG_EVENT)
zlog_debug(
- "Poison %s/%d on the interface %s with an "
- "infinity metric [delete]",
+ "Poison %s/%d on the interface %s with an infinity metric [delete]",
inet_ntoa(p->prefix),
p->prefixlen,
ifindex2ifname(
@@ -1814,8 +1809,7 @@ static int rip_read(struct thread *t)
if (ifc == NULL) {
zlog_info(
- "rip_read: cannot find connected address for packet from %s "
- "port %d on interface %s (VRF %s)",
+ "rip_read: cannot find connected address for packet from %s port %d on interface %s (VRF %s)",
inet_ntoa(from.sin_addr), ntohs(from.sin_port),
ifp->name, rip->vrf_name);
return -1;
@@ -1937,8 +1931,7 @@ static int rip_read(struct thread *t)
if (packet->command != RIP_REQUEST) {
if (IS_RIP_DEBUG_PACKET)
zlog_debug(
- "RIPv1"
- " dropped because authentication enabled");
+ "RIPv1 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
rip_peer_bad_packet(rip, &from);
return -1;
@@ -1961,8 +1954,7 @@ static int rip_read(struct thread *t)
if (packet->rte->family != htons(RIP_FAMILY_AUTH)) {
if (IS_RIP_DEBUG_PACKET)
zlog_debug(
- "RIPv2"
- " dropped because authentication enabled");
+ "RIPv2 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
rip_peer_bad_packet(rip, &from);
return -1;
diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c
index b3d92fb0d9..f66de175fa 100644
--- a/ripngd/ripng_cli.c
+++ b/ripngd/ripng_cli.c
@@ -37,7 +37,7 @@
/*
* XPath: /frr-ripngd:ripngd/instance
*/
-DEFPY_NOSH (router_ripng,
+DEFPY_YANG_NOSH (router_ripng,
router_ripng_cmd,
"router ripng [vrf NAME]",
"Enable a routing process\n"
@@ -62,7 +62,7 @@ DEFPY_NOSH (router_ripng,
return ret;
}
-DEFPY (no_router_ripng,
+DEFPY_YANG (no_router_ripng,
no_router_ripng_cmd,
"no router ripng [vrf NAME]",
NO_STR
@@ -100,7 +100,7 @@ void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/allow-ecmp
*/
-DEFPY (ripng_allow_ecmp,
+DEFPY_YANG (ripng_allow_ecmp,
ripng_allow_ecmp_cmd,
"[no] allow-ecmp",
NO_STR
@@ -124,7 +124,7 @@ void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/default-information-originate
*/
-DEFPY (ripng_default_information_originate,
+DEFPY_YANG (ripng_default_information_originate,
ripng_default_information_originate_cmd,
"[no] default-information originate",
NO_STR
@@ -150,7 +150,7 @@ void cli_show_ripng_default_information_originate(struct vty *vty,
/*
* XPath: /frr-ripngd:ripngd/instance/default-metric
*/
-DEFPY (ripng_default_metric,
+DEFPY_YANG (ripng_default_metric,
ripng_default_metric_cmd,
"default-metric (1-16)",
"Set a metric of redistribute routes\n"
@@ -162,7 +162,7 @@ DEFPY (ripng_default_metric,
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY (no_ripng_default_metric,
+DEFPY_YANG (no_ripng_default_metric,
no_ripng_default_metric_cmd,
"no default-metric [(1-16)]",
NO_STR
@@ -184,7 +184,7 @@ void cli_show_ripng_default_metric(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/network
*/
-DEFPY (ripng_network_prefix,
+DEFPY_YANG (ripng_network_prefix,
ripng_network_prefix_cmd,
"[no] network X:X::X:X/M",
NO_STR
@@ -206,7 +206,7 @@ void cli_show_ripng_network_prefix(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/interface
*/
-DEFPY (ripng_network_if,
+DEFPY_YANG (ripng_network_if,
ripng_network_if_cmd,
"[no] network WORD",
NO_STR
@@ -228,7 +228,7 @@ void cli_show_ripng_network_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/offset-list
*/
-DEFPY (ripng_offset_list,
+DEFPY_YANG (ripng_offset_list,
ripng_offset_list_cmd,
"[no] offset-list WORD$acl <in|out>$direction (0-16)$metric [IFNAME]",
NO_STR
@@ -271,7 +271,7 @@ void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/passive-interface
*/
-DEFPY (ripng_passive_interface,
+DEFPY_YANG (ripng_passive_interface,
ripng_passive_interface_cmd,
"[no] passive-interface IFNAME",
NO_STR
@@ -294,7 +294,7 @@ void cli_show_ripng_passive_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/redistribute
*/
-DEFPY (ripng_redistribute,
+DEFPY_YANG (ripng_redistribute,
ripng_redistribute_cmd,
"[no] redistribute " FRR_REDIST_STR_RIPNGD "$protocol [{metric (0-16)|route-map WORD}]",
NO_STR
@@ -337,7 +337,7 @@ void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/static-route
*/
-DEFPY (ripng_route,
+DEFPY_YANG (ripng_route,
ripng_route_cmd,
"[no] route X:X::X:X/M",
NO_STR
@@ -359,7 +359,7 @@ void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/aggregate-addres
*/
-DEFPY (ripng_aggregate_address,
+DEFPY_YANG (ripng_aggregate_address,
ripng_aggregate_address_cmd,
"[no] aggregate-address X:X::X:X/M",
NO_STR
@@ -383,7 +383,7 @@ void cli_show_ripng_aggregate_address(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:ripngd/instance/timers
*/
-DEFPY (ripng_timers,
+DEFPY_YANG (ripng_timers,
ripng_timers_cmd,
"timers basic (1-65535)$update (1-65535)$timeout (1-65535)$garbage",
"RIPng timers setup\n"
@@ -402,7 +402,7 @@ DEFPY (ripng_timers,
return nb_cli_apply_changes(vty, "./timers");
}
-DEFPY (no_ripng_timers,
+DEFPY_YANG (no_ripng_timers,
no_ripng_timers_cmd,
"no timers basic [(1-65535) (1-65535) (1-65535)]",
NO_STR
@@ -431,7 +431,7 @@ void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon
*/
-DEFPY (ipv6_ripng_split_horizon,
+DEFPY_YANG (ipv6_ripng_split_horizon,
ipv6_ripng_split_horizon_cmd,
"[no] ipv6 ripng split-horizon [poisoned-reverse$poisoned_reverse]",
NO_STR
@@ -476,7 +476,7 @@ void cli_show_ipv6_ripng_split_horizon(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-ripngd:clear-ripng-route
*/
-DEFPY (clear_ipv6_rip,
+DEFPY_YANG (clear_ipv6_rip,
clear_ipv6_rip_cmd,
"clear ipv6 ripng [vrf WORD]",
CLEAR_STR
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index 625adcaa3c..bf6c6ff27b 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -1047,8 +1047,7 @@ void ripng_redistribute_delete(struct ripng *ripng, int type, int sub_type,
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug(
- "Poisone %s/%d on the interface %s with an "
- "infinity metric [delete]",
+ "Poisone %s/%d on the interface %s with an infinity metric [delete]",
inet6_ntoa(p->prefix),
p->prefixlen,
ifindex2ifname(
diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c
index 40b41fd728..ccf34b10dd 100644
--- a/sharpd/sharp_main.c
+++ b/sharpd/sharp_main.c
@@ -167,9 +167,6 @@ int main(int argc, char **argv, char **envp)
nexthop_group_init(NULL, NULL, NULL, NULL);
vrf_init(NULL, NULL, NULL, NULL, NULL);
- access_list_init();
- route_map_init();
-
sharp_zebra_init();
/* Get configuration file. */
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 48220d1c9b..d390ea8192 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -131,8 +131,8 @@ DEFPY(sharp_nht_data_dump,
sharp_nht_data_dump_cmd,
"sharp data nexthop",
"Sharp routing Protocol\n"
- "Nexthop information\n"
- "Data Dump\n")
+ "Data about what is going on\n"
+ "Nexthop information\n")
{
sharp_nh_tracker_dump(vty);
@@ -278,7 +278,8 @@ DEFPY (install_routes,
if (backup) {
/* Set flag and index in primary nexthop */
SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP);
- sg.r.nhop.backup_idx = 0;
+ sg.r.nhop.backup_num = 1;
+ sg.r.nhop.backup_idx[0] = 0;
if (backup_nexthop4.s_addr != INADDR_ANY) {
sg.r.backup_nhop.gate.ipv4 = backup_nexthop4;
@@ -393,27 +394,31 @@ DEFUN_NOSH (show_debugging_sharpd,
return CMD_SUCCESS;
}
-DEFPY(sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd,
- "sharp lsp (0-100000)$inlabel\
+DEFPY (sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd,
+ "sharp lsp [update]$update (0-100000)$inlabel\
nexthop-group NHGNAME$nhgname\
[prefix A.B.C.D/M$pfx\
" FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]",
- "Sharp Routing Protocol\n"
- "Add an LSP\n"
- "The ingress label to use\n"
- "Use nexthops from a nexthop-group\n"
- "The nexthop-group name\n"
- "Label a prefix\n"
- "The v4 prefix to label\n"
- FRR_IP_REDIST_HELP_STR_ZEBRA
- "Instance to use\n"
- "Instance\n")
+ "Sharp Routing Protocol\n"
+ "Add an LSP\n"
+ "Update an LSP\n"
+ "The ingress label to use\n"
+ "Use nexthops from a nexthop-group\n"
+ "The nexthop-group name\n"
+ "Label a prefix\n"
+ "The v4 prefix to label\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Instance to use\n"
+ "Instance\n")
{
struct nexthop_group_cmd *nhgc = NULL;
struct nexthop_group_cmd *backup_nhgc = NULL;
struct nexthop_group *backup_nhg = NULL;
struct prefix p = {};
int type = 0;
+ bool update_p;
+
+ update_p = (update != NULL);
/* We're offered a v4 prefix */
if (pfx->family > 0 && type_str) {
@@ -457,7 +462,8 @@ DEFPY(sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd,
backup_nhg = &(backup_nhgc->nhg);
}
- if (sharp_install_lsps_helper(true, pfx->family > 0 ? &p : NULL,
+ if (sharp_install_lsps_helper(true /*install*/, update_p,
+ pfx->family > 0 ? &p : NULL,
type, instance, inlabel,
&(nhgc->nhg), backup_nhg) == 0)
return CMD_SUCCESS;
@@ -522,7 +528,8 @@ DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd,
nhg = &(nhgc->nhg);
}
- if (sharp_install_lsps_helper(false, pfx->family > 0 ? &p : NULL,
+ if (sharp_install_lsps_helper(false /*!install*/, false,
+ pfx->family > 0 ? &p : NULL,
type, instance, inlabel, nhg, NULL) == 0)
return CMD_SUCCESS;
else {
@@ -642,6 +649,51 @@ DEFPY (send_opaque_reg,
return CMD_SUCCESS;
}
+DEFPY (neigh_discover,
+ neigh_discover_cmd,
+ "sharp neigh discover [vrf NAME$vrf_name] <A.B.C.D$dst4|X:X::X:X$dst6> IFNAME$ifname",
+ SHARP_STR
+ "Discover neighbours\n"
+ "Send an ARP/NDP request\n"
+ VRF_CMD_HELP_STR
+ "v4 Destination address\n"
+ "v6 Destination address\n"
+ "Interface name\n")
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ struct prefix prefix;
+
+ memset(&prefix, 0, sizeof(prefix));
+
+ if (dst4.s_addr != 0) {
+ prefix.family = AF_INET;
+ prefix.prefixlen = 32;
+ prefix.u.prefix4 = dst4;
+ } else {
+ prefix.family = AF_INET6;
+ prefix.prefixlen = 128;
+ prefix.u.prefix6 = dst6;
+ }
+
+ vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ if (!vrf) {
+ vty_out(vty, "The vrf NAME specified: %s does not exist\n",
+ vrf_name ? vrf_name : VRF_DEFAULT_NAME);
+ return CMD_WARNING;
+ }
+
+ ifp = if_lookup_by_name_vrf(ifname, vrf);
+ if (ifp == NULL) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ sharp_zebra_send_arp(ifp, &prefix);
+
+ return CMD_SUCCESS;
+}
+
void sharp_vty_init(void)
{
install_element(ENABLE_NODE, &install_routes_data_dump_cmd);
@@ -659,6 +711,7 @@ void sharp_vty_init(void)
install_element(ENABLE_NODE, &send_opaque_cmd);
install_element(ENABLE_NODE, &send_opaque_unicast_cmd);
install_element(ENABLE_NODE, &send_opaque_reg_cmd);
+ install_element(ENABLE_NODE, &neigh_discover_cmd);
install_element(VIEW_NODE, &show_debugging_sharpd_cmd);
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 7ab2d6ec22..08f5a07b7e 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -114,15 +114,16 @@ static int sharp_ifp_down(struct interface *ifp)
return 0;
}
-int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
- uint8_t type, int instance, uint32_t in_label,
+int sharp_install_lsps_helper(bool install_p, bool update_p,
+ const struct prefix *p, uint8_t type,
+ int instance, uint32_t in_label,
const struct nexthop_group *nhg,
const struct nexthop_group *backup_nhg)
{
struct zapi_labels zl = {};
struct zapi_nexthop *znh;
const struct nexthop *nh;
- int i, ret;
+ int i, cmd, ret;
zl.type = ZEBRA_LSP_SHARP;
zl.local_label = in_label;
@@ -155,6 +156,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
}
@@ -188,6 +191,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
if (i > 0)
@@ -196,12 +201,17 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
zl.backup_nexthop_num = i;
}
- if (install_p)
- ret = zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_ADD,
- &zl);
- else
- ret = zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE,
- &zl);
+
+ if (install_p) {
+ if (update_p)
+ cmd = ZEBRA_MPLS_LABELS_REPLACE;
+ else
+ cmd = ZEBRA_MPLS_LABELS_ADD;
+ } else {
+ cmd = ZEBRA_MPLS_LABELS_DELETE;
+ }
+
+ ret = zebra_send_mpls_labels(zclient, cmd, &zl);
return ret;
}
@@ -653,6 +663,11 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
}
+void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
+{
+ zclient_send_neigh_discovery_req(zclient, ifp, p);
+}
+
void sharp_zebra_init(void)
{
struct zclient_options opt = {.receive_notify = true};
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
index cb2f38a6ab..0a44fa694f 100644
--- a/sharpd/sharp_zebra.h
+++ b/sharpd/sharp_zebra.h
@@ -44,8 +44,9 @@ extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id,
extern void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id,
uint8_t instance, uint32_t routes);
-int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
- uint8_t type, int instance, uint32_t in_label,
+int sharp_install_lsps_helper(bool install_p, bool update_p,
+ const struct prefix *p, uint8_t type,
+ int instance, uint32_t in_label,
const struct nexthop_group *nhg,
const struct nexthop_group *backup_nhg);
@@ -57,4 +58,7 @@ void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
uint32_t session_id, uint32_t type);
+extern void sharp_zebra_send_arp(const struct interface *ifp,
+ const struct prefix *p);
+
#endif
diff --git a/staticd/static_main.c b/staticd/static_main.c
index 3c5922b85a..ac8f8ff029 100644
--- a/staticd/static_main.c
+++ b/staticd/static_main.c
@@ -31,12 +31,14 @@
#include "vrf.h"
#include "nexthop.h"
#include "filter.h"
+#include "routing_nb.h"
#include "static_vrf.h"
#include "static_vty.h"
#include "static_routes.h"
#include "static_zebra.h"
#include "static_debug.h"
+#include "static_nb.h"
char backup_config_file[256];
@@ -63,10 +65,12 @@ struct option longopts[] = { { 0 } };
/* Master of threads. */
struct thread_master *master;
+static struct frr_daemon_info staticd_di;
/* SIGHUP handler. */
static void sighup(void)
{
zlog_info("SIGHUP received");
+ vty_read_config(NULL, staticd_di.config_file, config_default);
}
/* SIGINT / SIGTERM handler. */
@@ -108,7 +112,10 @@ struct quagga_signal_t static_signals[] = {
static const struct frr_yang_module_info *const staticd_yang_modules[] = {
&frr_filter_info,
+ &frr_interface_info,
&frr_vrf_info,
+ &frr_routing_info,
+ &frr_staticd_info,
};
#define STATIC_VTY_PORT 2616
@@ -148,13 +155,15 @@ int main(int argc, char **argv, char **envp)
master = frr_init();
- access_list_init();
static_debug_init();
static_vrf_init();
static_zebra_init();
static_vty_init();
+ hook_register(routing_conf_event,
+ routing_control_plane_protocols_name_validate);
+
snprintf(backup_config_file, sizeof(backup_config_file),
"%s/zebra.conf", frr_sysconfdir);
staticd_di.backup_config_file = backup_config_file;
diff --git a/staticd/static_memory.c b/staticd/static_memory.c
index 77ca4a3439..122cc9fce1 100644
--- a/staticd/static_memory.c
+++ b/staticd/static_memory.c
@@ -25,4 +25,4 @@
DEFINE_MGROUP(STATIC, "staticd")
-DEFINE_MTYPE(STATIC, STATIC_ROUTE, "Static Route");
+DEFINE_MTYPE(STATIC, STATIC_NEXTHOP, "Static Nexthop");
diff --git a/staticd/static_memory.h b/staticd/static_memory.h
index 77a0db3b12..e9cc7ba469 100644
--- a/staticd/static_memory.h
+++ b/staticd/static_memory.h
@@ -23,6 +23,7 @@
DECLARE_MGROUP(STATIC)
-DECLARE_MTYPE(STATIC_ROUTE);
+DECLARE_MTYPE(STATIC_NEXTHOP);
+DECLARE_MTYPE(STATIC_PATH);
#endif
diff --git a/staticd/static_nb.c b/staticd/static_nb.c
new file mode 100644
index 0000000000..51704426f0
--- /dev/null
+++ b/staticd/static_nb.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ *
+ * 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
+ */
+#include "northbound.h"
+#include "libfrr.h"
+#include "static_nb.h"
+
+
+/* clang-format off */
+
+const struct frr_yang_module_info frr_staticd_info = {
+ .name = "frr-staticd",
+ .nodes = {
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/table-id",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_table_id_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop",
+ .cbs = {
+ .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish,
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy,
+ .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy,
+
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/table-id",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_table_id_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop",
+ .cbs = {
+ .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish,
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy,
+ .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/staticd/static_nb.h b/staticd/static_nb.h
new file mode 100644
index 0000000000..d145c31f77
--- /dev/null
+++ b/staticd/static_nb.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ *
+ * 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
+ */
+#ifndef _FRR_STATIC_NB_H_
+#define _FRR_STATIC_NB_H_
+
+extern const struct frr_yang_module_info frr_staticd_info;
+
+/* Mandatory callbacks. */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_table_id_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_table_id_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args);
+
+/* Optional 'apply_finish' callbacks. */
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+
+/* Optional 'pre_validate' callbacks. */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate(
+ struct nb_cb_pre_validate_args *args);
+
+/*
+ * Callback registered with routing_nb lib to validate only
+ * one instance of staticd is allowed
+ */
+int routing_control_plane_protocols_name_validate(
+ struct nb_cb_create_args *args);
+
+/* xpath macros */
+/* route-list */
+#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "path-list[distance='%u']"
+
+
+#define FRR_STATIC_ROUTE_PATH_TAG_XPATH "/tag"
+
+#define FRR_STATIC_ROUTE_PATH_TABLEID_XPATH "/table-id"
+
+/* route-list/frr-nexthops */
+#define FRR_STATIC_ROUTE_NH_KEY_XPATH \
+ "/frr-nexthops/" \
+ "nexthop[nh-type='%s'][vrf='%s'][gateway='%s'][interface='%s']"
+
+#define FRR_STATIC_ROUTE_NH_ONLINK_XPATH "/onlink"
+
+#define FRR_STATIC_ROUTE_NH_COLOR_XPATH "/srte-color"
+
+#define FRR_STATIC_ROUTE_NH_BH_XPATH "/bh-type"
+
+#define FRR_STATIC_ROUTE_NH_LABEL_XPATH "/mpls-label-stack"
+
+#define FRR_STATIC_ROUTE_NHLB_KEY_XPATH "/entry[id='%u']/label"
+
+/* route-list/srclist */
+#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \
+ "/frr-routing:routing/control-plane-protocols/" \
+ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \
+ "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \
+ "src-list[src-prefix='%s']/path-list[distance='%u']"
+
+/* route-list/frr-nexthops */
+#define FRR_DEL_S_ROUTE_NH_KEY_XPATH \
+ FRR_STATIC_ROUTE_INFO_KEY_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+/* route-list/src/src-list/frr-nexthops*/
+#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH \
+ FRR_S_ROUTE_SRC_INFO_KEY_XPATH \
+ FRR_STATIC_ROUTE_NH_KEY_XPATH
+
+#endif
diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c
new file mode 100644
index 0000000000..e89832069c
--- /dev/null
+++ b/staticd/static_nb_config.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright (C) 2018 Vmware
+ * Vishal Dhingra
+ *
+ * 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
+ */
+#include "northbound.h"
+#include "libfrr.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "prefix.h"
+#include "table.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "srcdest_table.h"
+
+#include "static_vrf.h"
+#include "static_routes.h"
+#include "static_nb.h"
+
+
+static int static_path_list_create(struct nb_cb_create_args *args)
+{
+ struct route_node *rn;
+ struct static_path *pn;
+ uint8_t distance;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_get_entry(args->dnode, NULL, true);
+ distance = yang_dnode_get_uint8(args->dnode, "./distance");
+ pn = static_add_path(rn, distance);
+ nb_running_set_entry(args->dnode, pn);
+ }
+
+ return NB_OK;
+}
+
+static void static_path_list_destroy(struct nb_cb_destroy_args *args,
+ const struct lyd_node *rn_dnode,
+ struct stable_info *info)
+{
+ struct route_node *rn;
+ struct static_path *pn;
+
+ pn = nb_running_unset_entry(args->dnode);
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ static_del_path(rn, pn, info->safi, info->svrf);
+}
+
+static void static_path_list_tag_modify(struct nb_cb_modify_args *args,
+ const struct lyd_node *rn_dnode,
+ struct stable_info *info)
+{
+ struct static_path *pn;
+ struct route_node *rn;
+ route_tag_t tag;
+
+ tag = yang_dnode_get_uint32(args->dnode, NULL);
+ pn = nb_running_get_entry(args->dnode, NULL, true);
+ pn->tag = tag;
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+
+ static_install_path(rn, pn, info->safi, info->svrf);
+}
+
+static int static_path_list_tableid_modify(struct nb_cb_modify_args *args,
+ const struct lyd_node *rn_dnode,
+ struct stable_info *info)
+{
+ struct static_path *pn;
+ struct route_node *rn;
+ uint32_t table_id;
+ const struct lyd_node *vrf_dnode;
+ const char *vrf;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ vrf_dnode = yang_dnode_get_parent(args->dnode,
+ "control-plane-protocol");
+ vrf = yang_dnode_get_string(vrf_dnode, "./vrf");
+ table_id = yang_dnode_get_uint32(args->dnode, NULL);
+ if (table_id && (strcmp(vrf, vrf_get_default_name()) != 0)
+ && !vrf_is_backend_netns()) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "%% table param only available when running on netns-based vrfs");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ table_id = yang_dnode_get_uint32(args->dnode, NULL);
+ pn = nb_running_get_entry(args->dnode, NULL, true);
+ pn->table_id = table_id;
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ static_install_path(rn, pn, info->safi, info->svrf);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static bool static_nexthop_create(struct nb_cb_create_args *args,
+ const struct lyd_node *rn_dnode,
+ struct stable_info *info)
+{
+ struct route_node *rn;
+ struct static_path *pn;
+ struct ipaddr ipaddr;
+ struct static_nexthop *nh;
+ int nh_type;
+ const char *ifname;
+ const char *nh_vrf;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ if (ifname != NULL) {
+ if (strcasecmp(ifname, "Null0") == 0
+ || strcasecmp(ifname, "reject") == 0
+ || strcasecmp(ifname, "blackhole") == 0) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "%s: Nexthop interface name can not be from reserved keywords(Null0, reject, blackhole)",
+ ifname);
+ return NB_ERR_VALIDATION;
+ }
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ yang_dnode_get_ip(&ipaddr, args->dnode, "./gateway");
+ nh_type = yang_dnode_get_enum(args->dnode, "./nh-type");
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ nh_vrf = yang_dnode_get_string(args->dnode, "./vrf");
+ pn = nb_running_get_entry(args->dnode, NULL, true);
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+
+ if (!static_add_nexthop_validate(info->svrf, nh_type, &ipaddr))
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "Warning!! Local connected address is configured as Gateway IP((%s))",
+ yang_dnode_get_string(args->dnode,
+ "./gateway"));
+ nh = static_add_nexthop(rn, pn, info->safi, info->svrf, nh_type,
+ &ipaddr, ifname, nh_vrf, 0);
+ if (!nh) {
+ char buf[SRCDEST2STR_BUFFER];
+
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_APPLY,
+ "%s : nh [%d:%s:%s:%s] nexthop creation failed",
+ srcdest_rnode2str(rn, buf, sizeof(buf)),
+ nh_type, ifname,
+ yang_dnode_get_string(args->dnode, "./gateway"),
+ nh_vrf);
+ return NB_ERR;
+ }
+ nb_running_set_entry(args->dnode, nh);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static bool static_nexthop_destroy(struct nb_cb_destroy_args *args,
+ const struct lyd_node *rn_dnode,
+ struct stable_info *info)
+{
+ struct route_node *rn;
+ struct static_path *pn;
+ const struct lyd_node *pn_dnode;
+ struct static_nexthop *nh;
+ int ret;
+
+ nh = nb_running_unset_entry(args->dnode);
+ pn_dnode = yang_dnode_get_parent(args->dnode, "path-list");
+ pn = nb_running_get_entry(pn_dnode, NULL, true);
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+
+ ret = static_delete_nexthop(rn, pn, info->safi, info->svrf, nh);
+ if (!ret) {
+ char buf[SRCDEST2STR_BUFFER];
+
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "%s : nh [%d:%s:%s:%s] nexthop destroy failed",
+ srcdest_rnode2str(rn, buf, sizeof(buf)),
+ yang_dnode_get_enum(args->dnode, "./nh-type"),
+ yang_dnode_get_string(args->dnode, "./interface"),
+ yang_dnode_get_string(args->dnode, "./gateway"),
+ yang_dnode_get_string(args->dnode, "./vrf"));
+ return NB_ERR;
+ }
+
+ return NB_OK;
+}
+
+static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+ nh->snh_label.label[index] = 0;
+ nh->snh_label.num_labels++;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+nexthop_mpls_label_stack_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ index = pos - 1;
+ nh->snh_label.label[index] = 0;
+ nh->snh_label.num_labels--;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+ uint32_t pos;
+ uint8_t index;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ pos = yang_get_list_pos(args->dnode->parent);
+ if (!pos) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "libyang returns invalid label position");
+ return NB_ERR;
+ }
+ /* Mapping to array = list-index -1 */
+ index = pos - 1;
+ nh->snh_label.label[index] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->onlink = yang_dnode_get_bool(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+static int static_nexthop_color_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->color = yang_dnode_get_uint32(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_unset_entry(args->dnode);
+ nh->color = 0;
+
+ return NB_OK;
+}
+
+static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *nh;
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->bh_type = yang_dnode_get_enum(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct route_node *rn;
+ const struct lyd_node *pn_dnode;
+ const struct lyd_node *rn_dnode;
+ const char *ifname;
+ const char *nh_vrf;
+ struct stable_info *info;
+ int nh_type;
+
+ nh_type = yang_dnode_get_enum(args->dnode, "./nh-type");
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ nh_vrf = yang_dnode_get_string(args->dnode, "./vrf");
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+
+ pn_dnode = yang_dnode_get_parent(args->dnode, "path-list");
+ pn = nb_running_get_entry(pn_dnode, NULL, true);
+
+ rn_dnode = yang_dnode_get_parent(pn_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ static_install_nexthop(rn, pn, nh, info->safi, info->svrf, ifname,
+ nh_type, nh_vrf);
+}
+
+
+void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct route_node *rn;
+ struct route_node *src_rn;
+ const struct lyd_node *pn_dnode;
+ const struct lyd_node *rn_dnode;
+ const struct lyd_node *src_dnode;
+ const char *ifname;
+ const char *nh_vrf;
+ struct stable_info *info;
+ int nh_type;
+
+ nh_type = yang_dnode_get_enum(args->dnode, "./nh-type");
+ ifname = yang_dnode_get_string(args->dnode, "./interface");
+ nh_vrf = yang_dnode_get_string(args->dnode, "./vrf");
+
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+
+ pn_dnode = yang_dnode_get_parent(args->dnode, "path-list");
+ pn = nb_running_get_entry(pn_dnode, NULL, true);
+
+ src_dnode = yang_dnode_get_parent(pn_dnode, "src-list");
+ src_rn = nb_running_get_entry(src_dnode, NULL, true);
+
+ rn_dnode = yang_dnode_get_parent(src_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ static_install_nexthop(src_rn, pn, nh, info->safi, info->svrf, ifname,
+ nh_type, nh_vrf);
+}
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate(
+ struct nb_cb_pre_validate_args *args)
+{
+ const struct lyd_node *mls_dnode;
+ uint32_t count;
+
+ mls_dnode = yang_dnode_get(args->dnode, "./mpls-label-stack");
+ count = yang_get_list_elements_count(yang_dnode_get_child(mls_dnode));
+
+ if (count > MPLS_MAX_LABELS) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "Too many labels, Enter %d or fewer",
+ MPLS_MAX_LABELS);
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_name_validate(
+ struct nb_cb_create_args *args)
+{
+ const char *name;
+
+ name = yang_dnode_get_string(args->dnode, "./name");
+ if (!strmatch(name, "staticd")) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "static routing supports only one instance with name staticd");
+ return NB_ERR_VALIDATION;
+ }
+ return NB_OK;
+}
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create(
+ struct nb_cb_create_args *args)
+{
+ struct vrf *vrf;
+ struct static_vrf *s_vrf;
+ struct route_node *rn;
+ const struct lyd_node *vrf_dnode;
+ struct prefix prefix;
+ const char *afi_safi;
+ afi_t prefix_afi;
+ afi_t afi;
+ safi_t safi;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+ afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+ prefix_afi = family2afi(prefix.family);
+ if (afi != prefix_afi) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "route node %s creation failed",
+ yang_dnode_get_string(args->dnode, "./prefix"));
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ vrf_dnode = yang_dnode_get_parent(args->dnode,
+ "control-plane-protocol");
+ vrf = nb_running_get_entry(vrf_dnode, NULL, true);
+ s_vrf = vrf->info;
+
+ yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+ afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+
+ rn = static_add_route(afi, safi, &prefix, NULL, s_vrf);
+ if (!rn) {
+ flog_warn(
+ EC_LIB_NB_CB_CONFIG_APPLY,
+ "route node %s creation failed",
+ yang_dnode_get_string(args->dnode, "./prefix"));
+ return NB_ERR;
+ }
+ nb_running_set_entry(args->dnode, rn);
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_unset_entry(args->dnode);
+ info = route_table_get_info(rn->table);
+ static_del_route(rn, info->safi, info->svrf);
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create(
+ struct nb_cb_create_args *args)
+{
+ return static_path_list_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ const struct lyd_node *rn_dnode;
+ struct route_node *rn;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ static_path_list_destroy(args, rn_dnode, info);
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct stable_info *info;
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ static_path_list_tag_modify(args, rn_dnode, info);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/table-id
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_table_id_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (static_path_list_tableid_modify(args, NULL, NULL) != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_path_list_tableid_modify(args, rn_dnode, info)
+ != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ if (static_nexthop_create(args, rn_dnode, NULL) != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_nexthop_create(args, rn_dnode, info) != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_nexthop_destroy(args, rn_dnode, info) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_bh_type_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* blackhole type has a boolean type with default value,
+ * so no need to do any operations in destroy callback
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_onlink_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* onlink has a boolean type with default value,
+ * so no need to do any operations in destroy callback
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_destroy(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_mpls_label_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_mpls_label_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_mpls_label_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_mpls_label_stack_entry_destroy() will take care
+ * to reset the label vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
+ struct nb_cb_create_args *args)
+{
+ struct static_vrf *s_vrf;
+ struct route_node *rn;
+ struct route_node *src_rn;
+ struct prefix_ipv6 src_prefix = {};
+ struct stable_info *info;
+ afi_t afi;
+ safi_t safi = SAFI_UNICAST;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ rn = nb_running_get_entry(args->dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ s_vrf = info->svrf;
+ yang_dnode_get_ipv6p(&src_prefix, args->dnode, "./src-prefix");
+ afi = family2afi(src_prefix.family);
+ src_rn =
+ static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf);
+ if (!src_rn) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_APPLY,
+ "src rn %s creation failed",
+ yang_dnode_get_string(args->dnode,
+ "./src-prefix"));
+ return NB_ERR;
+ }
+ nb_running_set_entry(args->dnode, src_rn);
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *src_rn;
+ struct route_node *rn;
+ struct stable_info *info;
+ const struct lyd_node *rn_dnode;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ src_rn = nb_running_unset_entry(args->dnode);
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ static_del_route(src_rn, info->safi, info->svrf);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create(
+ struct nb_cb_create_args *args)
+{
+ return static_path_list_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ const struct lyd_node *srn_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ srn_dnode = yang_dnode_get_parent(args->dnode, "src-list");
+ rn_dnode = yang_dnode_get_parent(srn_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ static_path_list_destroy(args, srn_dnode, info);
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct stable_info *info;
+ struct route_node *rn;
+ const struct lyd_node *srn_dnode;
+ const struct lyd_node *rn_dnode;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_ABORT:
+ case NB_EV_PREPARE:
+ break;
+ case NB_EV_APPLY:
+ srn_dnode = yang_dnode_get_parent(args->dnode, "src-list");
+ rn_dnode = yang_dnode_get_parent(srn_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+ static_path_list_tag_modify(args, srn_dnode, info);
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/table-id
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_table_id_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ const struct lyd_node *src_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (static_path_list_tableid_modify(args, NULL, NULL) != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ src_dnode = yang_dnode_get_parent(args->dnode, "src-list");
+ rn_dnode = yang_dnode_get_parent(src_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_path_list_tableid_modify(args, src_dnode, info)
+ != NB_OK)
+ return NB_ERR_VALIDATION;
+
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create(
+ struct nb_cb_create_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ const struct lyd_node *src_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ rn_dnode = yang_dnode_get_parent(args->dnode, "route-list");
+ if (static_nexthop_create(args, rn_dnode, NULL) != NB_OK)
+ return NB_ERR_VALIDATION;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ src_dnode = yang_dnode_get_parent(args->dnode, "src-list");
+ rn_dnode = yang_dnode_get_parent(src_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_nexthop_create(args, src_dnode, info) != NB_OK)
+ return NB_ERR_VALIDATION;
+
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct route_node *rn;
+ const struct lyd_node *rn_dnode;
+ const struct lyd_node *src_dnode;
+ struct stable_info *info;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ src_dnode = yang_dnode_get_parent(args->dnode, "src-list");
+ rn_dnode = yang_dnode_get_parent(src_dnode, "route-list");
+ rn = nb_running_get_entry(rn_dnode, NULL, true);
+ info = route_table_get_info(rn->table);
+
+ if (static_nexthop_destroy(args, rn_dnode, info) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_bh_type_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* blackhole type has a boolean type with default value,
+ * so no need to do any operations in destroy callback
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_onlink_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* onlink has a boolean type with default value,
+ * so no need to do any operations in destroy callback
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_modify(args) != NB_OK)
+ return NB_ERR;
+
+ break;
+ }
+ return NB_OK;
+}
+
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_color_destroy(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create(
+ struct nb_cb_create_args *args)
+{
+ return nexthop_mpls_label_stack_entry_create(args);
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return nexthop_mpls_label_stack_entry_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ if (static_nexthop_mpls_label_modify(args) != NB_OK)
+ return NB_ERR;
+ break;
+ }
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /*
+ * No operation is required in this call back.
+ * nexthop_mpls_label_stack_entry_destroy() will take care
+ * to reset the label vaue.
+ */
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class
+ */
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
diff --git a/staticd/static_nht.c b/staticd/static_nht.c
index 1a2ddd7f05..feb6e0f993 100644
--- a/staticd/static_nht.c
+++ b/staticd/static_nht.c
@@ -30,33 +30,33 @@
#include "static_zebra.h"
#include "static_nht.h"
-static void static_nht_update_rn(struct route_node *rn,
- struct prefix *nhp, uint32_t nh_num,
- vrf_id_t nh_vrf_id, struct vrf *vrf,
- safi_t safi)
+static void static_nht_update_path(struct route_node *rn,
+ struct static_path *pn, struct prefix *nhp,
+ uint32_t nh_num, vrf_id_t nh_vrf_id,
+ struct vrf *vrf, safi_t safi)
{
- struct static_route *si;
+ struct static_nexthop *nh;
- for (si = rn->info; si; si = si->next) {
- if (si->nh_vrf_id != nh_vrf_id)
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != nh_vrf_id)
continue;
- if (si->type != STATIC_IPV4_GATEWAY
- && si->type != STATIC_IPV4_GATEWAY_IFNAME
- && si->type != STATIC_IPV6_GATEWAY
- && si->type != STATIC_IPV6_GATEWAY_IFNAME)
+ if (nh->type != STATIC_IPV4_GATEWAY
+ && nh->type != STATIC_IPV4_GATEWAY_IFNAME
+ && nh->type != STATIC_IPV6_GATEWAY
+ && nh->type != STATIC_IPV6_GATEWAY_IFNAME)
continue;
if (nhp->family == AF_INET
- && nhp->u.prefix4.s_addr == si->addr.ipv4.s_addr)
- si->nh_valid = !!nh_num;
+ && nhp->u.prefix4.s_addr == nh->addr.ipv4.s_addr)
+ nh->nh_valid = !!nh_num;
if (nhp->family == AF_INET6
- && memcmp(&nhp->u.prefix6, &si->addr.ipv6, 16) == 0)
- si->nh_valid = !!nh_num;
+ && memcmp(&nhp->u.prefix6, &nh->addr.ipv6, 16) == 0)
+ nh->nh_valid = !!nh_num;
- if (si->state == STATIC_START)
- static_zebra_route_add(rn, si, vrf->vrf_id, safi, true);
+ if (nh->state == STATIC_START)
+ static_zebra_route_add(rn, pn, safi, true);
}
}
@@ -67,6 +67,8 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,
struct route_table *stable;
struct static_vrf *svrf;
struct route_node *rn;
+ struct static_path *pn;
+ struct static_route_info *si;
svrf = vrf->info;
if (!svrf)
@@ -78,17 +80,26 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp,
if (sp) {
rn = srcdest_rnode_lookup(stable, sp, NULL);
- if (rn) {
- static_nht_update_rn(rn, nhp, nh_num, nh_vrf_id,
- vrf, safi);
+ if (rn && rn->info) {
+ si = static_route_info_from_rnode(rn);
+ frr_each(static_path_list, &si->path_list, pn) {
+ static_nht_update_path(rn, pn, nhp, nh_num,
+ nh_vrf_id, vrf, safi);
+ }
route_unlock_node(rn);
}
return;
}
- for (rn = route_top(stable); rn; rn = route_next(rn))
- static_nht_update_rn(rn, nhp, nh_num, nh_vrf_id, vrf, safi);
-
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ static_nht_update_path(rn, pn, nhp, nh_num, nh_vrf_id,
+ vrf, safi);
+ }
+ }
}
void static_nht_update(struct prefix *sp, struct prefix *nhp,
@@ -111,8 +122,10 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi,
{
struct static_vrf *svrf;
struct route_table *stable;
- struct static_route *si;
+ struct static_nexthop *nh;
+ struct static_path *pn;
struct route_node *rn;
+ struct static_route_info *si;
svrf = vrf->info;
if (!svrf)
@@ -123,25 +136,33 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi,
return;
for (rn = route_top(stable); rn; rn = route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- if (si->nh_vrf_id != nh_vrf_id)
- continue;
-
- if (nhp->family == AF_INET
- && nhp->u.prefix4.s_addr != si->addr.ipv4.s_addr)
- continue;
-
- if (nhp->family == AF_INET6
- && memcmp(&nhp->u.prefix6, &si->addr.ipv6, 16) != 0)
- continue;
-
- /*
- * We've been told that a nexthop we depend
- * on has changed in some manner, so reset
- * the state machine to allow us to start
- * over.
- */
- si->state = STATIC_START;
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != nh_vrf_id)
+ continue;
+
+ if (nhp->family == AF_INET
+ && nhp->u.prefix4.s_addr
+ != nh->addr.ipv4.s_addr)
+ continue;
+
+ if (nhp->family == AF_INET6
+ && memcmp(&nhp->u.prefix6, &nh->addr.ipv6,
+ 16)
+ != 0)
+ continue;
+
+ /*
+ * We've been told that a nexthop we
+ * depend on has changed in some manner,
+ * so reset the state machine to allow
+ * us to start over.
+ */
+ nh->state = STATIC_START;
+ }
}
}
}
@@ -164,8 +185,10 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi,
{
struct static_vrf *svrf;
struct route_table *stable;
- struct static_route *si;
struct route_node *rn;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
svrf = vrf->info;
if (!svrf)
@@ -178,9 +201,14 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi,
rn = srcdest_rnode_lookup(stable, sp, NULL);
if (!rn)
return;
-
- for (si = rn->info; si; si = si->next)
- si->state = state;
+ si = rn->info;
+ if (si) {
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ nh->state = state;
+ }
+ }
+ }
route_unlock_node(rn);
}
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
index e8d6a4289b..d9f2faabaf 100644
--- a/staticd/static_routes.c
+++ b/staticd/static_routes.c
@@ -32,267 +32,367 @@
#include "static_memory.h"
#include "static_zebra.h"
-/* Install static route into rib. */
-static void static_install_route(struct route_node *rn,
- struct static_route *si_changed, safi_t safi)
-{
- struct static_route *si;
+DEFINE_MTYPE_STATIC(STATIC, STATIC_ROUTE, "Static Route Info");
+DEFINE_MTYPE(STATIC, STATIC_PATH, "Static Path");
- for (si = rn->info; si; si = si->next)
- static_zebra_nht_register(rn, si, true);
+/* Install static path into rib. */
+void static_install_path(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf)
+{
+ struct static_nexthop *nh;
- si = rn->info;
- if (si)
- static_zebra_route_add(rn, si_changed, si->vrf_id, safi, true);
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh)
+ static_zebra_nht_register(rn, nh, true);
+ if (static_nexthop_list_count(&pn->nexthop_list) && svrf && svrf->vrf)
+ static_zebra_route_add(rn, pn, safi, true);
}
-/* Uninstall static route from RIB. */
-static void static_uninstall_route(vrf_id_t vrf_id, safi_t safi,
- struct route_node *rn,
- struct static_route *si_changed)
+/* Uninstall static path from RIB. */
+static void static_uninstall_path(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf)
{
-
- if (rn->info)
- static_zebra_route_add(rn, si_changed, vrf_id, safi, true);
+ if (static_nexthop_list_count(&pn->nexthop_list))
+ static_zebra_route_add(rn, pn, safi, true);
else
- static_zebra_route_add(rn, si_changed, vrf_id, safi, false);
+ static_zebra_route_add(rn, pn, safi, false);
}
-int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
- struct prefix_ipv6 *src_p, union g_addr *gate,
- const char *ifname, enum static_blackhole_type bh_type,
- route_tag_t tag, uint8_t distance, struct static_vrf *svrf,
- struct static_vrf *nh_svrf,
- struct static_nh_label *snh_label, uint32_t table_id,
- bool onlink)
+struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_ipv6 *src_p,
+ struct static_vrf *svrf)
{
struct route_node *rn;
- struct static_route *si;
- struct static_route *pp;
- struct static_route *cp;
- struct static_route *update = NULL;
+ struct static_route_info *si;
struct route_table *stable = svrf->stable[afi][safi];
- struct interface *ifp;
if (!stable)
- return -1;
-
- if (!gate && (type == STATIC_IPV4_GATEWAY
- || type == STATIC_IPV4_GATEWAY_IFNAME
- || type == STATIC_IPV6_GATEWAY
- || type == STATIC_IPV6_GATEWAY_IFNAME))
- return -1;
-
- if (!ifname
- && (type == STATIC_IFNAME || type == STATIC_IPV4_GATEWAY_IFNAME
- || type == STATIC_IPV6_GATEWAY_IFNAME))
- return -1;
+ return NULL;
/* Lookup static route prefix. */
rn = srcdest_rnode_get(stable, p, src_p);
- /* Do nothing if there is a same static route. */
- for (si = rn->info; si; si = si->next) {
- if (type == si->type
- && (!gate
- || ((afi == AFI_IP
- && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4))
- || (afi == AFI_IP6
- && IPV6_ADDR_SAME(gate, &si->addr.ipv6))))
- && (!strcmp(ifname ? ifname : "", si->ifname))
- && nh_svrf->vrf->vrf_id == si->nh_vrf_id) {
- if ((distance == si->distance) && (tag == si->tag)
- && (table_id == si->table_id)
- && !memcmp(&si->snh_label, snh_label,
- sizeof(struct static_nh_label))
- && si->bh_type == bh_type && si->onlink == onlink) {
- route_unlock_node(rn);
- return 0;
- }
- update = si;
+ si = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(struct static_route_info));
+ static_route_info_init(si);
+
+ rn->info = si;
+
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(svrf->vrf);
+
+ return rn;
+}
+
+/* To delete the srcnodes */
+static void static_del_src_route(struct route_node *rn, safi_t safi,
+ struct static_vrf *svrf)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ si = rn->info;
+
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ static_del_path(rn, pn, safi, svrf);
+ }
+
+ XFREE(MTYPE_STATIC_ROUTE, rn->info);
+ route_unlock_node(rn);
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!static_vrf_has_config(svrf))
+ vrf_reset_user_cfged(svrf->vrf);
+}
+
+void static_del_route(struct route_node *rn, safi_t safi,
+ struct static_vrf *svrf)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+ struct route_table *src_table;
+ struct route_node *src_node;
+
+ si = rn->info;
+
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ static_del_path(rn, pn, safi, svrf);
+ }
+
+ /* clean up for dst table */
+ src_table = srcdest_srcnode_table(rn);
+ if (src_table) {
+ /* This means the route_node is part of the top hierarchy
+ * and refers to a destination prefix.
+ */
+ for (src_node = route_top(src_table); src_node;
+ src_node = route_next(src_node)) {
+ static_del_src_route(src_node, safi, svrf);
}
}
+ XFREE(MTYPE_STATIC_ROUTE, rn->info);
+ route_unlock_node(rn);
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!static_vrf_has_config(svrf))
+ vrf_reset_user_cfged(svrf->vrf);
+}
+
+bool static_add_nexthop_validate(struct static_vrf *svrf, static_types type,
+ struct ipaddr *ipaddr)
+{
+ switch (type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ if (if_lookup_exact_address(&ipaddr->ipaddr_v4, AF_INET,
+ svrf->vrf->vrf_id))
+ return false;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ if (if_lookup_exact_address(&ipaddr->ipaddr_v6, AF_INET6,
+ svrf->vrf->vrf_id))
+ return false;
+ break;
+ default:
+ break;
+ }
- /* Distance or tag or label changed, delete existing first. */
- if (update)
- static_delete_route(afi, safi, type, p, src_p, gate, ifname,
- update->tag, update->distance, svrf,
- &update->snh_label, table_id);
+ return true;
+}
+
+struct static_path *static_add_path(struct route_node *rn, uint8_t distance)
+{
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ route_lock_node(rn);
/* Make new static route structure. */
- si = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(struct static_route));
-
- si->type = type;
- si->distance = distance;
- si->bh_type = bh_type;
- si->tag = tag;
- si->vrf_id = svrf->vrf->vrf_id;
- si->nh_vrf_id = nh_svrf->vrf->vrf_id;
- strlcpy(si->nh_vrfname, nh_svrf->vrf->name, sizeof(si->nh_vrfname));
- si->table_id = table_id;
- si->onlink = onlink;
+ pn = XCALLOC(MTYPE_STATIC_PATH, sizeof(struct static_path));
+
+ pn->distance = distance;
+ static_nexthop_list_init(&(pn->nexthop_list));
+
+ si = rn->info;
+ static_path_list_add_head(&(si->path_list), pn);
+
+ return pn;
+}
+
+void static_del_path(struct route_node *rn, struct static_path *pn, safi_t safi,
+ struct static_vrf *svrf)
+{
+ struct static_route_info *si;
+ struct static_nexthop *nh;
+
+ si = rn->info;
+
+ static_path_list_del(&si->path_list, pn);
+
+ frr_each_safe(static_nexthop_list, &pn->nexthop_list, nh) {
+ static_delete_nexthop(rn, pn, safi, svrf, nh);
+ }
+
+ route_unlock_node(rn);
+
+ XFREE(MTYPE_STATIC_PATH, pn);
+}
+
+struct static_nexthop *
+static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi,
+ struct static_vrf *svrf, static_types type,
+ struct ipaddr *ipaddr, const char *ifname,
+ const char *nh_vrf, uint32_t color)
+{
+ struct static_nexthop *nh;
+ struct static_vrf *nh_svrf;
+ struct interface *ifp;
+ struct static_nexthop *cp;
+
+ route_lock_node(rn);
+
+ nh_svrf = static_vty_get_unknown_vrf(nh_vrf);
+
+ if (!nh_svrf)
+ return NULL;
+
+ /* Make new static route structure. */
+ nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop));
+
+ nh->type = type;
+ nh->color = color;
+
+ nh->nh_vrf_id = nh_svrf->vrf->vrf_id;
+ strlcpy(nh->nh_vrfname, nh_svrf->vrf->name, sizeof(nh->nh_vrfname));
if (ifname)
- strlcpy(si->ifname, ifname, sizeof(si->ifname));
- si->ifindex = IFINDEX_INTERNAL;
+ strlcpy(nh->ifname, ifname, sizeof(nh->ifname));
+ nh->ifindex = IFINDEX_INTERNAL;
switch (type) {
case STATIC_IPV4_GATEWAY:
case STATIC_IPV4_GATEWAY_IFNAME:
- si->addr.ipv4 = gate->ipv4;
+ nh->addr.ipv4 = ipaddr->ipaddr_v4;
break;
case STATIC_IPV6_GATEWAY:
case STATIC_IPV6_GATEWAY_IFNAME:
- si->addr.ipv6 = gate->ipv6;
+ nh->addr.ipv6 = ipaddr->ipaddr_v6;
break;
- case STATIC_IFNAME:
+ default:
break;
}
-
- /* Save labels, if any. */
- memcpy(&si->snh_label, snh_label, sizeof(struct static_nh_label));
-
/*
* Add new static route information to the tree with sort by
- * distance value and gateway address.
+ * 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
+ frr_each(static_nexthop_list, &pn->nexthop_list, cp) {
+ if (nh->type == STATIC_IPV4_GATEWAY
&& cp->type == STATIC_IPV4_GATEWAY) {
- if (ntohl(si->addr.ipv4.s_addr)
+ if (ntohl(nh->addr.ipv4.s_addr)
< ntohl(cp->addr.ipv4.s_addr))
break;
- if (ntohl(si->addr.ipv4.s_addr)
+ if (ntohl(nh->addr.ipv4.s_addr)
> ntohl(cp->addr.ipv4.s_addr))
continue;
}
}
+ static_nexthop_list_add_after(&(pn->nexthop_list), cp, nh);
- /* Make linked list. */
- if (pp)
- pp->next = si;
- else
- rn->info = si;
- if (cp)
- cp->prev = si;
- si->prev = pp;
- si->next = cp;
+ if (nh_svrf->vrf->vrf_id == VRF_UNKNOWN)
+ return nh;
/* check whether interface exists in system & install if it does */
- switch (si->type) {
+ switch (nh->type) {
case STATIC_IPV4_GATEWAY:
case STATIC_IPV6_GATEWAY:
- static_zebra_nht_register(rn, si, true);
break;
case STATIC_IPV4_GATEWAY_IFNAME:
case STATIC_IPV6_GATEWAY_IFNAME:
- ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
+ ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
if (ifp && ifp->ifindex != IFINDEX_INTERNAL)
- si->ifindex = ifp->ifindex;
+ nh->ifindex = ifp->ifindex;
else
- zlog_warn("Static Route using %s interface not installed because the interface does not exist in specified vrf",
- ifname);
+ zlog_warn(
+ "Static Route using %s interface not installed because the interface does not exist in specified vrf",
+ ifname);
- static_zebra_nht_register(rn, si, true);
break;
case STATIC_BLACKHOLE:
- static_install_route(rn, si, safi);
break;
case STATIC_IFNAME:
ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
if (ifp && ifp->ifindex != IFINDEX_INTERNAL) {
- si->ifindex = ifp->ifindex;
- static_install_route(rn, si, safi);
+ nh->ifindex = ifp->ifindex;
} else
- zlog_warn("Static Route using %s interface not installed because the interface does not exist in specified vrf",
- ifname);
-
+ zlog_warn(
+ "Static Route using %s interface not installed because the interface does not exist in specified vrf",
+ ifname);
break;
}
- return 1;
+ return nh;
}
-int static_delete_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
- struct prefix_ipv6 *src_p, union g_addr *gate,
- const char *ifname, route_tag_t tag, uint8_t distance,
- struct static_vrf *svrf,
- struct static_nh_label *snh_label,
- uint32_t table_id)
+void static_install_nexthop(struct route_node *rn, struct static_path *pn,
+ struct static_nexthop *nh, safi_t safi,
+ struct static_vrf *svrf, const char *ifname,
+ static_types type, const char *nh_vrf)
{
- struct route_node *rn;
- struct static_route *si;
- struct route_table *stable;
+ struct static_vrf *nh_svrf;
+ struct interface *ifp;
- /* Lookup table. */
- stable = static_vrf_static_table(afi, safi, svrf);
- if (!stable)
- return -1;
+ nh_svrf = static_vty_get_unknown_vrf(nh_vrf);
- /* Lookup static route prefix. */
- rn = srcdest_rnode_lookup(stable, p, src_p);
- if (!rn)
- return 0;
-
- /* Find same static route is the tree */
- for (si = rn->info; si; si = si->next)
- if (type == si->type
- && (!gate
- || ((afi == AFI_IP
- && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4))
- || (afi == AFI_IP6
- && IPV6_ADDR_SAME(gate, &si->addr.ipv6))))
- && (!strcmp(ifname ? ifname : "", si->ifname))
- && (!tag || (tag == si->tag))
- && (table_id == si->table_id)
- && (!snh_label->num_labels
- || !memcmp(&si->snh_label, snh_label,
- sizeof(struct static_nh_label))))
- break;
-
- /* Can't find static route. */
- if (!si) {
- route_unlock_node(rn);
- return 0;
+ if (!nh_svrf)
+ return;
+
+ if (nh_svrf->vrf->vrf_id == VRF_UNKNOWN)
+ return;
+
+ /* check whether interface exists in system & install if it does */
+ switch (nh->type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ if (!static_zebra_nh_update(rn, nh))
+ static_zebra_nht_register(rn, nh, true);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ if (!static_zebra_nh_update(rn, nh))
+ static_zebra_nht_register(rn, nh, true);
+ break;
+ case STATIC_BLACKHOLE:
+ static_install_path(rn, pn, safi, svrf);
+ break;
+ case STATIC_IFNAME:
+ ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
+ if (ifp && ifp->ifindex != IFINDEX_INTERNAL)
+ static_install_path(rn, pn, safi, svrf);
+
+ break;
}
+}
- static_zebra_nht_register(rn, si, false);
+int static_delete_nexthop(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf,
+ struct static_nexthop *nh)
+{
+ struct static_vrf *nh_svrf;
- /* 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;
+ nh_svrf = static_vrf_lookup_by_name(nh->nh_vrfname);
+
+ static_nexthop_list_del(&(pn->nexthop_list), nh);
+ if (nh_svrf->vrf->vrf_id == VRF_UNKNOWN)
+ goto EXIT;
+
+ static_zebra_nht_register(rn, nh, false);
/*
* If we have other si nodes then route replace
* else delete the route
*/
- static_uninstall_route(si->vrf_id, safi, rn, si);
- route_unlock_node(rn);
-
- /* Free static route configuration. */
- XFREE(MTYPE_STATIC_ROUTE, si);
+ static_uninstall_path(rn, pn, safi, svrf);
+EXIT:
route_unlock_node(rn);
+ /* Free static route configuration. */
+ XFREE(MTYPE_STATIC_NEXTHOP, nh);
return 1;
}
+static void static_ifindex_update_nh(struct interface *ifp, bool up,
+ struct route_node *rn,
+ struct static_path *pn,
+ struct static_nexthop *nh,
+ struct static_vrf *svrf, safi_t safi)
+{
+ if (!nh->ifname[0])
+ return;
+ if (up) {
+ if (strcmp(nh->ifname, ifp->name))
+ return;
+ if (nh->nh_vrf_id != ifp->vrf_id)
+ return;
+ nh->ifindex = ifp->ifindex;
+ } else {
+ if (nh->ifindex != ifp->ifindex)
+ return;
+ if (nh->nh_vrf_id != ifp->vrf_id)
+ return;
+ nh->ifindex = IFINDEX_INTERNAL;
+ }
+
+ static_install_path(rn, pn, safi, svrf);
+}
+
static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi,
safi_t safi)
{
struct route_table *stable;
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
+ struct static_path *pn;
struct vrf *vrf;
+ struct static_route_info *si;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
struct static_vrf *svrf;
@@ -302,26 +402,17 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi,
stable = static_vrf_static_table(afi, safi, svrf);
if (!stable)
continue;
-
for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- if (!si->ifname[0])
- continue;
- if (up) {
- if (strcmp(si->ifname, ifp->name))
- continue;
- if (si->nh_vrf_id != ifp->vrf_id)
- continue;
- si->ifindex = ifp->ifindex;
- } else {
- if (si->ifindex != ifp->ifindex)
- continue;
- if (si->nh_vrf_id != ifp->vrf_id)
- continue;
- si->ifindex = IFINDEX_INTERNAL;
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list,
+ &pn->nexthop_list, nh) {
+ static_ifindex_update_nh(ifp, up, rn,
+ pn, nh, svrf,
+ safi);
}
-
- static_install_route(rn, si, safi);
}
}
}
@@ -343,26 +434,34 @@ static void static_fixup_vrf(struct static_vrf *svrf,
struct route_table *stable, afi_t afi, safi_t safi)
{
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
struct interface *ifp;
+ struct static_path *pn;
+ struct static_route_info *si;
for (rn = route_top(stable); rn; rn = route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- if (strcmp(svrf->vrf->name, si->nh_vrfname) != 0)
- continue;
-
- si->nh_vrf_id = svrf->vrf->vrf_id;
- si->nh_registered = false;
- if (si->ifindex) {
- ifp = if_lookup_by_name(si->ifname,
- si->nh_vrf_id);
- if (ifp)
- si->ifindex = ifp->ifindex;
- else
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (strcmp(svrf->vrf->name, nh->nh_vrfname)
+ != 0)
continue;
- }
- static_install_route(rn, si, safi);
+ nh->nh_vrf_id = svrf->vrf->vrf_id;
+ nh->nh_registered = false;
+ if (nh->ifindex) {
+ ifp = if_lookup_by_name(nh->ifname,
+ nh->nh_vrf_id);
+ if (ifp)
+ nh->ifindex = ifp->ifindex;
+ else
+ continue;
+ }
+
+ static_install_path(rn, pn, safi, svrf);
+ }
}
}
}
@@ -377,26 +476,31 @@ static void static_fixup_vrf(struct static_vrf *svrf,
* safi -> the safi in question
*/
static void static_enable_vrf(struct static_vrf *svrf,
- struct route_table *stable,
- afi_t afi, safi_t safi)
+ struct route_table *stable, afi_t afi,
+ safi_t safi)
{
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
struct interface *ifp;
- struct vrf *vrf = svrf->vrf;
+ struct static_path *pn;
+ struct static_route_info *si;
for (rn = route_top(stable); rn; rn = route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- si->vrf_id = vrf->vrf_id;
- if (si->ifindex) {
- ifp = if_lookup_by_name(si->ifname,
- si->nh_vrf_id);
- if (ifp)
- si->ifindex = ifp->ifindex;
- else
- continue;
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->ifindex) {
+ ifp = if_lookup_by_name(nh->ifname,
+ nh->nh_vrf_id);
+ if (ifp)
+ nh->ifindex = ifp->ifindex;
+ else
+ continue;
+ }
+ static_install_path(rn, pn, safi, svrf);
}
- static_install_route(rn, si, safi);
}
}
}
@@ -452,14 +556,22 @@ static void static_cleanup_vrf(struct static_vrf *svrf,
afi_t afi, safi_t safi)
{
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
for (rn = route_top(stable); rn; rn = route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- if (strcmp(svrf->vrf->name, si->nh_vrfname) != 0)
- continue;
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (strcmp(svrf->vrf->name, nh->nh_vrfname)
+ != 0)
+ continue;
- static_uninstall_route(si->vrf_id, safi, rn, si);
+ static_uninstall_path(rn, pn, safi, svrf);
+ }
}
}
}
@@ -476,11 +588,23 @@ static void static_disable_vrf(struct route_table *stable,
afi_t afi, safi_t safi)
{
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct stable_info *info;
+ struct static_route_info *si;
+
+ info = route_table_get_info(stable);
- for (rn = route_top(stable); rn; rn = route_next(rn))
- for (si = rn->info; si; si = si->next)
- static_uninstall_route(si->vrf_id, safi, rn, si);
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ static_uninstall_path(rn, pn, safi, info->svrf);
+ }
+ }
+ }
}
/*
@@ -535,17 +659,27 @@ static void static_fixup_intf_nh(struct route_table *stable,
afi_t afi, safi_t safi)
{
struct route_node *rn;
- struct static_route *si;
+ struct stable_info *info;
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ info = route_table_get_info(stable);
for (rn = route_top(stable); rn; rn = route_next(rn)) {
- for (si = rn->info; si; si = si->next) {
- if (si->nh_vrf_id != ifp->vrf_id)
- continue;
+ si = static_route_info_from_rnode(rn);
+ if (!si)
+ continue;
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ if (nh->nh_vrf_id != ifp->vrf_id)
+ continue;
- if (si->ifindex != ifp->ifindex)
- continue;
+ if (nh->ifindex != ifp->ifindex)
+ continue;
- static_install_route(rn, si, safi);
+ static_install_path(rn, pn, safi, info->svrf);
+ }
}
}
}
@@ -589,3 +723,40 @@ void static_ifindex_update(struct interface *ifp, bool up)
static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_UNICAST);
static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST);
}
+
+void static_get_nh_type(static_types stype, char *type, size_t size)
+{
+ switch (stype) {
+ case STATIC_IFNAME:
+ strlcpy(type, "ifindex", size);
+ break;
+ case STATIC_IPV4_GATEWAY:
+ strlcpy(type, "ip4", size);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ strlcpy(type, "ip4-ifindex", size);
+ break;
+ case STATIC_BLACKHOLE:
+ strlcpy(type, "blackhole", size);
+ break;
+ case STATIC_IPV6_GATEWAY:
+ strlcpy(type, "ip6", size);
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ strlcpy(type, "ip6-ifindex", size);
+ break;
+ };
+}
+
+struct stable_info *static_get_stable_info(struct route_node *rn)
+{
+ struct route_table *table;
+
+ table = srcdest_rnode_table(rn);
+ return table->info;
+}
+
+void static_route_info_init(struct static_route_info *si)
+{
+ static_path_list_init(&(si->path_list));
+}
diff --git a/staticd/static_routes.h b/staticd/static_routes.h
index 6414947b16..e5c10d18a4 100644
--- a/staticd/static_routes.h
+++ b/staticd/static_routes.h
@@ -21,6 +21,7 @@
#define __STATIC_ROUTES_H__
#include "lib/mpls.h"
+#include "table.h"
/* Static route label information */
struct static_nh_label {
@@ -35,13 +36,17 @@ enum static_blackhole_type {
STATIC_BLACKHOLE_REJECT
};
+/*
+ * The order for below macros should be in sync with
+ * yang model typedef nexthop-type
+ */
typedef enum {
- STATIC_IFNAME,
+ STATIC_IFNAME = 1,
STATIC_IPV4_GATEWAY,
STATIC_IPV4_GATEWAY_IFNAME,
- STATIC_BLACKHOLE,
STATIC_IPV6_GATEWAY,
STATIC_IPV6_GATEWAY_IFNAME,
+ STATIC_BLACKHOLE,
} static_types;
/*
@@ -64,14 +69,37 @@ enum static_install_states {
STATIC_NOT_INSTALLED,
};
+PREDECL_DLIST(static_path_list);
+PREDECL_DLIST(static_nexthop_list);
+
+/* Static route information */
+struct static_route_info {
+ /* path list */
+ struct static_path_list_head path_list;
+};
+
+/* Static path information */
+struct static_path {
+ /* Linkage for static path lists */
+ struct static_path_list_item list;
+ /* Administrative distance. */
+ uint8_t distance;
+ /* Tag */
+ route_tag_t tag;
+ /* Table-id */
+ uint32_t table_id;
+ /* Nexthop list */
+ struct static_nexthop_list_head nexthop_list;
+};
+
+DECLARE_DLIST(static_path_list, struct static_path, list);
+
/* Static route information. */
-struct static_route {
+struct static_nexthop {
/* For linked list. */
- struct static_route *prev;
- struct static_route *next;
+ struct static_nexthop_list_item list;
/* VRF identifier. */
- vrf_id_t vrf_id;
vrf_id_t nh_vrf_id;
char nh_vrfname[VRF_NAMSIZ + 1];
@@ -81,12 +109,6 @@ struct static_route {
*/
enum static_install_states state;
- /* Administrative distance. */
- uint8_t distance;
-
- /* Tag */
- route_tag_t tag;
-
/* Flag for this static route's type. */
static_types type;
@@ -104,42 +126,75 @@ struct static_route {
/* Label information */
struct static_nh_label snh_label;
- uint32_t table_id;
-
/*
* Whether to pretend the nexthop is directly attached to the specified
* link. Only meaningful when both a gateway address and interface name
* are specified.
*/
bool onlink;
+
+ /* SR-TE color */
+ uint32_t color;
};
+DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list);
+
+
+/*
+ * rib_dest_from_rnode
+ */
+static inline struct static_route_info *
+static_route_info_from_rnode(struct route_node *rn)
+{
+ return (struct static_route_info *)(rn->info);
+}
+
extern bool mpls_enabled;
extern struct zebra_privs_t static_privs;
void static_fixup_vrf_ids(struct static_vrf *svrf);
-extern int static_add_route(afi_t afi, safi_t safi, uint8_t type,
- struct prefix *p, struct prefix_ipv6 *src_p,
- union g_addr *gate, const char *ifname,
- enum static_blackhole_type bh_type, route_tag_t tag,
- uint8_t distance, struct static_vrf *svrf,
- struct static_vrf *nh_svrf,
- struct static_nh_label *snh_label,
- uint32_t table_id, bool onlink);
-
-extern int static_delete_route(afi_t afi, safi_t safi, uint8_t type,
- struct prefix *p, struct prefix_ipv6 *src_p,
- union g_addr *gate, const char *ifname,
- route_tag_t tag, uint8_t distance,
- struct static_vrf *svrf,
- struct static_nh_label *snh_label,
- uint32_t table_id);
+extern struct static_nexthop *
+static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi,
+ struct static_vrf *svrf, static_types type,
+ struct ipaddr *ipaddr, const char *ifname,
+ const char *nh_vrf, uint32_t color);
+extern void static_install_nexthop(struct route_node *rn,
+ struct static_path *pn,
+ struct static_nexthop *nh, safi_t safi,
+ struct static_vrf *svrf, const char *ifname,
+ static_types type, const char *nh_vrf);
+
+extern int static_delete_nexthop(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf,
+ struct static_nexthop *nh);
extern void static_cleanup_vrf_ids(struct static_vrf *disable_svrf);
extern void static_install_intf_nh(struct interface *ifp);
extern void static_ifindex_update(struct interface *ifp, bool up);
+
+extern void static_install_path(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf);
+
+extern struct route_node *static_add_route(afi_t afi, safi_t safi,
+ struct prefix *p,
+ struct prefix_ipv6 *src_p,
+ struct static_vrf *svrf);
+extern void static_del_route(struct route_node *rn, safi_t safi,
+ struct static_vrf *svrf);
+
+extern struct static_path *static_add_path(struct route_node *rn,
+ uint8_t distance);
+extern void static_del_path(struct route_node *rn, struct static_path *pn,
+ safi_t safi, struct static_vrf *svrf);
+
+extern void static_get_nh_type(static_types stype, char *type, size_t size);
+extern bool static_add_nexthop_validate(struct static_vrf *svrf,
+ static_types type,
+ struct ipaddr *ipaddr);
+extern struct stable_info *static_get_stable_info(struct route_node *rn);
+extern void static_route_info_init(struct static_route_info *si);
#endif
diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c
index 6c065932a1..39b86787ff 100644
--- a/staticd/static_vrf.c
+++ b/staticd/static_vrf.c
@@ -30,26 +30,39 @@
#include "static_zebra.h"
#include "static_vty.h"
+DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info");
+
static void zebra_stable_node_cleanup(struct route_table *table,
struct route_node *node)
{
- struct static_route *si, *next;
-
- if (node->info)
- for (si = node->info; si; si = next) {
- next = si->next;
- XFREE(MTYPE_STATIC_ROUTE, si);
+ struct static_nexthop *nh;
+ struct static_path *pn;
+ struct static_route_info *si;
+
+ si = node->info;
+
+ if (si) {
+ frr_each_safe(static_path_list, &si->path_list, pn) {
+ frr_each_safe(static_nexthop_list, &pn->nexthop_list,
+ nh) {
+ static_nexthop_list_del(&pn->nexthop_list, nh);
+ XFREE(MTYPE_STATIC_NEXTHOP, nh);
+ }
+ static_path_list_del(&si->path_list, pn);
+ XFREE(MTYPE_STATIC_PATH, pn);
}
+ }
}
static struct static_vrf *static_vrf_alloc(void)
{
struct route_table *table;
struct static_vrf *svrf;
+ struct stable_info *info;
safi_t safi;
afi_t afi;
- svrf = XCALLOC(MTYPE_TMP, sizeof(struct static_vrf));
+ svrf = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct static_vrf));
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
@@ -57,6 +70,14 @@ static struct static_vrf *static_vrf_alloc(void)
table = srcdest_table_init();
else
table = route_table_init();
+
+ info = XCALLOC(MTYPE_STATIC_RTABLE_INFO,
+ sizeof(struct stable_info));
+ info->svrf = svrf;
+ info->afi = afi;
+ info->safi = safi;
+ route_table_set_info(table, info);
+
table->cleanup = zebra_stable_node_cleanup;
svrf->stable[afi][safi] = table;
}
@@ -81,12 +102,6 @@ static int static_vrf_enable(struct vrf *vrf)
static_fixup_vrf_ids(vrf->info);
- /*
- * We may have static routes that are now possible to
- * insert into the appropriate tables
- */
- static_config_install_delayed_routes(vrf->info);
-
return 0;
}
@@ -102,16 +117,19 @@ static int static_vrf_delete(struct vrf *vrf)
struct static_vrf *svrf;
safi_t safi;
afi_t afi;
+ void *info;
svrf = vrf->info;
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
table = svrf->stable[afi][safi];
+ info = route_table_get_info(table);
route_table_finish(table);
+ XFREE(MTYPE_STATIC_RTABLE_INFO, info);
svrf->stable[afi][safi] = NULL;
}
}
- XFREE(MTYPE_TMP, svrf);
+ XFREE(MTYPE_STATIC_RTABLE_INFO, svrf);
return 0;
}
@@ -210,3 +228,25 @@ void static_vrf_terminate(void)
{
vrf_terminate();
}
+
+struct static_vrf *static_vty_get_unknown_vrf(const char *vrf_name)
+{
+ struct static_vrf *svrf;
+ struct vrf *vrf;
+
+ svrf = static_vrf_lookup_by_name(vrf_name);
+
+ if (svrf)
+ return svrf;
+
+ vrf = vrf_get(VRF_UNKNOWN, vrf_name);
+ if (!vrf)
+ return NULL;
+ svrf = vrf->info;
+ if (!svrf)
+ return NULL;
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(vrf);
+
+ return svrf;
+}
diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h
index 6951e56712..12ad1b255a 100644
--- a/staticd/static_vrf.h
+++ b/staticd/static_vrf.h
@@ -26,6 +26,14 @@ struct static_vrf {
struct route_table *stable[AFI_MAX][SAFI_MAX];
};
+struct stable_info {
+ struct static_vrf *svrf;
+ afi_t afi;
+ safi_t safi;
+};
+
+#define GET_STABLE_VRF_ID(info) info->svrf->vrf->vrf_id
+
struct static_vrf *static_vrf_lookup_by_name(const char *vrf_name);
struct static_vrf *static_vrf_lookup_by_id(vrf_id_t vrf_id);
@@ -36,4 +44,6 @@ void static_vrf_init(void);
struct route_table *static_vrf_static_table(afi_t afi, safi_t safi,
struct static_vrf *svrf);
extern void static_vrf_terminate(void);
+
+struct static_vrf *static_vty_get_unknown_vrf(const char *vrf_name);
#endif
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
index 75bce82eef..ba1a8f9a25 100644
--- a/staticd/static_vty.c
+++ b/staticd/static_vty.c
@@ -27,6 +27,10 @@
#include "table.h"
#include "srcdest_table.h"
#include "mpls.h"
+#include "northbound.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
#include "static_vrf.h"
#include "static_memory.h"
@@ -36,251 +40,44 @@
#ifndef VTYSH_EXTRACT_PL
#include "staticd/static_vty_clippy.c"
#endif
+#include "static_nb.h"
#define STATICD_STR "Static route daemon\n"
-static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty,
- const char *vrf_name)
+static int static_route_leak(struct vty *vty, const char *svrf,
+ const char *nh_svrf, afi_t afi, safi_t safi,
+ const char *negate, const char *dest_str,
+ const char *mask_str, const char *src_str,
+ const char *gate_str, const char *ifname,
+ const char *flag_str, const char *tag_str,
+ const char *distance_str, const char *label_str,
+ const char *table_str, bool onlink,
+ const char *color_str)
{
- struct static_vrf *svrf;
- struct vrf *vrf;
-
- svrf = static_vrf_lookup_by_name(vrf_name);
-
- if (svrf)
- return svrf;
-
- vrf = vrf_get(VRF_UNKNOWN, vrf_name);
- if (!vrf) {
- vty_out(vty, "%% Could not create vrf %s\n", vrf_name);
- return NULL;
- }
- svrf = vrf->info;
- if (!svrf) {
- vty_out(vty, "%% Could not create vrf-info %s\n",
- vrf_name);
- return NULL;
- }
- /* Mark as having FRR configuration */
- vrf_set_user_cfged(vrf);
-
- return svrf;
-}
-
-struct static_hold_route {
- char *vrf_name;
- char *nhvrf_name;
- afi_t afi;
- safi_t safi;
- char *dest_str;
- char *mask_str;
- char *src_str;
- char *gate_str;
- char *ifname;
- char *flag_str;
- char *tag_str;
- char *distance_str;
- char *label_str;
- char *table_str;
- bool onlink;
-
- /* processed & masked destination, used for config display */
- struct prefix dest;
-};
-
-static struct list *static_list;
-
-static int static_list_compare_helper(const char *s1, const char *s2)
-{
- /* extra (!s1 && !s2) to keep SA happy */
- if (s1 == s2 || (!s1 && !s2))
- return 0;
-
- if (!s1 && s2)
- return -1;
-
- if (s1 && !s2)
- return 1;
-
- return strcmp(s1, s2);
-}
-
-static void static_list_delete(struct static_hold_route *shr)
-{
- XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name);
- XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name);
- XFREE(MTYPE_STATIC_ROUTE, shr->dest_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->mask_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->src_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->gate_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->ifname);
- XFREE(MTYPE_STATIC_ROUTE, shr->flag_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->tag_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->distance_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->label_str);
- XFREE(MTYPE_STATIC_ROUTE, shr->table_str);
-
- XFREE(MTYPE_STATIC_ROUTE, shr);
-}
-
-static int static_list_compare(void *arg1, void *arg2)
-{
- struct static_hold_route *shr1 = arg1;
- struct static_hold_route *shr2 = arg2;
int ret;
-
- ret = strcmp(shr1->vrf_name, shr2->vrf_name);
- if (ret)
- return ret;
-
- ret = strcmp(shr1->nhvrf_name, shr2->nhvrf_name);
- if (ret)
- return ret;
-
- ret = shr1->afi - shr2->afi;
- if (ret)
- return ret;
-
- ret = shr1->safi - shr2->safi;
- if (ret)
- return ret;
-
- ret = prefix_cmp(&shr1->dest, &shr2->dest);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->src_str, shr2->src_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->gate_str, shr2->gate_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->ifname, shr2->ifname);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->flag_str, shr2->flag_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->tag_str, shr2->tag_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->distance_str,
- shr2->distance_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->table_str,
- shr2->table_str);
- if (ret)
- return ret;
-
- return static_list_compare_helper(shr1->label_str, shr2->label_str);
-}
-
-
-/* General function for static route. */
-static int zebra_static_route_holdem(
- struct static_vrf *svrf, struct static_vrf *nh_svrf, afi_t afi,
- safi_t safi, const char *negate, struct prefix *dest,
- const char *dest_str, const char *mask_str, const char *src_str,
- const char *gate_str, const char *ifname, const char *flag_str,
- const char *tag_str, const char *distance_str, const char *label_str,
- const char *table_str, bool onlink)
-{
- struct static_hold_route *shr, *lookup;
- struct listnode *node;
-
- zlog_warn("Static Route to %s not installed currently because dependent config not fully available",
- dest_str);
-
- shr = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(*shr));
- shr->vrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, svrf->vrf->name);
- shr->nhvrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, nh_svrf->vrf->name);
- shr->afi = afi;
- shr->safi = safi;
- shr->onlink = onlink;
- if (dest)
- prefix_copy(&shr->dest, dest);
- if (dest_str)
- shr->dest_str = XSTRDUP(MTYPE_STATIC_ROUTE, dest_str);
- if (mask_str)
- shr->mask_str = XSTRDUP(MTYPE_STATIC_ROUTE, mask_str);
- if (src_str)
- shr->src_str = XSTRDUP(MTYPE_STATIC_ROUTE, src_str);
- if (gate_str)
- shr->gate_str = XSTRDUP(MTYPE_STATIC_ROUTE, gate_str);
- if (ifname)
- shr->ifname = XSTRDUP(MTYPE_STATIC_ROUTE, ifname);
- if (flag_str)
- shr->flag_str = XSTRDUP(MTYPE_STATIC_ROUTE, flag_str);
- if (tag_str)
- shr->tag_str = XSTRDUP(MTYPE_STATIC_ROUTE, tag_str);
- if (distance_str)
- shr->distance_str = XSTRDUP(MTYPE_STATIC_ROUTE, distance_str);
- if (label_str)
- shr->label_str = XSTRDUP(MTYPE_STATIC_ROUTE, label_str);
- if (table_str)
- shr->table_str = XSTRDUP(MTYPE_STATIC_ROUTE, table_str);
-
- for (ALL_LIST_ELEMENTS_RO(static_list, node, lookup)) {
- if (static_list_compare(shr, lookup) == 0)
- break;
- }
-
- if (lookup) {
- if (negate) {
- listnode_delete(static_list, lookup);
- static_list_delete(shr);
- static_list_delete(lookup);
-
- return CMD_SUCCESS;
- }
-
- /*
- * If a person enters the same line again
- * we need to silently accept it
- */
- goto shr_cleanup;
- }
-
- if (!negate) {
- listnode_add_sort(static_list, shr);
- return CMD_SUCCESS;
- }
-
- shr_cleanup:
- XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name);
- XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name);
- XFREE(MTYPE_STATIC_ROUTE, shr);
-
- return CMD_SUCCESS;
-}
-
-static int static_route_leak(
- struct vty *vty, struct static_vrf *svrf, struct static_vrf *nh_svrf,
- afi_t afi, safi_t safi, const char *negate, const char *dest_str,
- const char *mask_str, const char *src_str, const char *gate_str,
- const char *ifname, const char *flag_str, const char *tag_str,
- const char *distance_str, const char *label_str, const char *table_str,
- bool onlink)
-{
- int ret;
- uint8_t distance;
struct prefix p, src;
- struct prefix_ipv6 *src_p = NULL;
- union g_addr gate;
- union g_addr *gatep = NULL;
struct in_addr mask;
- enum static_blackhole_type bh_type = 0;
- route_tag_t tag = 0;
uint8_t type;
- struct static_nh_label snh_label;
+ const char *bh_type;
+ char xpath_prefix[XPATH_MAXLEN];
+ char xpath_nexthop[XPATH_MAXLEN];
+ char xpath_mpls[XPATH_MAXLEN];
+ char xpath_label[XPATH_MAXLEN];
+ char ab_xpath[XPATH_MAXLEN];
+ char buf_prefix[PREFIX_STRLEN];
+ char buf_src_prefix[PREFIX_STRLEN];
+ char buf_nh_type[PREFIX_STRLEN];
+ char buf_tag[PREFIX_STRLEN];
+ char buf_tableid[PREFIX_STRLEN];
+ uint8_t label_stack_id = 0;
+ const char *buf_gate_str;
+ uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
+ route_tag_t tag = 0;
uint32_t table_id = 0;
+ const struct lyd_node *dnode;
+
+ memset(buf_src_prefix, 0, PREFIX_STRLEN);
+ memset(buf_nh_type, 0, PREFIX_STRLEN);
ret = str2prefix(dest_str, &p);
if (ret <= 0) {
@@ -322,7 +119,6 @@ static int static_route_leak(
__func__, src_str);
return CMD_WARNING_CONFIG_FAILED;
}
- src_p = (struct prefix_ipv6 *)&src;
}
break;
default:
@@ -332,29 +128,29 @@ static int static_route_leak(
/* Apply mask for given prefix. */
apply_mask(&p);
- if (svrf->vrf->vrf_id == VRF_UNKNOWN
- || nh_svrf->vrf->vrf_id == VRF_UNKNOWN) {
- vrf_set_user_cfged(svrf->vrf);
- return zebra_static_route_holdem(
- svrf, nh_svrf, afi, safi, negate, &p, dest_str,
- mask_str, src_str, gate_str, ifname, flag_str, tag_str,
- distance_str, label_str, table_str, onlink);
- }
+ prefix2str(&p, buf_prefix, sizeof(buf_prefix));
- if (table_str) {
- /* table configured. check consistent with vrf config
- */
- if (svrf->vrf->data.l.table_id != RT_TABLE_MAIN) {
- if (vty)
- vty_out(vty,
- "%% Table %s overlaps vrf table %u\n",
- table_str, svrf->vrf->data.l.table_id);
- else
- zlog_warn("%s: Table %s overlaps vrf table %u",
- __func__, table_str,
- svrf->vrf->data.l.table_id);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (src_str)
+ prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));
+ if (gate_str)
+ buf_gate_str = gate_str;
+ else
+ buf_gate_str = "";
+
+ if (gate_str == NULL && ifname == NULL)
+ type = STATIC_BLACKHOLE;
+ else if (gate_str && ifname) {
+ if (afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY_IFNAME;
+ else
+ type = STATIC_IPV6_GATEWAY_IFNAME;
+ } else if (ifname)
+ type = STATIC_IFNAME;
+ else {
+ if (afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY;
+ else
+ type = STATIC_IPV6_GATEWAY;
}
/* Administrative distance. */
@@ -367,169 +163,170 @@ static int static_route_leak(
if (tag_str)
tag = strtoul(tag_str, NULL, 10);
- /* Labels */
- memset(&snh_label, 0, sizeof(struct static_nh_label));
- if (label_str) {
- if (!mpls_enabled) {
- if (vty)
- vty_out(vty,
- "%% MPLS not turned on in kernel, ignoring command\n");
- else
- zlog_warn(
- "%s: MPLS not turned on in kernel ignoring static route to %s",
- __func__, dest_str);
- return CMD_WARNING_CONFIG_FAILED;
- }
- int rc = mpls_str2label(label_str, &snh_label.num_labels,
- snh_label.label);
- if (rc < 0) {
- switch (rc) {
- case -1:
- if (vty)
- vty_out(vty, "%% Malformed label(s)\n");
- else
- zlog_warn(
- "%s: Malformed labels specified for route %s",
- __func__, dest_str);
- break;
- case -2:
- if (vty)
- vty_out(vty,
- "%% Cannot use reserved label(s) (%d-%d)\n",
- MPLS_LABEL_RESERVED_MIN,
- MPLS_LABEL_RESERVED_MAX);
- else
- zlog_warn(
- "%s: Cannot use reserved labels (%d-%d) for %s",
- __func__,
- MPLS_LABEL_RESERVED_MIN,
- MPLS_LABEL_RESERVED_MAX,
- dest_str);
- break;
- case -3:
- if (vty)
- vty_out(vty,
- "%% Too many labels. Enter %d or fewer\n",
- MPLS_MAX_LABELS);
- else
- zlog_warn(
- "%s: Too many labels, Enter %d or fewer for %s",
- __func__, MPLS_MAX_LABELS,
- dest_str);
- break;
- }
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
/* TableID */
if (table_str)
table_id = atol(table_str);
- /* Null0 static route. */
- if (ifname != NULL) {
- if (strcasecmp(ifname, "Null0") == 0
- || strcasecmp(ifname, "reject") == 0
- || strcasecmp(ifname, "blackhole") == 0) {
- if (vty)
- vty_out(vty,
- "%% Nexthop interface name can not be from reserved keywords (Null0, reject, blackhole)\n");
- else
- zlog_warn(
- "%s: %s: Nexthop interface name can not be from reserved keywords (Null0, reject, blackhole)",
- __func__, dest_str);
- return CMD_WARNING_CONFIG_FAILED;
+ static_get_nh_type(type, buf_nh_type, PREFIX_STRLEN);
+ if (!negate) {
+ /* route + path procesing */
+ if (src_str)
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_S_ROUTE_SRC_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", svrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(afi, safi),
+ buf_src_prefix, distance);
+ else
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_STATIC_ROUTE_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", svrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(afi, safi),
+ distance);
+
+ nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL);
+
+ /* Tag processing */
+ snprintf(buf_tag, sizeof(buf_tag), "%u", tag);
+ strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TAG_XPATH,
+ sizeof(ab_xpath));
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tag);
+
+ /* Table-Id processing */
+ snprintf(buf_tableid, sizeof(buf_tableid), "%u", table_id);
+ strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TABLEID_XPATH,
+ sizeof(ab_xpath));
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tableid);
+ /* nexthop processing */
+
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NH_KEY_XPATH, buf_nh_type, nh_svrf,
+ buf_gate_str, ifname);
+ strlcpy(xpath_nexthop, xpath_prefix, sizeof(xpath_nexthop));
+ strlcat(xpath_nexthop, ab_xpath, sizeof(xpath_nexthop));
+ nb_cli_enqueue_change(vty, xpath_nexthop, NB_OP_CREATE, NULL);
+
+ if (type == STATIC_BLACKHOLE) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_BH_XPATH,
+ sizeof(ab_xpath));
+
+ /* Route flags */
+ if (flag_str) {
+ switch (flag_str[0]) {
+ case 'r':
+ bh_type = "reject";
+ break;
+ case 'b':
+ bh_type = "unspec";
+ break;
+ case 'N':
+ bh_type = "null";
+ break;
+ default:
+ bh_type = NULL;
+ break;
+ }
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, bh_type);
+ } else {
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "null");
+ }
}
- }
-
- /* Route flags */
- if (flag_str) {
- switch (flag_str[0]) {
- case 'r':
- bh_type = STATIC_BLACKHOLE_REJECT;
- break;
- case 'b':
- bh_type = STATIC_BLACKHOLE_DROP;
- break;
- case 'N':
- bh_type = STATIC_BLACKHOLE_NULL;
- break;
- default:
- if (vty)
- vty_out(vty, "%% Malformed flag %s \n",
- flag_str);
+ if (type == STATIC_IPV4_GATEWAY_IFNAME
+ || type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_ONLINK_XPATH,
+ sizeof(ab_xpath));
+
+ if (onlink)
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "true");
else
- zlog_warn("%s: Malformed flag %s for %s",
- __func__, flag_str, dest_str);
- return CMD_WARNING_CONFIG_FAILED;
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "false");
}
- }
-
- if (gate_str) {
- if (inet_pton(afi2family(afi), gate_str, &gate) != 1) {
- if (vty)
- vty_out(vty,
- "%% Malformed nexthop address %s\n",
- gate_str);
- else
- zlog_warn(
- "%s: Malformed nexthop address %s for %s",
- __func__, gate_str, dest_str);
- return CMD_WARNING_CONFIG_FAILED;
+ if (type == STATIC_IPV4_GATEWAY
+ || type == STATIC_IPV6_GATEWAY
+ || type == STATIC_IPV4_GATEWAY_IFNAME
+ || type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_COLOR_XPATH,
+ sizeof(ab_xpath));
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY,
+ color_str);
}
- gatep = &gate;
-
- if (afi == AFI_IP && !negate) {
- if (if_lookup_exact_address(&gatep->ipv4, AF_INET,
- svrf->vrf->vrf_id))
- if (vty)
- vty_out(vty,
- "%% Warning!! Local connected address is configured as Gateway IP(%s)\n",
- gate_str);
- } else if (afi == AFI_IP6 && !negate) {
- if (if_lookup_exact_address(&gatep->ipv6, AF_INET6,
- svrf->vrf->vrf_id))
- if (vty)
- vty_out(vty,
- "%% Warning!! Local connected address is configured as Gateway IPv6(%s)\n",
- gate_str);
+ if (label_str) {
+ /* copy of label string (start) */
+ char *ostr;
+ /* pointer to next segment */
+ char *nump;
+
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
+
+ ostr = XSTRDUP(MTYPE_TMP, label_str);
+ while ((nump = strsep(&ostr, "/")) != NULL) {
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NHLB_KEY_XPATH,
+ label_stack_id);
+ strlcpy(xpath_label, xpath_mpls,
+ sizeof(xpath_label));
+ strlcat(xpath_label, ab_xpath,
+ sizeof(xpath_label));
+ nb_cli_enqueue_change(vty, xpath_label,
+ NB_OP_MODIFY, nump);
+ label_stack_id++;
+ }
+ XFREE(MTYPE_TMP, ostr);
+ } else {
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
}
-
- }
-
- if (gate_str == NULL && ifname == NULL)
- type = STATIC_BLACKHOLE;
- else if (gate_str && ifname) {
- if (afi == AFI_IP)
- type = STATIC_IPV4_GATEWAY_IFNAME;
- else
- type = STATIC_IPV6_GATEWAY_IFNAME;
- } else if (ifname)
- type = STATIC_IFNAME;
- else {
- if (afi == AFI_IP)
- type = STATIC_IPV4_GATEWAY;
- else
- type = STATIC_IPV6_GATEWAY;
- }
-
- if (!negate) {
- static_add_route(afi, safi, type, &p, src_p, gatep, ifname,
- bh_type, tag, distance, svrf, nh_svrf,
- &snh_label, table_id, onlink);
- /* Mark as having FRR configuration */
- vrf_set_user_cfged(svrf->vrf);
+ ret = nb_cli_apply_changes(vty, xpath_prefix);
} else {
- static_delete_route(afi, safi, type, &p, src_p, gatep, ifname,
- tag, distance, svrf, &snh_label, table_id);
- /* If no other FRR config for this VRF, mark accordingly. */
- if (!static_vrf_has_config(svrf))
- vrf_reset_user_cfged(svrf->vrf);
+ if (src_str)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", svrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(afi, safi),
+ buf_src_prefix, distance, buf_nh_type, nh_svrf,
+ buf_gate_str, ifname);
+ else
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", svrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(afi, safi),
+ distance, buf_nh_type, nh_svrf, buf_gate_str,
+ ifname);
+
+ dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
+ if (!dnode)
+ return ret;
+
+ dnode = yang_get_subtree_with_no_sibling(dnode);
+ assert(dnode);
+ yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN);
+
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY, NULL);
+ ret = nb_cli_apply_changes(vty, ab_xpath);
}
- return CMD_SUCCESS;
+ return ret;
}
-
static int static_route(struct vty *vty, afi_t afi, safi_t safi,
const char *negate, const char *dest_str,
const char *mask_str, const char *src_str,
@@ -538,77 +335,28 @@ static int static_route(struct vty *vty, afi_t afi, safi_t safi,
const char *distance_str, const char *vrf_name,
const char *label_str, const char *table_str)
{
- struct static_vrf *svrf;
-
- /* VRF id */
- svrf = static_vrf_lookup_by_name(vrf_name);
-
- /* When trying to delete, the VRF must exist. */
- if (negate && !svrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf_name);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* When trying to create, create the VRF if it doesn't exist.
- * Note: The VRF isn't active until we hear about it from the kernel.
- */
- if (!svrf) {
- svrf = static_vty_get_unknown_vrf(vty, vrf_name);
- if (!svrf)
- return CMD_WARNING_CONFIG_FAILED;
- }
- return static_route_leak(vty, svrf, svrf, afi, safi, negate, dest_str,
- mask_str, src_str, gate_str, ifname, flag_str,
- tag_str, distance_str, label_str, table_str,
- false);
-}
-
-void static_config_install_delayed_routes(struct static_vrf *svrf)
-{
- struct listnode *node, *nnode;
- struct static_hold_route *shr;
- struct static_vrf *osvrf, *nh_svrf;
- int installed;
+ if (!vrf_name)
+ vrf_name = VRF_DEFAULT_NAME;
- for (ALL_LIST_ELEMENTS(static_list, node, nnode, shr)) {
- osvrf = static_vrf_lookup_by_name(shr->vrf_name);
- nh_svrf = static_vrf_lookup_by_name(shr->nhvrf_name);
-
- if (osvrf != svrf && nh_svrf != svrf)
- continue;
-
- if (osvrf->vrf->vrf_id == VRF_UNKNOWN
- || nh_svrf->vrf->vrf_id == VRF_UNKNOWN)
- continue;
-
- installed = static_route_leak(
- NULL, osvrf, nh_svrf, shr->afi, shr->safi, NULL,
- shr->dest_str, shr->mask_str, shr->src_str,
- shr->gate_str, shr->ifname, shr->flag_str, shr->tag_str,
- shr->distance_str, shr->label_str, shr->table_str,
- shr->onlink);
-
- if (installed != CMD_SUCCESS)
- zlog_debug(
- "%s: Attempt to install %s as a route and it was rejected",
- __func__, shr->dest_str);
- listnode_delete(static_list, shr);
- static_list_delete(shr);
- }
+ return static_route_leak(vty, vrf_name, vrf_name, afi, safi, negate,
+ dest_str, mask_str, src_str, gate_str, ifname,
+ flag_str, tag_str, distance_str, label_str,
+ table_str, false, NULL);
}
/* Write static route configuration. */
int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi,
safi_t safi, const char *cmd)
{
- struct static_hold_route *shr;
- struct listnode *node;
char spacing[100];
struct route_node *rn;
- struct static_route *si;
+ struct static_nexthop *nh;
+ struct static_path *pn;
struct route_table *stable;
+ struct static_route_info *si;
char buf[SRCDEST2STR_BUFFER];
int write = 0;
+ struct stable_info *info;
stable = svrf->stable[afi][safi];
if (stable == NULL)
@@ -617,125 +365,110 @@ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi,
snprintf(spacing, sizeof(spacing), "%s%s",
(svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd);
- /*
- * Static routes for vrfs not fully inited
- */
- for (ALL_LIST_ELEMENTS_RO(static_list, node, shr)) {
- if (shr->afi != afi || shr->safi != safi)
+ for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
+ si = static_route_info_from_rnode(rn);
+ if (!si)
continue;
-
- if (strcmp(svrf->vrf->name, shr->vrf_name) != 0)
- continue;
-
- char dest_str[PREFIX_STRLEN];
-
- prefix2str(&shr->dest, dest_str, sizeof(dest_str));
-
- vty_out(vty, "%s ", spacing);
- if (shr->dest_str)
- vty_out(vty, "%s ", dest_str);
- if (shr->src_str)
- vty_out(vty, "from %s ", shr->src_str);
- if (shr->gate_str)
- vty_out(vty, "%s ", shr->gate_str);
- if (shr->ifname)
- vty_out(vty, "%s ", shr->ifname);
- if (shr->flag_str)
- vty_out(vty, "%s ", shr->flag_str);
- if (shr->tag_str)
- vty_out(vty, "tag %s ", shr->tag_str);
- if (shr->distance_str)
- vty_out(vty, "%s ", shr->distance_str);
- if (shr->label_str)
- vty_out(vty, "label %s ", shr->label_str);
- if (shr->table_str)
- vty_out(vty, "table %s", shr->table_str);
- if (strcmp(shr->vrf_name, shr->nhvrf_name) != 0)
- vty_out(vty, "nexthop-vrf %s ", shr->nhvrf_name);
- if (shr->onlink)
- vty_out(vty, "onlink");
- vty_out(vty, "\n");
- }
-
- for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))
- for (si = rn->info; si; si = si->next) {
- vty_out(vty, "%s %s", spacing,
- srcdest_rnode2str(rn, buf, sizeof(buf)));
-
- switch (si->type) {
- case STATIC_IPV4_GATEWAY:
- vty_out(vty, " %s", inet_ntoa(si->addr.ipv4));
- break;
- case STATIC_IPV6_GATEWAY:
- vty_out(vty, " %s",
- inet_ntop(AF_INET6, &si->addr.ipv6, buf,
- sizeof(buf)));
- break;
- case STATIC_IFNAME:
- vty_out(vty, " %s", si->ifname);
- break;
- case STATIC_BLACKHOLE:
- switch (si->bh_type) {
- case STATIC_BLACKHOLE_DROP:
- vty_out(vty, " blackhole");
+ info = static_get_stable_info(rn);
+ frr_each(static_path_list, &si->path_list, pn) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
+ vty_out(vty, "%s %s", spacing,
+ srcdest_rnode2str(rn, buf,
+ sizeof(buf)));
+
+ switch (nh->type) {
+ case STATIC_IPV4_GATEWAY:
+ vty_out(vty, " %s",
+ inet_ntoa(nh->addr.ipv4));
break;
- case STATIC_BLACKHOLE_NULL:
- vty_out(vty, " Null0");
+ case STATIC_IPV6_GATEWAY:
+ vty_out(vty, " %s",
+ inet_ntop(AF_INET6,
+ &nh->addr.ipv6, buf,
+ sizeof(buf)));
break;
- case STATIC_BLACKHOLE_REJECT:
- vty_out(vty, " reject");
+ case STATIC_IFNAME:
+ vty_out(vty, " %s", nh->ifname);
+ break;
+ case STATIC_BLACKHOLE:
+ switch (nh->bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ vty_out(vty, " blackhole");
+ break;
+ case STATIC_BLACKHOLE_NULL:
+ vty_out(vty, " Null0");
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ vty_out(vty, " reject");
+ break;
+ }
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ vty_out(vty, " %s %s",
+ inet_ntop(AF_INET,
+ &nh->addr.ipv4, buf,
+ sizeof(buf)),
+ nh->ifname);
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ vty_out(vty, " %s %s",
+ inet_ntop(AF_INET6,
+ &nh->addr.ipv6, buf,
+ sizeof(buf)),
+ nh->ifname);
break;
}
- break;
- case STATIC_IPV4_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET, &si->addr.ipv4, buf,
- sizeof(buf)),
- si->ifname);
- break;
- case STATIC_IPV6_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET6, &si->addr.ipv6, buf,
- sizeof(buf)),
- si->ifname);
- break;
- }
- if (si->tag)
- vty_out(vty, " tag %" ROUTE_TAG_PRI, si->tag);
-
- if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT)
- vty_out(vty, " %d", si->distance);
-
- /* Label information */
- if (si->snh_label.num_labels)
- vty_out(vty, " label %s",
- mpls_label2str(si->snh_label.num_labels,
- si->snh_label.label, buf,
- sizeof(buf), 0));
-
- if (si->nh_vrf_id != si->vrf_id)
- vty_out(vty, " nexthop-vrf %s", si->nh_vrfname);
-
- /*
- * table ID from VRF overrides configured
- */
- if (si->table_id &&
- svrf->vrf->data.l.table_id == RT_TABLE_MAIN)
- vty_out(vty, " table %u", si->table_id);
-
- if (si->onlink)
- vty_out(vty, " onlink");
-
- vty_out(vty, "\n");
-
- write = 1;
+ if (pn->tag)
+ vty_out(vty, " tag %" ROUTE_TAG_PRI,
+ pn->tag);
+
+ if (pn->distance
+ != ZEBRA_STATIC_DISTANCE_DEFAULT)
+ vty_out(vty, " %u", pn->distance);
+
+ /* Label information */
+ if (nh->snh_label.num_labels)
+ vty_out(vty, " label %s",
+ mpls_label2str(
+ nh->snh_label
+ .num_labels,
+ nh->snh_label.label,
+ buf, sizeof(buf), 0));
+
+ if (nh->nh_vrf_id != GET_STABLE_VRF_ID(info))
+ vty_out(vty, " nexthop-vrf %s",
+ nh->nh_vrfname);
+
+ /*
+ * table ID from VRF overrides
+ * configured
+ */
+ if (pn->table_id
+ && svrf->vrf->data.l.table_id
+ == RT_TABLE_MAIN)
+ vty_out(vty, " table %u", pn->table_id);
+
+ if (nh->onlink)
+ vty_out(vty, " onlink");
+
+ /*
+ * SR-TE color
+ */
+ if (nh->color != 0)
+ vty_out(vty, " color %u", nh->color);
+
+ vty_out(vty, "\n");
+
+ write = 1;
+ }
}
+ }
return write;
}
/* Static unicast routes for multicast RPF lookup. */
-DEFPY (ip_mroute_dist,
+DEFPY_YANG (ip_mroute_dist,
ip_mroute_dist_cmd,
"[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [(1-255)$distance]",
NO_STR
@@ -752,7 +485,7 @@ DEFPY (ip_mroute_dist,
}
/* Static route configuration. */
-DEFPY(ip_route_blackhole,
+DEFPY_YANG(ip_route_blackhole,
ip_route_blackhole_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -790,7 +523,7 @@ DEFPY(ip_route_blackhole,
distance_str, vrf, label, table_str);
}
-DEFPY(ip_route_blackhole_vrf,
+DEFPY_YANG(ip_route_blackhole_vrf,
ip_route_blackhole_vrf_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -815,28 +548,29 @@ DEFPY(ip_route_blackhole_vrf,
"Table to configure\n"
"The table number to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
- struct static_vrf *svrf = vrf->info;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
-
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
/*
* Coverity is complaining that prefix could
* be dereferenced, but we know that prefix will
* valid. Add an assert to make it happy
*/
assert(prefix);
- return static_route_leak(vty, svrf, svrf, AFI_IP, SAFI_UNICAST, no,
- prefix, mask_str, NULL, NULL, NULL, flag,
+ return static_route_leak(vty, vrfname, vrfname, AFI_IP, SAFI_UNICAST,
+ no, prefix, mask_str, NULL, NULL, NULL, flag,
tag_str, distance_str, label, table_str,
- false);
+ false, NULL);
}
-DEFPY(ip_route_address_interface,
+DEFPY_YANG(ip_route_address_interface,
ip_route_address_interface_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -850,6 +584,7 @@ DEFPY(ip_route_address_interface,
|table (1-4294967295) \
|nexthop-vrf NAME \
|onlink$onlink \
+ |color (1-4294967295) \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -867,46 +602,32 @@ DEFPY(ip_route_address_interface,
"Table to configure\n"
"The table number to configure\n"
VRF_CMD_HELP_STR
- "Treat the nexthop as directly attached to the interface\n")
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- struct static_vrf *svrf;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
- svrf = static_vty_get_unknown_vrf(vty, vrf);
- if (!svrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (table_str && vrf && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
+ nh_vrf = vrf;
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no,
+ return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no,
prefix, mask_str, NULL, gate_str, ifname, flag,
tag_str, distance_str, label, table_str,
- !!onlink);
+ !!onlink, color_str);
}
-DEFPY(ip_route_address_interface_vrf,
+DEFPY_YANG(ip_route_address_interface_vrf,
ip_route_address_interface_vrf_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -919,7 +640,8 @@ DEFPY(ip_route_address_interface_vrf,
|table (1-4294967295) \
|nexthop-vrf NAME \
|onlink$onlink \
- }]",
+ |color (1-4294967295) \
+ }]",
NO_STR IP_STR
"Establish static routes\n"
"IP destination prefix (e.g. 10.0.0.0/8)\n"
@@ -935,41 +657,39 @@ DEFPY(ip_route_address_interface_vrf,
"Table to configure\n"
"The table number to configure\n"
VRF_CMD_HELP_STR
- "Treat the nexthop as directly attached to the interface\n")
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
+ const char *nh_vrf;
const char *flag = NULL;
- struct static_vrf *svrf = vrf->info;
- struct static_vrf *nh_svrf;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrfname;
- return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no,
+ return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no,
prefix, mask_str, NULL, gate_str, ifname, flag,
tag_str, distance_str, label, table_str,
- !!onlink);
+ !!onlink, color_str);
}
-DEFPY(ip_route,
+DEFPY_YANG(ip_route,
ip_route_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -981,6 +701,7 @@ DEFPY(ip_route,
|label WORD \
|table (1-4294967295) \
|nexthop-vrf NAME \
+ |color (1-4294967295) \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -997,46 +718,33 @@ DEFPY(ip_route,
MPLS_LABEL_HELPSTR
"Table to configure\n"
"The table number to configure\n"
- VRF_CMD_HELP_STR)
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- struct static_vrf *svrf;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
- if (table_str && vrf && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
- svrf = static_vty_get_unknown_vrf(vty, vrf);
- if (!svrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrf;
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
- NULL, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, false);
+ return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no,
+ prefix, mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label, table_str,
+ false, color_str);
}
-DEFPY(ip_route_vrf,
+DEFPY_YANG(ip_route_vrf,
ip_route_vrf_cmd,
"[no] ip route\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
@@ -1047,6 +755,7 @@ DEFPY(ip_route_vrf,
|label WORD \
|table (1-4294967295) \
|nexthop-vrf NAME \
+ |color (1-4294967295) \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -1062,41 +771,40 @@ DEFPY(ip_route_vrf,
MPLS_LABEL_HELPSTR
"Table to configure\n"
"The table number to configure\n"
- VRF_CMD_HELP_STR)
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
- struct static_vrf *svrf = vrf->info;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
+
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrfname;
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
- NULL, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, false);
+ return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no,
+ prefix, mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label, table_str,
+ false, color_str);
}
-DEFPY(ipv6_route_blackhole,
+DEFPY_YANG(ipv6_route_blackhole,
ipv6_route_blackhole_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<reject|blackhole>$flag \
@@ -1134,7 +842,7 @@ DEFPY(ipv6_route_blackhole,
distance_str, vrf, label, table_str);
}
-DEFPY(ipv6_route_blackhole_vrf,
+DEFPY_YANG(ipv6_route_blackhole_vrf,
ipv6_route_blackhole_vrf_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<reject|blackhole>$flag \
@@ -1159,14 +867,16 @@ DEFPY(ipv6_route_blackhole_vrf,
"Table to configure\n"
"The table number to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
- struct static_vrf *svrf = vrf->info;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
/*
* Coverity is complaining that prefix could
@@ -1174,13 +884,14 @@ DEFPY(ipv6_route_blackhole_vrf,
* valid. Add an assert to make it happy
*/
assert(prefix);
- return static_route_leak(
- vty, svrf, svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
- from_str, NULL, NULL, flag, tag_str, distance_str, label,
- table_str, false);
+
+ return static_route_leak(vty, vrfname, vrfname, AFI_IP6, SAFI_UNICAST,
+ no, prefix_str, NULL, from_str, NULL, NULL,
+ flag, tag_str, distance_str, label, table_str,
+ false, NULL);
}
-DEFPY(ipv6_route_address_interface,
+DEFPY_YANG(ipv6_route_address_interface,
ipv6_route_address_interface_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
X:X::X:X$gate \
@@ -1193,6 +904,7 @@ DEFPY(ipv6_route_address_interface,
|table (1-4294967295) \
|nexthop-vrf NAME \
|onlink$onlink \
+ |color (1-4294967295) \
}]",
NO_STR
IPV6_STR
@@ -1211,46 +923,33 @@ DEFPY(ipv6_route_address_interface,
"Table to configure\n"
"The table number to configure\n"
VRF_CMD_HELP_STR
- "Treat the nexthop as directly attached to the interface\n")
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- struct static_vrf *svrf;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
- if (table_str && vrf && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
- return CMD_WARNING_CONFIG_FAILED;
+ if (ifname && !strncasecmp(ifname, "Null0", 5)) {
+ flag = "Null0";
+ ifname = NULL;
}
- svrf = static_vty_get_unknown_vrf(vty, vrf);
- if (!svrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrf;
- if (ifname && !strncasecmp(ifname, "Null0", 5)) {
- flag = "Null0";
- ifname = NULL;
- }
-
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
- from_str, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, !!onlink);
+ return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no,
+ prefix_str, NULL, from_str, gate_str, ifname,
+ flag, tag_str, distance_str, label, table_str,
+ !!onlink, color_str);
}
-DEFPY(ipv6_route_address_interface_vrf,
+DEFPY_YANG(ipv6_route_address_interface_vrf,
ipv6_route_address_interface_vrf_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
X:X::X:X$gate \
@@ -1262,6 +961,7 @@ DEFPY(ipv6_route_address_interface_vrf,
|table (1-4294967295) \
|nexthop-vrf NAME \
|onlink$onlink \
+ |color (1-4294967295) \
}]",
NO_STR
IPV6_STR
@@ -1279,41 +979,39 @@ DEFPY(ipv6_route_address_interface_vrf,
"Table to configure\n"
"The table number to configure\n"
VRF_CMD_HELP_STR
- "Treat the nexthop as directly attached to the interface\n")
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
- struct static_vrf *svrf = vrf->info;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrfname;
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
- from_str, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, !!onlink);
+ return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST,
+ no, prefix_str, NULL, from_str, gate_str,
+ ifname, flag, tag_str, distance_str, label,
+ table_str, !!onlink, color_str);
}
-DEFPY(ipv6_route,
+DEFPY_YANG(ipv6_route,
ipv6_route_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
@@ -1324,6 +1022,7 @@ DEFPY(ipv6_route,
|label WORD \
|table (1-4294967295) \
|nexthop-vrf NAME \
+ |color (1-4294967295) \
}]",
NO_STR
IPV6_STR
@@ -1341,46 +1040,32 @@ DEFPY(ipv6_route,
MPLS_LABEL_HELPSTR
"Table to configure\n"
"The table number to configure\n"
- VRF_CMD_HELP_STR)
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- struct static_vrf *svrf;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
- if (table_str && vrf && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- svrf = static_vty_get_unknown_vrf(vty, vrf);
- if (!svrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (!vrf)
+ vrf = VRF_DEFAULT_NAME;
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrf;
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
- from_str, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, false);
+ return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no,
+ prefix_str, NULL, from_str, gate_str, ifname,
+ flag, tag_str, distance_str, label, table_str,
+ false, color_str);
}
-DEFPY(ipv6_route_vrf,
+DEFPY_YANG(ipv6_route_vrf,
ipv6_route_vrf_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
<X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
@@ -1390,6 +1075,7 @@ DEFPY(ipv6_route_vrf,
|label WORD \
|table (1-4294967295) \
|nexthop-vrf NAME \
+ |color (1-4294967295) \
}]",
NO_STR
IPV6_STR
@@ -1406,40 +1092,38 @@ DEFPY(ipv6_route_vrf,
MPLS_LABEL_HELPSTR
"Table to configure\n"
"The table number to configure\n"
- VRF_CMD_HELP_STR)
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n")
{
- VTY_DECLVAR_CONTEXT(vrf, vrf);
- struct static_vrf *svrf = vrf->info;
- struct static_vrf *nh_svrf;
+ const char *nh_vrf;
const char *flag = NULL;
+ const struct lyd_node *vrf_dnode;
+ const char *vrfname;
- if (table_str && !vrf_is_backend_netns()) {
- vty_out(vty,
- "%% table param only available when running on netns-based vrfs\n");
+ vrf_dnode =
+ yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
+ if (!vrf_dnode) {
+ vty_out(vty, "%% Failed to get vrf dnode in candidate db\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ vrfname = yang_dnode_get_string(vrf_dnode, "./name");
if (nexthop_vrf)
- nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf);
+ nh_vrf = nexthop_vrf;
else
- nh_svrf = svrf;
-
- if (!nh_svrf) {
- vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ nh_vrf = vrfname;
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
-
- return static_route_leak(
- vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
- from_str, gate_str, ifname, flag, tag_str, distance_str, label,
- table_str, false);
+ return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST,
+ no, prefix_str, NULL, from_str, gate_str,
+ ifname, flag, tag_str, distance_str, label,
+ table_str, false, color_str);
}
-DEFPY(debug_staticd,
+DEFPY_YANG(debug_staticd,
debug_staticd_cmd,
"[no] debug static [{events$events}]",
NO_STR
@@ -1500,8 +1184,4 @@ void static_vty_init(void)
install_element(VIEW_NODE, &show_debugging_static_cmd);
install_element(VIEW_NODE, &debug_staticd_cmd);
install_element(CONFIG_NODE, &debug_staticd_cmd);
-
- static_list = list_new();
- static_list->cmp = (int (*)(void *, void *))static_list_compare;
- static_list->del = (void (*)(void *))static_list_delete;
}
diff --git a/staticd/static_vty.h b/staticd/static_vty.h
index 2f65c08b8b..7ffc8d9c98 100644
--- a/staticd/static_vty.h
+++ b/staticd/static_vty.h
@@ -19,8 +19,6 @@
#ifndef __STATIC_VTY_H__
#define __STATIC_VTY_H__
-void static_config_install_delayed_routes(struct static_vrf *svrf);
-
int static_config(struct vty *vty, struct static_vrf *svrf,
afi_t afi, safi_t safi, const char *cmd);
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
index c42f632ffb..1bdbb69d00 100644
--- a/staticd/static_zebra.c
+++ b/staticd/static_zebra.c
@@ -89,7 +89,6 @@ static int static_ifp_up(struct interface *ifp)
struct static_vrf *svrf = static_vrf_lookup_by_id(ifp->vrf_id);
static_fixup_vrf_ids(svrf);
- static_config_install_delayed_routes(svrf);
}
/* Install any static reliant on this interface coming up */
@@ -265,8 +264,8 @@ static void static_nht_hash_free(void *data)
XFREE(MTYPE_TMP, nhtd);
}
-void static_zebra_nht_register(struct route_node *rn,
- struct static_route *si, bool reg)
+void static_zebra_nht_register(struct route_node *rn, struct static_nexthop *nh,
+ bool reg)
{
struct static_nht_data *nhtd, lookup;
uint32_t cmd;
@@ -276,14 +275,14 @@ void static_zebra_nht_register(struct route_node *rn,
cmd = (reg) ?
ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER;
- if (si->nh_registered && reg)
+ if (nh->nh_registered && reg)
return;
- if (!si->nh_registered && !reg)
+ if (!nh->nh_registered && !reg)
return;
memset(&p, 0, sizeof(p));
- switch (si->type) {
+ switch (nh->type) {
case STATIC_IFNAME:
case STATIC_BLACKHOLE:
return;
@@ -291,23 +290,23 @@ void static_zebra_nht_register(struct route_node *rn,
case STATIC_IPV4_GATEWAY_IFNAME:
p.family = AF_INET;
p.prefixlen = IPV4_MAX_BITLEN;
- p.u.prefix4 = si->addr.ipv4;
+ p.u.prefix4 = nh->addr.ipv4;
afi = AFI_IP;
break;
case STATIC_IPV6_GATEWAY:
case STATIC_IPV6_GATEWAY_IFNAME:
p.family = AF_INET6;
p.prefixlen = IPV6_MAX_BITLEN;
- p.u.prefix6 = si->addr.ipv6;
+ p.u.prefix6 = nh->addr.ipv6;
afi = AFI_IP6;
break;
}
memset(&lookup, 0, sizeof(lookup));
lookup.nh = &p;
- lookup.nh_vrf_id = si->nh_vrf_id;
+ lookup.nh_vrf_id = nh->nh_vrf_id;
- si->nh_registered = reg;
+ nh->nh_registered = reg;
if (reg) {
nhtd = hash_get(static_nht_hash, &lookup,
@@ -318,8 +317,8 @@ void static_zebra_nht_register(struct route_node *rn,
zlog_debug("Registered nexthop(%pFX) for %pRN %d", &p,
rn, nhtd->nh_num);
if (nhtd->refcount > 1 && nhtd->nh_num) {
- static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num,
- afi, si->nh_vrf_id);
+ static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num, afi,
+ nh->nh_vrf_id);
return;
}
} else {
@@ -335,25 +334,72 @@ void static_zebra_nht_register(struct route_node *rn,
static_nht_hash_free(nhtd);
}
- if (zclient_send_rnh(zclient, cmd, &p, false, si->nh_vrf_id) < 0)
+ if (zclient_send_rnh(zclient, cmd, &p, false, nh->nh_vrf_id) < 0)
zlog_warn("%s: Failure to send nexthop to zebra", __func__);
}
+/*
+ * When nexthop gets updated via configuration then use the
+ * already registered NH and resend the route to zebra
+ */
+int static_zebra_nh_update(struct route_node *rn, struct static_nexthop *nh)
+{
+ struct static_nht_data *nhtd, lookup = {};
+ struct prefix p = {};
+ afi_t afi = AFI_IP;
+
+ if (!nh->nh_registered)
+ return 0;
+
+ switch (nh->type) {
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ return 0;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = nh->addr.ipv4;
+ afi = AFI_IP;
+ break;
+ case STATIC_IPV6_GATEWAY:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = nh->addr.ipv6;
+ afi = AFI_IP6;
+ break;
+ }
+
+ lookup.nh = &p;
+ lookup.nh_vrf_id = nh->nh_vrf_id;
+
+ nhtd = hash_lookup(static_nht_hash, &lookup);
+ if (nhtd && nhtd->nh_num) {
+ nh->state = STATIC_START;
+ static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num, afi,
+ nh->nh_vrf_id);
+ return 1;
+ }
+ return 0;
+}
extern void static_zebra_route_add(struct route_node *rn,
- struct static_route *si_changed,
- vrf_id_t vrf_id, safi_t safi, bool install)
+ struct static_path *pn, safi_t safi,
+ bool install)
{
- struct static_route *si = rn->info;
+ struct static_nexthop *nh;
const struct prefix *p, *src_pp;
struct zapi_nexthop *api_nh;
struct zapi_route api;
uint32_t nh_num = 0;
+ struct stable_info *info;
p = src_pp = NULL;
srcdest_rnode_prefixes(rn, &p, &src_pp);
memset(&api, 0, sizeof(api));
- api.vrf_id = vrf_id;
+ info = static_get_stable_info(rn);
+ api.vrf_id = GET_STABLE_VRF_ID(info);
api.type = ZEBRA_ROUTE_STATIC;
api.safi = safi;
memcpy(&api.prefix, p, sizeof(api.prefix));
@@ -365,71 +411,69 @@ extern void static_zebra_route_add(struct route_node *rn,
SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE);
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
- if (si_changed->distance) {
+ if (pn->distance) {
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
- api.distance = si_changed->distance;
+ api.distance = pn->distance;
}
- if (si_changed->tag) {
+ if (pn->tag) {
SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
- api.tag = si_changed->tag;
+ api.tag = pn->tag;
}
- if (si_changed->table_id != 0) {
+ if (pn->table_id != 0) {
SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
- api.tableid = si_changed->table_id;
+ api.tableid = pn->table_id;
}
- for (/*loaded above*/; si; si = si->next) {
+ frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
api_nh = &api.nexthops[nh_num];
- if (si->nh_vrf_id == VRF_UNKNOWN)
+ if (nh->nh_vrf_id == VRF_UNKNOWN)
continue;
- if (si->distance != si_changed->distance)
- continue;
-
- if (si->table_id != si_changed->table_id)
- continue;
-
- api_nh->vrf_id = si->nh_vrf_id;
- if (si->onlink)
+ api_nh->vrf_id = nh->nh_vrf_id;
+ if (nh->onlink)
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ if (nh->color != 0) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRTE);
+ api_nh->srte_color = nh->color;
+ }
- si->state = STATIC_SENT_TO_ZEBRA;
+ nh->state = STATIC_SENT_TO_ZEBRA;
- switch (si->type) {
+ switch (nh->type) {
case STATIC_IFNAME:
- if (si->ifindex == IFINDEX_INTERNAL)
+ if (nh->ifindex == IFINDEX_INTERNAL)
continue;
- api_nh->ifindex = si->ifindex;
+ api_nh->ifindex = nh->ifindex;
api_nh->type = NEXTHOP_TYPE_IFINDEX;
break;
case STATIC_IPV4_GATEWAY:
- if (!si->nh_valid)
+ if (!nh->nh_valid)
continue;
api_nh->type = NEXTHOP_TYPE_IPV4;
- api_nh->gate = si->addr;
+ api_nh->gate = nh->addr;
break;
case STATIC_IPV4_GATEWAY_IFNAME:
- if (si->ifindex == IFINDEX_INTERNAL)
+ if (nh->ifindex == IFINDEX_INTERNAL)
continue;
- api_nh->ifindex = si->ifindex;
+ api_nh->ifindex = nh->ifindex;
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- api_nh->gate = si->addr;
+ api_nh->gate = nh->addr;
break;
case STATIC_IPV6_GATEWAY:
- if (!si->nh_valid)
+ if (!nh->nh_valid)
continue;
api_nh->type = NEXTHOP_TYPE_IPV6;
- api_nh->gate = si->addr;
+ api_nh->gate = nh->addr;
break;
case STATIC_IPV6_GATEWAY_IFNAME:
- if (si->ifindex == IFINDEX_INTERNAL)
+ if (nh->ifindex == IFINDEX_INTERNAL)
continue;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- api_nh->ifindex = si->ifindex;
- api_nh->gate = si->addr;
+ api_nh->ifindex = nh->ifindex;
+ api_nh->gate = nh->addr;
break;
case STATIC_BLACKHOLE:
api_nh->type = NEXTHOP_TYPE_BLACKHOLE;
- switch (si->bh_type) {
+ switch (nh->bh_type) {
case STATIC_BLACKHOLE_DROP:
case STATIC_BLACKHOLE_NULL:
api_nh->bh_type = BLACKHOLE_NULL;
@@ -440,13 +484,13 @@ extern void static_zebra_route_add(struct route_node *rn,
break;
}
- if (si->snh_label.num_labels) {
+ if (nh->snh_label.num_labels) {
int i;
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
- api_nh->label_num = si->snh_label.num_labels;
+ api_nh->label_num = nh->snh_label.num_labels;
for (i = 0; i < api_nh->label_num; i++)
- api_nh->labels[i] = si->snh_label.label[i];
+ api_nh->labels[i] = nh->snh_label.label[i];
}
nh_num++;
}
diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h
index 962dc3908f..9f93f3ee63 100644
--- a/staticd/static_zebra.h
+++ b/staticd/static_zebra.h
@@ -22,13 +22,15 @@
extern struct thread_master *master;
extern void static_zebra_nht_register(struct route_node *rn,
- struct static_route *si, bool reg);
+ struct static_nexthop *nh, bool reg);
extern void static_zebra_route_add(struct route_node *rn,
- struct static_route *si_changed,
- vrf_id_t vrf_id, safi_t safi, bool install);
+ struct static_path *pn, safi_t safi,
+ bool install);
extern void static_zebra_init(void);
extern void static_zebra_vrf_register(struct vrf *vrf);
extern void static_zebra_vrf_unregister(struct vrf *vrf);
+extern int static_zebra_nh_update(struct route_node *rn,
+ struct static_nexthop *nh);
#endif
diff --git a/staticd/subdir.am b/staticd/subdir.am
index f2b3d11f29..eba7f270bb 100644
--- a/staticd/subdir.am
+++ b/staticd/subdir.am
@@ -18,6 +18,8 @@ staticd_libstatic_a_SOURCES = \
staticd/static_zebra.c \
staticd/static_vrf.c \
staticd/static_vty.c \
+ staticd/static_nb.c \
+ staticd/static_nb_config.c \
# end
noinst_HEADERS += \
@@ -28,6 +30,7 @@ noinst_HEADERS += \
staticd/static_routes.h \
staticd/static_vty.h \
staticd/static_vrf.h \
+ staticd/static_nb.h \
# end
clippy_scan += \
@@ -36,3 +39,7 @@ clippy_scan += \
staticd_staticd_SOURCES = staticd/static_main.c
staticd_staticd_LDADD = staticd/libstatic.a lib/libfrr.la $(LIBCAP)
+
+nodist_staticd_staticd_SOURCES = \
+ yang/frr-staticd.yang.c \
+ # end
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
index b94355e8b8..439891b559 100644
--- a/tests/bgpd/test_aspath.c
+++ b/tests/bgpd/test_aspath.c
@@ -271,57 +271,9 @@ static struct test_segment {
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",
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 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},
},
{
@@ -383,15 +335,13 @@ static struct test_segment {
{
/* 20 */
"reconcile_confed",
- "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)"
- " set(23456,23456,23456), seq(23456,23456,23456)",
+ "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",
+ {"(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},
},
@@ -739,10 +689,8 @@ static struct tests {
/* 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",
+ {"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 */
{
@@ -777,59 +725,9 @@ static struct tests {
{
&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",
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 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,
@@ -861,8 +759,7 @@ struct tests reconcile_tests[] = {
{
&test_segments[20],
&test_segments[19],
- {"(123 456 789) [124,456,788] 6435 59408 21665"
- " {2457,4369,61697} 1842 41591 51793",
+ {"(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},
},
diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c
index f0baa482c7..6a571eaa84 100644
--- a/tests/isisd/test_isis_lspdb.c
+++ b/tests/isisd/test_isis_lspdb.c
@@ -82,6 +82,7 @@ static void test_lsp_build_list_nonzero_ht(void)
int main(int argc, char **argv)
{
+ struct isis *isis = NULL;
isis = calloc(sizeof(*isis), 1);
test_lsp_build_list_nonzero_ht();
return 0;
diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c
index ddb76c8f9d..301078867a 100644
--- a/tests/lib/test_checksum.c
+++ b/tests/lib/test_checksum.c
@@ -489,8 +489,7 @@ int main(int argc, char **argv)
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",
+ 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(uint16_t),
diff --git a/tests/lib/test_ttable.c b/tests/lib/test_ttable.c
index 674179b6ab..43b8adcb97 100644
--- a/tests/lib/test_ttable.c
+++ b/tests/lib/test_ttable.c
@@ -93,8 +93,7 @@ int main(int argc, char **argv)
/* add bigger row */
ttable_add_row(tt, "%s|%s||%s|%s",
- "nebula dusk session streets twilight "
- "pioneer beats yeah",
+ "nebula dusk session streets twilight pioneer beats yeah",
"prarie dog", "cornmeal", ":O -*_-*");
assert(tt->ncols == 5);
assert(tt->nrows == 2);
diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile
index cdd0ae2f6e..b7c6298228 100644
--- a/tests/topotests/Dockerfile
+++ b/tests/topotests/Dockerfile
@@ -19,6 +19,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \
libjson-c-dev \
libpcre3-dev \
libpython-dev \
+ libpython3-dev \
libreadline-dev \
libc-ares-dev \
libcap-dev \
@@ -26,7 +27,10 @@ RUN export DEBIAN_FRONTEND=noninteractive \
mininet \
pkg-config \
python-pip \
- python-sphinx \
+ python3 \
+ python3-dev \
+ python3-sphinx \
+ python3-pytest \
rsync \
strace \
tcpdump \
diff --git a/tests/topotests/all-protocol-startup/r1/show_route_map.ref b/tests/topotests/all-protocol-startup/r1/show_route_map.ref
index 25786081d1..612d0a729d 100644
--- a/tests/topotests/all-protocol-startup/r1/show_route_map.ref
+++ b/tests/topotests/all-protocol-startup/r1/show_route_map.ref
@@ -1,5 +1,5 @@
ZEBRA:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -8,7 +8,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
RIP:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -17,7 +17,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
RIPNG:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -26,7 +26,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
OSPF:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -35,7 +35,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
OSPF6:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -44,7 +44,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Action:
Exit routemap
BGP:
-route-map: LIES Invoked: 0 Optimization: enabled
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
deny, sequence 10 Invoked 0
Match clauses:
interface notpresent
@@ -52,7 +52,7 @@ route-map: LIES Invoked: 0 Optimization: enabled
Call clause:
Action:
Exit routemap
-route-map: bgp-map Invoked: 0 Optimization: enabled
+route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false
permit, sequence 10 Invoked 0
Match clauses:
Set clauses:
@@ -70,4 +70,3 @@ route-map: bgp-map Invoked: 0 Optimization: enabled
Action:
Exit routemap
ISIS:
-SHARP:
diff --git a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
index 4793155939..57f9cd9e3d 100644
--- a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf
@@ -8,10 +8,12 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
peer 10.0.2.2 interface eth-rt3
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
index a49cd4fa6b..6b34e337d3 100644
--- a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf
@@ -8,5 +8,6 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
index 600054a0bc..22937fe46f 100644
--- a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
+++ b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf
@@ -8,5 +8,6 @@ bfd
detect-multiplier 3
receive-interval 300
transmit-interval 300
+ no shutdown
!
!
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
index abca1ed131..d2d0c601c3 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
@@ -25,14 +25,14 @@
"local": "*",
"multihop": false,
"peer": "*",
- "receive-interval": 300,
+ "receive-interval": 250,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
"remote-receive-interval": 300,
"remote-transmit-interval": 300,
"status": "up",
- "transmit-interval": 300,
+ "transmit-interval": 250,
"uptime": "*",
"vrf": "default"
}
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
index 74dae5a60d..08eb0468d6 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
@@ -3,9 +3,8 @@ debug bfd network
debug bfd zebra
!
bfd
- ! profile is commented out on purpose.
- !profile fasttx
- ! receive-interval 250
- ! transmit-interval 250
- !!
+ profile fasttx
+ receive-interval 250
+ transmit-interval 250
+ !
!
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
index 9c56a349ed..c7b75d2fde 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
@@ -1,7 +1,7 @@
router bgp 100
bgp router-id 10.254.254.3
neighbor 172.16.1.2 remote-as 100
- neighbor 172.16.1.2 bfd profile fasttx
+ neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
index 5d774a356b..d27a783adf 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
@@ -8,6 +8,7 @@ interface r3-eth1
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile fasttx
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
index c8bc4c20e9..2c2e136abf 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
@@ -11,8 +11,8 @@
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
- "remote-receive-interval": 300,
- "remote-transmit-interval": 300,
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
"status": "up",
"transmit-interval": 300,
"uptime": "*",
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
index 7c4b39b020..aff1016dee 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
@@ -5,7 +5,7 @@ router bgp 200
no bgp ebgp-requires-policy
neighbor 2001:db8:1::2 remote-as 100
neighbor 2001:db8:1::2 ebgp-multihop 2
- neighbor 2001:db8:1::2 bfd profile fasttx
+ neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
index 477740087d..01e197bed5 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
@@ -8,6 +8,7 @@ interface r4-eth0
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile DOES_NOT_EXIST
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
index 02385b32e5..02385b32e5 100644..100755
--- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
+++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
diff --git a/tests/topotests/bfd-topo3/__init__.py b/tests/topotests/bfd-topo3/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bfd-topo3/__init__.py
diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json
new file mode 100644
index 0000000000..56205d538b
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "2001:db8:1::1",
+ "minimum-ttl": 253,
+ "multihop": true,
+ "passive-mode": true,
+ "peer": "2001:db8:3::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r1-eth0",
+ "local": "2001:db8:1::1",
+ "multihop": false,
+ "passive-mode": true,
+ "peer": "2001:db8:1::2",
+ "receive-interval": 600,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "transmit-interval": 600,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "192.168.1.1",
+ "minimum-ttl": 254,
+ "multihop": true,
+ "passive-mode": true,
+ "peer": "192.168.2.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r1/bfdd.conf b/tests/topotests/bfd-topo3/r1/bfdd.conf
new file mode 100644
index 0000000000..8e40b76d41
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bfdd.conf
@@ -0,0 +1,17 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile fast-tx
+ receive-interval 600
+ transmit-interval 600
+ passive-mode
+ !
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ passive-mode
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf
new file mode 100644
index 0000000000..a0281d50c6
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/bgpd.conf
@@ -0,0 +1,20 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::2 remote-as internal
+ neighbor 2001:db8:1::2 bfd profile fast-tx
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 ebgp-multihop 2
+ neighbor 192.168.2.1 bfd profile slow-tx
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 ebgp-multihop 3
+ neighbor 2001:db8:3::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::2 activate
+ neighbor 192.168.2.1 activate
+ neighbor 2001:db8:3::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r1/zebra.conf b/tests/topotests/bfd-topo3/r1/zebra.conf
new file mode 100644
index 0000000000..64aee48436
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r1/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json
new file mode 100644
index 0000000000..cb8985b13e
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r2-eth0",
+ "local": "2001:db8:1::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:1::1",
+ "receive-interval": 600,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "transmit-interval": 600,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r2-eth1",
+ "local": "2001:db8:2::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:2::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r2/bfdd.conf b/tests/topotests/bfd-topo3/r2/bfdd.conf
new file mode 100644
index 0000000000..2a92e463e0
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bfdd.conf
@@ -0,0 +1,15 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile fast-tx
+ receive-interval 600
+ transmit-interval 600
+ !
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf
new file mode 100644
index 0000000000..0e96033023
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::1 remote-as internal
+ neighbor 2001:db8:1::1 bfd profile fast-tx
+ neighbor 2001:db8:2::1 remote-as external
+ neighbor 2001:db8:2::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::1 activate
+ neighbor 2001:db8:2::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r2/zebra.conf b/tests/topotests/bfd-topo3/r2/zebra.conf
new file mode 100644
index 0000000000..c7e22d4804
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r2/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+ ipv6 address 2001:db8:2::2/64
+!
diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json
new file mode 100644
index 0000000000..8be35fd084
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r3-eth1",
+ "local": "2001:db8:3::2",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:3::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r3-eth0",
+ "local": "2001:db8:2::1",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:2::2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "192.168.2.1",
+ "minimum-ttl": 254,
+ "multihop": true,
+ "passive-mode": false,
+ "peer": "192.168.1.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r3/bfdd.conf b/tests/topotests/bfd-topo3/r3/bfdd.conf
new file mode 100644
index 0000000000..f7972c6ce5
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bfdd.conf
@@ -0,0 +1,11 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf
new file mode 100644
index 0000000000..e14d2011a0
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 300
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 ebgp-multihop 2
+ neighbor 192.168.1.1 bfd profile slow-tx
+ neighbor 2001:db8:2::2 remote-as external
+ neighbor 2001:db8:2::2 bfd profile slow-tx
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 bfd profile slow-tx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 192.168.1.1 activate
+ neighbor 2001:db8:2::2 activate
+ neighbor 2001:db8:3::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r3/zebra.conf b/tests/topotests/bfd-topo3/r3/zebra.conf
new file mode 100644
index 0000000000..14248fb6f7
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r3/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:db8:2::1/64
+!
+interface r3-eth1
+ ip address 192.168.3.2/24
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json
new file mode 100644
index 0000000000..e2e6722ef4
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "local": "2001:db8:3::1",
+ "minimum-ttl": 253,
+ "multihop": true,
+ "passive-mode": false,
+ "peer": "2001:db8:1::1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-interval": 0,
+ "id": "*",
+ "interface": "r4-eth0",
+ "local": "2001:db8:3::1",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "2001:db8:3::2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd-topo3/r4/bfdd.conf b/tests/topotests/bfd-topo3/r4/bfdd.conf
new file mode 100644
index 0000000000..f44abc0b8a
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bfdd.conf
@@ -0,0 +1,16 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ !
+ profile slow-tx-mh
+ receive-interval 2000
+ transmit-interval 2000
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf
new file mode 100644
index 0000000000..3e81008d5d
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/bgpd.conf
@@ -0,0 +1,16 @@
+router bgp 400
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:3::2 remote-as external
+ neighbor 2001:db8:3::2 bfd profile slow-tx
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 ebgp-multihop 3
+ neighbor 2001:db8:1::1 bfd profile slow-tx-mh
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::1 activate
+ neighbor 2001:db8:3::2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd-topo3/r4/zebra.conf b/tests/topotests/bfd-topo3/r4/zebra.conf
new file mode 100644
index 0000000000..bf0cfcf42c
--- /dev/null
+++ b/tests/topotests/bfd-topo3/r4/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.4/32
+!
+interface r4-eth0
+ ip address 192.168.3.1/24
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.dot b/tests/topotests/bfd-topo3/test_bfd_topo3.dot
new file mode 100644
index 0000000000..502cea11f2
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.dot
@@ -0,0 +1,73 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="bfd-topo3";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n192.168.1.0/24\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.2.0/24\n2001:db8:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n192.168.3.0/24\n2001:db8:3::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0\n.1"];
+ r2 -- sw1 [label="eth0\n.2"];
+
+ r3 -- sw2 [label="eth0\n.1"];
+ r2 -- sw2 [label="eth1\n.2"];
+
+ r4 -- sw3 [label="eth0\n.1"];
+ r3 -- sw3 [label="eth2\n.2"];
+}
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.jpg b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg
new file mode 100644
index 0000000000..6b532560bf
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg
Binary files differ
diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py
new file mode 100644
index 0000000000..bcee338a92
--- /dev/null
+++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_topo3.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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 NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
+#
+
+"""
+test_bfd_topo3.py: Test the FRR BFD daemon multi hop.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class BFDTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(BFDTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ daemon_file = "{}/{}/bfdd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BFD, daemon_file)
+
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BGP, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def test_wait_bgp_convergence():
+ "Wait for BGP to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_loopback_route(router, iptype, route, proto):
+ "Wait until route is present on RIB for protocol."
+ logger.info('waiting route {} in {}'.format(route, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ 'show {} route json'.format(iptype),
+ { route: [{ 'protocol': proto }] }
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" OSPF convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for R1 <-> R2 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R1 <-> R3 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.3/32', 'bgp')
+ # Wait for R1 <-> R4 convergence.
+ expect_loopback_route('r1', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R2 <-> R1 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R2 <-> R3 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp')
+ # Wait for R2 <-> R4 convergence.
+ expect_loopback_route('r2', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R3 <-> R1 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R3 <-> R2 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R3 <-> R4 convergence.
+ expect_loopback_route('r3', 'ip', '10.254.254.4/32', 'bgp')
+
+ # Wait for R4 <-> R1 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.1/32', 'bgp')
+ # Wait for R4 <-> R2 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.2/32', 'bgp')
+ # Wait for R4 <-> R3 convergence.
+ expect_loopback_route('r4', 'ip', '10.254.254.3/32', 'bgp')
+
+
+def test_wait_bfd_convergence():
+ "Wait for BFD to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test BFD configurations")
+
+ def expect_bfd_configuration(router):
+ "Load JSON file and compare with 'show bfd peer json'"
+ logger.info('waiting BFD configuration on router {}'.format(router))
+ bfd_config = json.loads(open('{}/{}/bfd-peers.json'.format(CWD, router)).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ 'show bfd peers json',
+ bfd_config
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" BFD configuration failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_bfd_configuration('r1')
+ expect_bfd_configuration('r2')
+ expect_bfd_configuration('r3')
+ expect_bfd_configuration('r4')
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf
new file mode 100644
index 0000000000..8858e21496
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf
Binary files differ
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/pim.conf b/tests/topotests/bgp-evpn-mh/hostd11/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/pim.conf b/tests/topotests/bgp-evpn-mh/hostd12/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/pim.conf b/tests/topotests/bgp-evpn-mh/hostd21/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/pim.conf b/tests/topotests/bgp-evpn-mh/hostd22/pim.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/pim.conf
diff --git a/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf
diff --git a/tests/topotests/bgp-evpn-mh/spine1/evpn.conf b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf
new file mode 100644
index 0000000000..2e26f60f44
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf
@@ -0,0 +1,17 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.13
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.3.2 remote-as external
+ neighbor 192.168.4.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.2.2 activate
+ neighbor 192.168.3.2 activate
+ neighbor 192.168.4.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine1/pim.conf b/tests/topotests/bgp-evpn-mh/spine1/pim.conf
new file mode 100644
index 0000000000..68e686e8c7
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/pim.conf
@@ -0,0 +1,18 @@
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int lo
+ ip pim
+!
+int spine1-eth0
+ ip pim
+!
+int spine1-eth1
+ ip pim
+!
+int spine1-eth2
+ ip pim
+!
+int spine1-eth3
+ ip pim
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine1/zebra.conf b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf
new file mode 100644
index 0000000000..80e9e5a263
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf
@@ -0,0 +1,15 @@
+int spine1-eth0
+ ip addr 192.168.1.1/24
+!
+int spine1-eth1
+ ip addr 192.168.2.1/24
+!
+int spine1-eth2
+ ip addr 192.168.3.1/24
+!
+int spine1-eth3
+ ip addr 192.168.4.1/24
+!
+int lo
+ ip addr 192.168.100.13/32
+ ip addr 192.168.100.100/32
diff --git a/tests/topotests/bgp-evpn-mh/spine2/evpn.conf b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf
new file mode 100644
index 0000000000..ec2e789276
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf
@@ -0,0 +1,17 @@
+frr defaults datacenter
+!
+router bgp 65001
+ bgp router-id 192.168.100.14
+ no bgp ebgp-requires-policy
+ neighbor 192.168.5.2 remote-as external
+ neighbor 192.168.6.2 remote-as external
+ neighbor 192.168.7.2 remote-as external
+ neighbor 192.168.8.2 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.5.2 activate
+ neighbor 192.168.6.2 activate
+ neighbor 192.168.7.2 activate
+ neighbor 192.168.8.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine2/pim.conf b/tests/topotests/bgp-evpn-mh/spine2/pim.conf
new file mode 100644
index 0000000000..c1566240e6
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/pim.conf
@@ -0,0 +1,18 @@
+ip pim rp 192.168.100.13
+ip pim spt-switchover infinity-and-beyond
+!
+int lo
+ ip pim
+!
+int spine2-eth0
+ ip pim
+!
+int spine2-eth1
+ ip pim
+!
+int spine2-eth2
+ ip pim
+!
+int spine2-eth3
+ ip pim
+!
diff --git a/tests/topotests/bgp-evpn-mh/spine2/zebra.conf b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf
new file mode 100644
index 0000000000..1cd1df8c81
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf
@@ -0,0 +1,15 @@
+int spine2-eth0
+ ip addr 192.168.5.1/24
+!
+int spine2-eth1
+ ip addr 192.168.6.1/24
+!
+int spine2-eth2
+ ip addr 192.168.7.1/24
+!
+int spine2-eth3
+ ip addr 192.168.8.1/24
+!
+int lo
+ ip addr 192.168.100.14/32
+ ip addr 192.168.100.100/32
diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
new file mode 100755
index 0000000000..fe28f79bd4
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py
@@ -0,0 +1,651 @@
+#!/usr/bin/env python
+
+#
+# test_evpn_mh.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Anuradha Karuppiah
+#
+# 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 NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
+#
+
+"""
+test_evpn_mh.py: Testing EVPN multihoming
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#####################################################
+##
+## Network Topology Definition
+##
+## See topology picture at evpn-mh-topo-tests.pdf
+#####################################################
+
+
+class NetworkTopo(Topo):
+ '''
+ EVPN Multihoming Topology -
+ 1. Two level CLOS
+ 2. Two spine switches - spine1, spine2
+ 3. Two racks with Top-of-Rack switches per rack - tormx1, tormx2
+ 4. Two dual attached hosts per-rack - hostdx1, hostdx2
+ '''
+
+ def build(self, **_opts):
+ "Build function"
+
+ tgen = get_topogen(self)
+
+ tgen.add_router("spine1")
+ tgen.add_router("spine2")
+ tgen.add_router("torm11")
+ tgen.add_router("torm12")
+ tgen.add_router("torm21")
+ tgen.add_router("torm22")
+ tgen.add_router("hostd11")
+ tgen.add_router("hostd12")
+ tgen.add_router("hostd21")
+ tgen.add_router("hostd22")
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+
+
+ ##################### spine1 ########################
+ # spine1-eth0 is connected to torm11-eth0
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # spine1-eth1 is connected to torm12-eth0
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # spine1-eth2 is connected to torm21-eth0
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # spine1-eth3 is connected to torm22-eth0
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["spine1"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### spine2 ########################
+ # spine2-eth0 is connected to torm11-eth1
+ switch = tgen.add_switch("sw5")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm11"])
+
+ # spine2-eth1 is connected to torm12-eth1
+ switch = tgen.add_switch("sw6")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm12"])
+
+ # spine2-eth2 is connected to torm21-eth1
+ switch = tgen.add_switch("sw7")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm21"])
+
+ # spine2-eth3 is connected to torm22-eth1
+ switch = tgen.add_switch("sw8")
+ switch.add_link(tgen.gears["spine2"])
+ switch.add_link(tgen.gears["torm22"])
+
+ ##################### torm11 ########################
+ # torm11-eth2 is connected to hostd11-eth0
+ switch = tgen.add_switch("sw9")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm11-eth3 is connected to hostd12-eth0
+ switch = tgen.add_switch("sw10")
+ switch.add_link(tgen.gears["torm11"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm12 ########################
+ # torm12-eth2 is connected to hostd11-eth1
+ switch = tgen.add_switch("sw11")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd11"])
+
+ # torm12-eth3 is connected to hostd12-eth1
+ switch = tgen.add_switch("sw12")
+ switch.add_link(tgen.gears["torm12"])
+ switch.add_link(tgen.gears["hostd12"])
+
+ ##################### torm21 ########################
+ # torm21-eth2 is connected to hostd21-eth0
+ switch = tgen.add_switch("sw13")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm21-eth3 is connected to hostd22-eth0
+ switch = tgen.add_switch("sw14")
+ switch.add_link(tgen.gears["torm21"])
+ switch.add_link(tgen.gears["hostd22"])
+
+ ##################### torm22 ########################
+ # torm22-eth2 is connected to hostd21-eth1
+ switch = tgen.add_switch("sw15")
+ switch.add_link(tgen.gears["torm22"])
+ switch.add_link(tgen.gears["hostd21"])
+
+ # torm22-eth3 is connected to hostd22-eth1
+ switch = tgen.add_switch("sw16")
+ switch.add_link(tgen.gears["torm22"])
+ switch.add_link(tgen.gears["hostd22"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+tor_ips = {"torm11" : "192.168.100.15", \
+ "torm12" : "192.168.100.16", \
+ "torm21" : "192.168.100.17", \
+ "torm22" : "192.168.100.18"}
+
+svi_ips = {"torm11" : "45.0.0.2", \
+ "torm12" : "45.0.0.3", \
+ "torm21" : "45.0.0.4", \
+ "torm22" : "45.0.0.5"}
+
+tor_ips_rack_1 = {"torm11" : "192.168.100.15", \
+ "torm12" : "192.168.100.16"}
+
+tor_ips_rack_2 = {"torm21" : "192.168.100.17", \
+ "torm22" : "192.168.100.18"}
+
+host_es_map = {"hostd11" : "03:44:38:39:ff:ff:01:00:00:01",
+ "hostd12" : "03:44:38:39:ff:ff:01:00:00:02",
+ "hostd21" : "03:44:38:39:ff:ff:02:00:00:01",
+ "hostd22" : "03:44:38:39:ff:ff:02:00:00:02"}
+
+def config_bond(node, bond_name, bond_members, bond_ad_sys_mac, br):
+ '''
+ Used to setup bonds on the TORs and hosts for MH
+ '''
+ node.run("ip link add dev %s type bond mode 802.3ad" % bond_name)
+ node.run("ip link set dev %s type bond lacp_rate 1" % bond_name)
+ node.run("ip link set dev %s type bond miimon 100" % bond_name)
+ node.run("ip link set dev %s type bond xmit_hash_policy layer3+4" % bond_name)
+ node.run("ip link set dev %s type bond min_links 1" % bond_name)
+ node.run("ip link set dev %s type bond ad_actor_system %s" %\
+ (bond_name, bond_ad_sys_mac))
+
+ for bond_member in bond_members:
+ node.run("ip link set dev %s down" % bond_member)
+ node.run("ip link set dev %s master %s" % (bond_member, bond_name))
+ node.run("ip link set dev %s up" % bond_member)
+
+ node.run("ip link set dev %s up" % bond_name)
+
+ # if bridge is specified add the bond as a bridge member
+ if br:
+ node.run(" ip link set dev %s master bridge" % bond_name)
+ node.run("/sbin/bridge link set dev %s priority 8" % bond_name)
+ node.run("/sbin/bridge vlan del vid 1 dev %s" % bond_name)
+ node.run("/sbin/bridge vlan del vid 1 untagged pvid dev %s" % bond_name)
+ node.run("/sbin/bridge vlan add vid 1000 dev %s" % bond_name)
+ node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev %s"\
+ % bond_name)
+
+
+def config_mcast_tunnel_termination_device(node):
+ '''
+ The kernel requires a device to terminate VxLAN multicast tunnels
+ when EVPN-PIM is used for flooded traffic
+ '''
+ node.run("ip link add dev ipmr-lo type dummy")
+ node.run("ip link set dev ipmr-lo mtu 16000")
+ node.run("ip link set dev ipmr-lo mode dormant")
+ node.run("ip link set dev ipmr-lo up")
+
+
+def config_bridge(node):
+ '''
+ Create a VLAN aware bridge
+ '''
+ node.run("ip link add dev bridge type bridge stp_state 0")
+ node.run("ip link set dev bridge type bridge vlan_filtering 1")
+ node.run("ip link set dev bridge mtu 9216")
+ node.run("ip link set dev bridge type bridge ageing_time 1800")
+ node.run("ip link set dev bridge type bridge mcast_snooping 0")
+ node.run("ip link set dev bridge type bridge vlan_stats_enabled 1")
+ node.run("ip link set dev bridge up")
+ node.run("/sbin/bridge vlan add vid 1000 dev bridge")
+
+
+def config_vxlan(node, node_ip):
+ '''
+ Create a VxLAN device for VNI 1000 and add it to the bridge.
+ VLAN-1000 is mapped to VNI-1000.
+ '''
+ node.run("ip link add dev vx-1000 type vxlan id 1000 dstport 4789")
+ node.run("ip link set dev vx-1000 type vxlan nolearning")
+ node.run("ip link set dev vx-1000 type vxlan local %s" % node_ip)
+ node.run("ip link set dev vx-1000 type vxlan ttl 64")
+ node.run("ip link set dev vx-1000 mtu 9152")
+ node.run("ip link set dev vx-1000 type vxlan dev ipmr-lo group 239.1.1.100")
+ node.run("ip link set dev vx-1000 up")
+
+ # bridge attrs
+ node.run("ip link set dev vx-1000 master bridge")
+ node.run("/sbin/bridge link set dev vx-1000 neigh_suppress on")
+ node.run("/sbin/bridge link set dev vx-1000 learning off")
+ node.run("/sbin/bridge link set dev vx-1000 priority 8")
+ node.run("/sbin/bridge vlan del vid 1 dev vx-1000")
+ node.run("/sbin/bridge vlan del vid 1 untagged pvid dev vx-1000")
+ node.run("/sbin/bridge vlan add vid 1000 dev vx-1000")
+ node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev vx-1000")
+
+
+def config_svi(node, svi_pip):
+ '''
+ Create an SVI for VLAN 1000
+ '''
+ node.run("ip link add link bridge name vlan1000 type vlan id 1000 protocol 802.1q")
+ node.run("ip addr add %s/24 dev vlan1000" % svi_pip)
+ node.run("ip link set dev vlan1000 up")
+ node.run("/sbin/sysctl net.ipv4.conf.vlan1000.arp_accept=1")
+ node.run("ip link add link vlan1000 name vlan1000-v0 type macvlan mode private")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.accept_dad=0")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits")
+ node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits=0")
+ node.run("ip link set dev vlan1000-v0 address 00:00:5e:00:01:01")
+ node.run("ip link set dev vlan1000-v0 up")
+ # metric 1024 is not working
+ node.run("ip addr add 45.0.0.1/24 dev vlan1000-v0")
+
+
+def config_tor(tor_name, tor, tor_ip, svi_pip):
+ '''
+ Create the bond/vxlan-bridge on the TOR which acts as VTEP and EPN-PE
+ '''
+ # create a device for terminating VxLAN multicast tunnels
+ config_mcast_tunnel_termination_device(tor)
+
+ # create a vlan aware bridge
+ config_bridge(tor)
+
+ # create vxlan device and add it to bridge
+ config_vxlan(tor, tor_ip)
+
+ # create hostbonds and add them to the bridge
+ if "torm1" in tor_name:
+ sys_mac = "44:38:39:ff:ff:01"
+ else:
+ sys_mac = "44:38:39:ff:ff:02"
+ bond_member = tor_name + "-eth2"
+ config_bond(tor, "hostbond1", [bond_member], sys_mac, "bridge")
+
+ bond_member = tor_name + "-eth3"
+ config_bond(tor, "hostbond2", [bond_member], sys_mac, "bridge")
+
+ # create SVI
+ config_svi(tor, svi_pip)
+
+
+def config_tors(tgen, tors):
+ for tor_name in tors:
+ tor = tgen.gears[tor_name]
+ config_tor(tor_name, tor, tor_ips.get(tor_name), svi_ips.get(tor_name))
+
+def compute_host_ip_mac(host_name):
+ host_id = host_name.split("hostd")[1]
+ host_ip = "45.0.0."+ host_id + "/24"
+ host_mac = "00:00:00:00:00:" + host_id
+
+ return host_ip, host_mac
+
+def config_host(host_name, host):
+ '''
+ Create the dual-attached bond on host nodes for MH
+ '''
+ bond_members = []
+ bond_members.append(host_name + "-eth0")
+ bond_members.append(host_name + "-eth1")
+ bond_name = "torbond"
+ config_bond(host, bond_name, bond_members, "00:00:00:00:00:00", None)
+
+ host_ip, host_mac = compute_host_ip_mac(host_name)
+ host.run("ip addr add %s dev %s" % (host_ip, bond_name))
+ host.run("ip link set dev %s address %s" % (bond_name, host_mac))
+
+
+def config_hosts(tgen, hosts):
+ for host_name in hosts:
+ host = tgen.gears[host_name]
+ config_host(host_name, host)
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(NetworkTopo, module.__name__)
+ tgen.start_topology()
+
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.19") < 0:
+ tgen.errors = "kernel 4.19 needed for multihoming tests"
+ pytest.skip(tgen.errors)
+
+ tors = []
+ tors.append("torm11")
+ tors.append("torm12")
+ tors.append("torm21")
+ tors.append("torm22")
+ config_tors(tgen, tors)
+
+ hosts = []
+ hosts.append("hostd11")
+ hosts.append("hostd12")
+ hosts.append("hostd21")
+ hosts.append("hostd22")
+ config_hosts(tgen, hosts)
+
+ # tgen.mininet_cli()
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pim.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/evpn.conf".format(rname))
+ )
+ tgen.start_router()
+ # tgen.mininet_cli()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def check_local_es(esi, vtep_ips, dut_name, down_vteps):
+ '''
+ Check if ES peers are setup correctly on local ESs
+ '''
+ peer_ips = []
+ if "torm1" in dut_name:
+ tor_ips_rack = tor_ips_rack_1
+ else:
+ tor_ips_rack = tor_ips_rack_2
+
+ for tor_name, tor_ip in tor_ips_rack.iteritems():
+ if dut_name not in tor_name:
+ peer_ips.append(tor_ip)
+
+ # remove down VTEPs from the peer check list
+ peer_set = set(peer_ips)
+ down_vtep_set = set(down_vteps)
+ peer_set = peer_set - down_vtep_set
+
+ vtep_set = set(vtep_ips)
+ diff = peer_set.symmetric_difference(vtep_set)
+
+ return (esi, diff) if diff else None
+
+
+def check_remote_es(esi, vtep_ips, dut_name, down_vteps):
+ '''
+ Verify list of PEs associated with a remote ES
+ '''
+ remote_ips = []
+
+ if "torm1" in dut_name:
+ tor_ips_rack = tor_ips_rack_2
+ else:
+ tor_ips_rack = tor_ips_rack_1
+
+ for tor_name, tor_ip in tor_ips_rack.iteritems():
+ remote_ips.append(tor_ip)
+
+ # remove down VTEPs from the remote check list
+ remote_set = set(remote_ips)
+ down_vtep_set = set(down_vteps)
+ remote_set = remote_set - down_vtep_set
+
+ vtep_set = set(vtep_ips)
+ diff = remote_set.symmetric_difference(vtep_set)
+
+ return (esi, diff) if diff else None
+
+def check_es(dut):
+ '''
+ Verify list of PEs associated all ESs, local and remote
+ '''
+ bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es json")
+ bgp_es_json = json.loads(bgp_es)
+
+ result = None
+
+ expected_es_set = set([v for k, v in host_es_map.iteritems()])
+ curr_es_set = []
+
+ # check is ES content is correct
+ for es in bgp_es_json:
+ esi = es["esi"]
+ curr_es_set.append(esi)
+ types = es["type"]
+ vtep_ips = []
+ for vtep in es["vteps"]:
+ vtep_ips.append(vtep["vtep_ip"])
+
+ if "local" in types:
+ result = check_local_es(esi, vtep_ips, dut.name, [])
+ else:
+ result = check_remote_es(esi, vtep_ips, dut.name, [])
+
+ if result:
+ return result
+
+ # check if all ESs are present
+ curr_es_set = set(curr_es_set)
+ result = curr_es_set.symmetric_difference(expected_es_set)
+
+ return result if result else None
+
+def check_one_es(dut, esi, down_vteps):
+ '''
+ Verify list of PEs associated all ESs, local and remote
+ '''
+ bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es %s json" % esi)
+ es = json.loads(bgp_es)
+
+ if not es:
+ return "esi %s not found" % esi
+
+ esi = es["esi"]
+ types = es["type"]
+ vtep_ips = []
+ for vtep in es["vteps"]:
+ vtep_ips.append(vtep["vtep_ip"])
+
+ if "local" in types:
+ result = check_local_es(esi, vtep_ips, dut.name, down_vteps)
+ else:
+ result = check_remote_es(esi, vtep_ips, dut.name, down_vteps)
+
+ return result
+
+def test_evpn_es():
+ '''
+ Two ES are setup on each rack. This test checks if -
+ 1. ES peer has been added to the local ES (via Type-1/EAD route)
+ 2. The remote ESs are setup with the right list of PEs (via Type-1)
+ '''
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ dut_name = "torm11"
+ dut = tgen.gears[dut_name]
+ test_fn = partial(check_es, dut)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+
+ assertmsg = '"{}" ES content incorrect'.format(dut_name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+def test_evpn_ead_update():
+ '''
+ Flap a host link one the remote rack and check if the EAD updates
+ are sent/processed for the corresponding ESI
+ '''
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # dut on rack1 and host link flap on rack2
+ dut_name = "torm11"
+ dut = tgen.gears[dut_name]
+
+ remote_tor_name = "torm21"
+ remote_tor = tgen.gears[remote_tor_name]
+
+ host_name = "hostd21"
+ host = tgen.gears[host_name]
+ esi = host_es_map.get(host_name)
+
+ # check if the VTEP list is right to start with
+ down_vteps = []
+ test_fn = partial(check_one_es, dut, esi, down_vteps)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES content incorrect'.format(dut_name)
+ assert result is None, assertmsg
+
+ # down a remote host link and check if the EAD withdraw is rxed
+ # Note: LACP is not working as expected so I am temporarily shutting
+ # down the link on the remote TOR instead of the remote host
+ remote_tor.run("ip link set dev %s-%s down" % (remote_tor_name, "eth2"))
+ down_vteps.append(tor_ips.get(remote_tor_name))
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES incorrect after remote link down'.format(dut_name)
+ assert result is None, assertmsg
+
+ # bring up remote host link and check if the EAD update is rxed
+ down_vteps.remove(tor_ips.get(remote_tor_name))
+ remote_tor.run("ip link set dev %s-%s up" % (remote_tor_name, "eth2"))
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" ES incorrect after remote link flap'.format(dut_name)
+ assert result is None, assertmsg
+
+ # tgen.mininet_cli()
+
+def check_mac(dut, vni, mac, m_type, esi, intf):
+ '''
+ checks if mac is present and if desination matches the one provided
+ '''
+
+ out = dut.vtysh_cmd("show evpn mac vni %d mac %s json" % (vni, mac))
+
+ mac_js = json.loads(out)
+ for mac, info in mac_js.iteritems():
+ tmp_esi = info.get("esi", "")
+ tmp_m_type = info.get("type", "")
+ tmp_intf = info.get("intf", "") if tmp_m_type == "local" else ""
+ if tmp_esi == esi and tmp_m_type == m_type and intf == intf:
+ return None
+
+ return "invalid vni %d mac %s out %s" % (vni, mac, mac_js)
+
+def test_evpn_mac():
+ '''
+ 1. Add a MAC on hostd11 and check if the MAC is synced between
+ torm11 and torm12. And installed as a local MAC.
+ 2. Add a MAC on hostd21 and check if the MAC is installed as a
+ remote MAC on torm11 and torm12
+ '''
+
+ tgen = get_topogen()
+
+ local_host = tgen.gears["hostd11"]
+ remote_host = tgen.gears["hostd21"]
+ tors = []
+ tors.append(tgen.gears["torm11"])
+ tors.append(tgen.gears["torm12"])
+
+ # ping the anycast gw from the local and remote hosts to populate
+ # the mac address on the PEs
+ local_host.run("arping -I torbond -c 1 45.0.0.1")
+ remote_host.run("arping -I torbond -c 1 45.0.0.1")
+
+ vni = 1000
+
+ # check if the rack-1 host MAC is present on all rack-1 PEs
+ # and points to local access port
+ m_type = "local"
+ _, mac = compute_host_ip_mac(local_host.name)
+ esi = host_es_map.get(local_host.name)
+ intf = "hostbond1"
+
+ for tor in tors:
+ test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" local MAC content incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+ # check if the rack-2 host MAC is present on all rack-1 PEs
+ # and points to the remote ES destination
+ m_type = "remote"
+ _, mac = compute_host_ip_mac(remote_host.name)
+ esi = host_es_map.get(remote_host.name)
+ intf = ""
+
+ for tor in tors:
+ test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" remote MAC content incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp-evpn-mh/torm11/evpn.conf b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf
new file mode 100644
index 0000000000..01f4b65704
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65002
+ bgp router-id 192.168.100.15
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.5.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.1 activate
+ neighbor 192.168.5.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm11/pim.conf b/tests/topotests/bgp-evpn-mh/torm11/pim.conf
new file mode 100644
index 0000000000..fbba735873
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm11-eth0
+ ip pim
+!
+interface torm11-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm11/zebra.conf b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf
new file mode 100644
index 0000000000..ee4e87e1c2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm11-eth0
+ ip addr 192.168.1.2/24
+!
+int torm11-eth1
+ ip addr 192.168.5.2/24
+!
+int lo
+ ip addr 192.168.100.15/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm12/evpn.conf b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf
new file mode 100644
index 0000000000..2c13024bbc
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65003
+ bgp router-id 192.168.100.16
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.6.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.2.1 activate
+ neighbor 192.168.6.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm12/pim.conf b/tests/topotests/bgp-evpn-mh/torm12/pim.conf
new file mode 100644
index 0000000000..3dd63b44ca
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm12-eth0
+ ip pim
+!
+interface torm12-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm12/zebra.conf b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf
new file mode 100644
index 0000000000..736af4159e
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm12-eth0
+ ip addr 192.168.2.2/24
+!
+int torm12-eth1
+ ip addr 192.168.6.2/24
+!
+int lo
+ ip addr 192.168.100.16/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:01
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm21/evpn.conf b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf
new file mode 100644
index 0000000000..2a2ba061c6
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65004
+ bgp router-id 192.168.100.17
+ no bgp ebgp-requires-policy
+ neighbor 192.168.3.1 remote-as external
+ neighbor 192.168.7.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.3.1 activate
+ neighbor 192.168.7.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm21/pim.conf b/tests/topotests/bgp-evpn-mh/torm21/pim.conf
new file mode 100644
index 0000000000..71aa91a06d
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm21-eth0
+ ip pim
+!
+interface torm21-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm21/zebra.conf b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf
new file mode 100644
index 0000000000..0ebe6f2d95
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm21-eth0
+ ip addr 192.168.3.2/24
+!
+int torm21-eth1
+ ip addr 192.168.7.2/24
+!
+int lo
+ ip addr 192.168.100.17/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm22/evpn.conf b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf
new file mode 100644
index 0000000000..b4f4f1dc25
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf
@@ -0,0 +1,21 @@
+!
+frr defaults datacenter
+!
+debug bgp evpn mh es
+debug bgp evpn mh route
+debug bgp zebra
+!
+!
+router bgp 65005
+ bgp router-id 192.168.100.18
+ no bgp ebgp-requires-policy
+ neighbor 192.168.4.1 remote-as external
+ neighbor 192.168.8.1 remote-as external
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.4.1 activate
+ neighbor 192.168.8.1 activate
+ advertise-all-vni
+ advertise-svi-ip
+ exit-address-family
+!
diff --git a/tests/topotests/bgp-evpn-mh/torm22/pim.conf b/tests/topotests/bgp-evpn-mh/torm22/pim.conf
new file mode 100644
index 0000000000..46f330f5cd
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/pim.conf
@@ -0,0 +1,13 @@
+!
+ip pim rp 192.168.100.13 239.1.1.0/24
+ip pim spt-switchover infinity-and-beyond
+!
+interface lo
+ ip igmp
+ ip pim
+!
+interface torm22-eth0
+ ip pim
+!
+interface torm22-eth1
+ ip pim
diff --git a/tests/topotests/bgp-evpn-mh/torm22/zebra.conf b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf
new file mode 100644
index 0000000000..356d8a43e7
--- /dev/null
+++ b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf
@@ -0,0 +1,23 @@
+debug zebra evpn mh es
+debug zebra evpn mh mac
+debug zebra evpn mh neigh
+debug zebra evpn mh nh
+debug zebra vxlan
+!
+int torm22-eth0
+ ip addr 192.168.4.2/24
+!
+int torm22-eth1
+ ip addr 192.168.8.2/24
+!
+int lo
+ ip addr 192.168.100.18/32
+!
+interface hostbond1
+ evpn mh es-id 1
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
+interface hostbond2
+ evpn mh es-id 2
+ evpn mh es-sys-mac 44:38:39:ff:ff:02
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
index d9f2182aa0..2937504244 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
@@ -3,12 +3,11 @@
"type":"L2",
"vrf":"default",
"vxlanInterface":"vxlan101",
- "ifindex":5,
"vtepIp":"10.10.10.10",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
"numMacs":5,
- "numArpNd":2,
+ "numArpNd":3,
"numRemoteVteps":[
"10.30.30.30"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
index 938ec7bca9..e2699475c9 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
@@ -3,8 +3,6 @@ log file zebra.log
!
interface lo
ip address 10.10.10.10/32
-interface PE1-eth0
- ip address 10.10.1.1/24
interface PE1-eth1
ip address 10.20.1.1/24
!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
index 13255ab4f2..0853147a00 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
@@ -3,12 +3,11 @@
"type":"L2",
"vrf":"default",
"vxlanInterface":"vxlan101",
- "ifindex":5,
"vtepIp":"10.30.30.30",
"mcastGroup":"0.0.0.0",
"advertiseGatewayMacip":"No",
"numMacs":5,
- "numArpNd":2,
+ "numArpNd":3,
"numRemoteVteps":[
"10.10.10.10"
]
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
index 07b83f6395..9738916ab0 100644
--- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
@@ -3,6 +3,4 @@ interface lo
ip address 10.30.30.30/32
interface PE2-eth0
ip address 10.20.2.3/24
-interface PE2-eth1
- ip address 10.10.1.3/24
!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
index ad72540185..90144f5c66 100755
--- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -29,6 +29,7 @@ import os
import sys
import json
from functools import partial
+from time import sleep
import pytest
# Save the Current Working Directory to find configuration files.
@@ -97,6 +98,7 @@ def setup_module(mod):
# set up PE bridges with the EVPN member interfaces facing the CE hosts
pe1.run("ip link add name br101 type bridge stp_state 0")
+ pe1.run("ip addr add 10.10.1.1/24 dev br101")
pe1.run("ip link set dev br101 up")
pe1.run(
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning"
@@ -106,6 +108,7 @@ def setup_module(mod):
pe1.run("ip link set dev PE1-eth0 master br101")
pe2.run("ip link add name br101 type bridge stp_state 0")
+ pe2.run("ip addr add 10.10.1.3/24 dev br101")
pe2.run("ip link set dev br101 up")
pe2.run(
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning"
@@ -142,6 +145,15 @@ def teardown_module(mod):
tgen.stop_topology()
+def show_vni_json_elide_ifindex(pe, vni, expected):
+ output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True)
+
+ if "ifindex" in output_json:
+ output_json.pop("ifindex")
+
+ return topotest.json_cmp(output_json, expected)
+
+
def test_pe1_converge_evpn():
"Wait for protocol convergence"
@@ -154,9 +166,7 @@ def test_pe1_converge_evpn():
json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name)
expected = json.loads(open(json_file).read())
- test_func = partial(
- topotest.router_json_cmp, pe1, "show evpn vni 101 json", expected
- )
+ test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected)
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
assert result is None, assertmsg
@@ -175,9 +185,7 @@ def test_pe2_converge_evpn():
json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name)
expected = json.loads(open(json_file).read())
- test_func = partial(
- topotest.router_json_cmp, pe2, "show evpn vni 101 json", expected
- )
+ test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected)
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
assertmsg = '"{}" JSON output mismatches'.format(pe2.name)
assert result is None, assertmsg
@@ -194,7 +202,7 @@ def mac_learn_test(host, local):
mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
mac_output_json = json.loads(mac_output)
assertmsg = "Local MAC output does not match interface mac {}".format(mac)
- assert mac_output_json[mac]["type"] == "local"
+ assert mac_output_json[mac]["type"] == "local", assertmsg
def mac_test_local_remote(local, remote):
@@ -275,6 +283,103 @@ def test_local_remote_mac_pe2():
# Memory leak test template
+def ip_learn_test(tgen, host, local, remote, ip_addr):
+ "check the host IP gets learned by the VNI"
+ host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name))
+ int_lines = host_output.splitlines()
+ mac_line = int_lines[7].split(": ")
+ mac = mac_line[1]
+ print(host_output)
+
+ # check we have a local association between the MAC and IP
+ local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
+ print(local_output)
+ local_output_json = json.loads(local_output)
+ mac_type = local_output_json[mac]["type"]
+ assertmsg = "Failed to learn local IP address on host {}".format(host.name)
+ assert local_output_json[mac]["neighbors"] != "none", assertmsg
+ learned_ip = local_output_json[mac]["neighbors"]["active"][0]
+
+ assertmsg = "local learned mac wrong type: {} ".format(mac_type)
+ assert mac_type == "local", assertmsg
+
+ assertmsg = "learned address mismatch with configured address host: {} learned: {}".format(
+ ip_addr, learned_ip
+ )
+ assert ip_addr == learned_ip, assertmsg
+
+ # now lets check the remote
+ count = 0
+ converged = False
+ while count < 30:
+ remote_output = remote.vtysh_cmd(
+ "show evpn mac vni 101 mac {} json".format(mac)
+ )
+ print(remote_output)
+ remote_output_json = json.loads(remote_output)
+ type = remote_output_json[mac]["type"]
+ if not remote_output_json[mac]["neighbors"] == "none":
+ # due to a kernel quirk, learned IPs can be inactive
+ if (
+ remote_output_json[mac]["neighbors"]["active"]
+ or remote_output_json[mac]["neighbors"]["inactive"]
+ ):
+ converged = True
+ break
+ count += 1
+ sleep(1)
+
+ print("tries: {}".format(count))
+ assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac)
+ # some debug for this failure
+ if not converged == True:
+ log_output = remote.run("cat zebra.log")
+ print(log_output)
+
+ assert converged == True, assertmsg
+ if remote_output_json[mac]["neighbors"]["active"]:
+ learned_ip = remote_output_json[mac]["neighbors"]["active"][0]
+ else:
+ learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0]
+ assertmsg = "remote learned mac wrong type: {} ".format(type)
+ assert type == "remote", assertmsg
+
+ assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format(
+ ip_addr, learned_ip
+ )
+ assert ip_addr == learned_ip, assertmsg
+
+
+def test_ip_pe1_learn():
+ "run the IP learn test for PE1"
+
+ tgen = get_topogen()
+ host1 = tgen.gears["host1"]
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ pe2.vtysh_cmd("debug zebra vxlan")
+ pe2.vtysh_cmd("debug zebra kernel")
+ # lets populate that arp cache
+ host1.run("ping -c1 10.10.1.1")
+ ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55")
+ # tgen.mininet_cli()
+
+
+def test_ip_pe2_learn():
+ "run the IP learn test for PE2"
+
+ tgen = get_topogen()
+ host2 = tgen.gears["host2"]
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ pe1.vtysh_cmd("debug zebra vxlan")
+ pe1.vtysh_cmd("debug zebra kernel")
+ # lets populate that arp cache
+ host2.run("ping -c1 10.10.1.3")
+ ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56")
+ # tgen.mininet_cli()
+
+
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
index c1bb7e3d15..2712e54f12 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
index c889a4c596..69305512cb 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
index 36dd97190e..3ad95c3612 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
index 33041262f6..502c4c8b2f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
index 29b9f0da6c..2f7de073c3 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
index e09b505ee4..720d06dbf1 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
index c3309d8c75..b81cd33c4f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
index 54401bfb2f..f18e5b852e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
index f742fede1a..54a0933588 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
index 91311f32c5..5289628480 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
index a9549e8fee..5da53ae1e7 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
index cda6d9429a..e4a6b8e32c 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
index e2a8de7db7..a861469c7a 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
index 7b267a6ee1..480f95954e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debug
diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
index 2f8759f960..06bdc31f8c 100644
--- a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
+++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
@@ -1,5 +1,4 @@
log stdout notifications
-log monitor notifications
log commands
!
router bgp 1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
index b3fe5ff23d..ada354bd62 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
index fbb6a65d61..4932d63d4f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
index d61f776f3d..1a5e41aae6 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
index 626d8227e7..a38afd632f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
index 8c75a39efa..dbeb2c4665 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
index 38f8758cbc..ae1787718c 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/evpn_type5_test_topo1/__init__.py b/tests/topotests/evpn_type5_test_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/__init__.py
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
new file mode 100755
index 0000000000..941593e51f
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -0,0 +1,1047 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+1. In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+2. EVPN CLI output and JSON format validation.
+3. RT verification(auto)
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ create_vrf_cfg,
+ create_route_maps,
+ create_interface_in_kernel,
+ check_router_status,
+ configure_vxlan,
+ configure_brctl,
+ apply_raw_config,
+ verify_vrf_vni,
+ verify_cli_json
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Reading the data from JSON File for topology creation
+# Global variables
+TCPDUMP_FILE = "evpn_log.txt"
+LOGDIR = "/tmp/topotests/"
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+MULTICAST_MAC1 = "01:00:5e:00:52:02"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_verify_overlay_index_p1(request):
+ """
+ In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Following steps are taken care in base config:")
+ step(
+ "Configure BGP neighborship for both address families"
+ "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)"
+ )
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify: Prefixes are received in all VRFs on Edge-1 router.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that EVPN routes, received on DCG-1 and DCG-2 do not "
+ "carry any overlay index and these indexes are set to default "
+ "value=0. "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d1", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_cli_json_available_p1(request):
+ """
+ EVPN CLI output and JSON format validation.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Need to verify below CLIs and associated JSON format " "outputs:")
+
+ input_dict = {
+ "e1": {
+ "cli": [
+ "show evpn vni detail",
+ "show bgp l2vpn evpn all overlay",
+ "show bgp l2vpn evpn vni"
+ ]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_auto_p0(request):
+ """
+ RT verification(auto)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs "
+ "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that Edge-1 receives same prefixes in all 3 VRFs via "
+ "corresponding next-hop in associated VRF sh bgp vrf all"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 4-byte local AS number on Edge-1 and establish EVPN "
+ "neighborship with DCG-1 & DCG-2."
+ )
+
+ topo_local = deepcopy(topo)
+
+ step("Delete BGP config for vrf RED.")
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {}
+ for dut in ["e1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that all overlapping prefixes across different VRFs are "
+ "advertised in EVPN with unique RD value(auto derived)."
+ )
+ step(
+ "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto "
+ "derived RT value."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+ input_routes_2 = {
+ "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]}
+ }
+ input_routes_3 = {
+ "r2": {
+ "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+ step(
+ "Verify if DCG-2(eBGP peer) automatically imports the prefixes "
+ "from EVPN address-family to respective VRFs or not."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Change the VNI number for all 3 VRFs on Edge-1 as:"
+ "RED : 75400, GREEN: 75500, BLUE: 75600"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": 75400},
+ {"name": "BLUE", "vni": 75500},
+ {"name": "GREEN", "vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75400, 75500, 75600]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number.."
+ )
+
+ input_dict = {
+ "e1": {
+ "vrfs": [
+ {"RED": {"vni": 75400}},
+ {"BLUE": {"vni": 75500}},
+ {"GREEN": {"vni": 75600}},
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on DCG-2 that prefixes are not imported from EVPN "
+ "address-family to VRFs as RT values are different on sending("
+ "edge-1) and receiving(DCG-2) end."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Revert back to original VNI number for all 3 VRFs on Edge-1 "
+ "as: RED : 75100, GREEN: 75200, BLUE: 75300"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": 75400},
+ {"name": "BLUE", "no_vni": 75500},
+ {"name": "GREEN", "no_vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75100, 75200, 75300]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Test with smaller VNI numbers (1-75000)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 receives EVPN prefixes along with auto "
+ "derived RT values(based on smaller VNI numbers)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("CLI error for malformed VNI.")
+ input_dict = {
+ "e1": {
+ "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Un-configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
new file mode 100755
index 0000000000..8892d13eff
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -0,0 +1,2117 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+
+1. RD verification (manual/auto).
+2. RT verification(manual)
+3. In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+4. EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+5. Route-map operations for EVPN address family.
+6. BGP attributes for EVPN address-family.
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ create_route_maps,
+ verify_cli_json,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ stop_router,
+ start_router,
+ create_vrf_cfg,
+ check_router_status,
+ apply_raw_config,
+ configure_vxlan,
+ configure_brctl,
+ verify_vrf_vni,
+ create_interface_in_kernel
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_RD_verification_manual_and_auto_p0(request):
+ """
+ RD verification (manual/auto).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step(
+ "Advertise vrf RED's routes in EVPN address family from Edge-1 router"
+ ", without manual configuration of RD."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify on DCG-1 and DCG-2:")
+ step("EVPN route for 10.1.1.1/32 has auto-assigned RD value.")
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 50.50.50.50:50 and "
+ "advertise vrf RED's routes in EVPN address family from "
+ "Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {"l2vpn": {"evpn": {"rd": "50.50.50.50:50"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("EVPN route for vrf RED has RD value as 50.50.50.50:50")
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="50.50.50.50:50"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 100.100.100.100:100 and "
+ "advertise vrf RED's routes in EVPN address family from Edge-1 "
+ "router."
+ )
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for vrf RED is overridden with RD value as " "100.100.100.100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="100.100.100.100:100"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RD for vrf BLUE manually same as vrf RED "
+ "(100.100.100.100:100) and advertise vrf RED and BLUE's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Delete manually configured RD and advertise vrf RED's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"no rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure same RD value for vrf GREEN, as auto generated RD "
+ "value for vrf RED on Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete auto configured RD value from vrf RED in EVPN " "address family.")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"no rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure RD value as 100.100.100:100")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "100.100.100:100"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_manual_p0(request):
+ """
+ RT verification(manual)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 100:100 "
+ "and advertise vrf RED's routes in EVPN address family"
+ " from Edge-1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "100:100"}]}}
+ },
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100"
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 500:500 and"
+ " advertise vrf RED's routes in EVPN address family from"
+ " e1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "500:500"}]}}
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 500:500."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Import RT value 100:100 and 500:500 in vrf BLUE manually on"
+ " peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be installed "
+ "in vrf BLUE on DCG-1 and DCG-2 and further advertised to "
+ "VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Delete import RT value 500:500 in vrf BLUE manually on "
+ "peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step("Delete RT export value 100:100 for vrf RED on Edge-1")
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [{"value": "100:100", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be withdrawn "
+ "from vrf BLUE on DCG-1,DCG-2 and VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Configure RT value as 100:100000010000010000101010 to check "
+ "the boundary value."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [
+ {"value": "100:100000010000010000101010"}
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "CLI error: RT value: 100:100000010000010000101010 should not " "be configured"
+ )
+
+ dut = "e1"
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100000010000010000101010", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: RT value of out"
+ " of boundary \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_active_standby_evpn_implementation_p1(request):
+ """
+ In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Taken care in base config: Configure BGP neighborship for both "
+ "address families(IPv4 & IPv6) between DCG-1/DCG-2 and VFN routers"
+ "(R3 and R4)."
+ )
+
+ step(
+ "BGP neighborships come up within defined VRFs. Please use below "
+ "command: sh bgp vrf all summary"
+ )
+
+ result = verify_bgp_convergence(tgen, topo, "d1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo, "d2")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Advertise prefixes from VNF routers R3 and R4 in associated "
+ "VRFs for both address-families."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Prefixes are received in respective VRFs on DCG-1/DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VRF routes in EVPN "
+ "address-family from DCG-1 and DCG-2 router."
+ )
+
+ step("Verify on Edge-1 that EVPN routes are installed via next-hop " "as DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ if addr_type == "ipv4":
+ result = verify_rib(
+ tgen, addr_type, "e1", input_routes, next_hop=BRIDGE_INTF2
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 'next-hop self' on DCG-1 for peer Edge-1 in EVPN " "address-family."
+ )
+
+ input_dict_3 = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {"d1-link1": {"next_hop_self": True}}
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "Creating route-map so ipv6 glpbal ip wpuld be preferred " "as next-hop"
+ )
+
+ step(
+ "Verify on Edge-1 that EVPN routes are now preferred via "
+ "next-hop as DCG-1(iBGP) due to shortest AS-Path."
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ logger.info("Verifying only ipv4 routes")
+ if addr_type != "ipv4":
+ continue
+
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ next_hop = topo["routers"]["d1"]["links"]["e1-link1"]["ipv4"].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes, next_hop=next_hop)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_routes_from_VNFs_p1(request):
+ """
+ EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VNFs'(R1 and R2) "
+ "originated routes in EVPN address-family from Edge-1 to "
+ "DCG-1 and DCG-2 routers."
+ )
+ step(
+ "Taken care in base config: Advertise IPv4 and IPv6 routes "
+ "from default vrf in EVPN address-family from Edge-1."
+ )
+
+ step(
+ "Verify on DCG-2 that VNF routes are received in respective "
+ "VRFs along with auto derived RD/RT values 'show bgp l2vpn evpn'"
+ )
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
+ )
+
+ step(
+ "Verify on R3 and R4 that DCG-2 further advertises all EVPN "
+ "routes to corresponding VRFs."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes associated to default "
+ "VRF and install in default IP routing table as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw the IP prefixes from VFN(R1).")
+ dut = "r1"
+ input_dict_2 = {}
+ static_routes = topo["routers"][dut]["static_routes"]
+ for static_route in static_routes:
+ static_route["delete"] = True
+ temp = {dut: {"static_routes": [static_route]}}
+ input_dict_2.update(temp)
+
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to vrf RED and "
+ "send an withdraw to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Re-advertise IP prefixes from VFN(R1).")
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to vrf RED "
+ "again and send an update to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete vrf BLUE from router Edge-1")
+ input_dict_3 = {"e1": {"vrfs": [{"name": "BLUE", "id": "2", "delete": True}]}}
+
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to "
+ "vrf BLUE and send an withdraw to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Add vrf BLUE on router Edge-1 again.")
+ interface = topo["routers"]["e1"]["links"]["r2-link1"]["interface"]
+ input_dict_3 = {
+ "e1": {
+ "links": {
+ "r2-link1": {
+ "interface": interface,
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "BLUE",
+ }
+ },
+ "vrfs": [{"name": "BLUE", "id": "2"}],
+ }
+ }
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "After deleting VRFs ipv6 addresses wil be deleted "
+ "from kernel Adding back ipv6 addresses"
+ )
+ dut = "e1"
+ vrfs = ["BLUE"]
+
+ for vrf in vrfs:
+ for c_link, c_data in topo["routers"][dut]["links"].items():
+ if "vrf" in c_data:
+ if c_data["vrf"] != vrf:
+ continue
+
+ intf_name = c_data["interface"]
+ intf_ipv6 = c_data["ipv6"]
+
+ create_interface_in_kernel(
+ tgen, dut, intf_name, intf_ipv6, vrf, create=False
+ )
+
+ logger.info("Wait for 60 sec.")
+ sleep(60)
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to "
+ "vrf BLUE again and send an update to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw IPv6 address-family in EVPN advertisements for " "VRF GREEN")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {addr_type: {"unicast": {"delete": True}}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that EVPN routes (IPv6)associated with vrf GREEN are "
+ "withdrawn from DCG-2 and VNF R4."
+ )
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Advertise IPv6 address-family in EVPN advertisements " "for VRF GREEN.")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {"evpn": {"advertise": {addr_type: {"unicast": {}}}}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {
+ "static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize(
+ "attribute", [{"route-type": "prefix"}, {"vni": VNI_1}, {"rt": "300:300"}]
+)
+def test_route_map_operations_for_evpn_address_family_p1(request, attribute):
+ """
+ Route-map operations for EVPN address family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise VRF routes in EVPN address family from Edge-1 router."
+ " Configure a route-map on e1 to filter EVPN routes based on"
+ " below keywords: route-type: prefix"
+ )
+
+ for key, value in attribute.items():
+ if key == "rt":
+ logger.info("Creating extcommunity using raw_config")
+ raw_config = {
+ "d2": {
+ "raw_config": [
+ "bgp extcommunity-list standard ECOMM300 permit {} {}".format(
+ key, value
+ )
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "set": {"extcommunity": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"extcommunity": "ECOMM300"}}
+ ]
+ }
+ },
+ }
+
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {
+ "d2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router DCG-2 that EVPN routes corresponding to all "
+ "VRFs are received. As all EVPN routes are type-5 only."
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"])
+def test_bgp_attributes_for_evpn_address_family_p1(request, attribute):
+ """
+ BGP attributes for EVPN address-family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ topo_local = deepcopy(topo)
+
+ logger.info("Modifying topology b/w e1 and d1 from iBGP to eBGP")
+ step("Delete BGP config for vrf RED.")
+
+ if attribute == "locPrf":
+ input_dict_vni = {
+ "d1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {}
+ for dut in ["d1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 200
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Advertise VRF routes in EVPN address-family from DCG-1 " "and DCG-2 routers.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes corresponding to "
+ "all VRFs are received from both routers DCG-1 and DCG-2"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map on Edge-1 to modify below BGP attributes "
+ "for EVPN address-family:"
+ )
+
+ if attribute == "path":
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {
+ "as_num": "123 231 321",
+ "as_action": "prepend",
+ }
+ },
+ }
+ ],
+ "rmap_d2".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {"as_num": "121", "as_action": "prepend"}
+ },
+ }
+ ],
+ }
+ }
+ }
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {"action": "permit", "set": {attribute: 120}}
+ ],
+ "rmap_d2".format(addr_type): [
+ {"action": "permit", "set": {attribute: 150}}
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d1",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d2",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes are preferred via"
+ " DCG-1 or DCG-2 based on best path selection criteria "
+ "(according to the configured BGP attribute values in route-map)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, "e1", input_routes, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/isis-sr-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-topo1/rt1/isisd.conf
index 70ae1b07f5..26ec4eb261 100644
--- a/tests/topotests/isis-sr-topo1/rt1/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt1/isisd.conf
@@ -21,6 +21,7 @@ interface eth-sw1
router isis 1
net 49.0000.0000.0000.0001.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-topo1/rt2/isisd.conf
index 733f26bc62..8704a28b6c 100644
--- a/tests/topotests/isis-sr-topo1/rt2/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt2/isisd.conf
@@ -32,6 +32,7 @@ interface eth-rt4-2
router isis 1
net 49.0000.0000.0000.0002.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-topo1/rt3/isisd.conf
index ceb982ca32..5a0add22a9 100644
--- a/tests/topotests/isis-sr-topo1/rt3/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt3/isisd.conf
@@ -32,6 +32,7 @@ interface eth-rt5-2
router isis 1
net 49.0000.0000.0000.0003.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 17000 24999
diff --git a/tests/topotests/isis-sr-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-topo1/rt4/isisd.conf
index 07a7867cbb..39003b9d7b 100644
--- a/tests/topotests/isis-sr-topo1/rt4/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt4/isisd.conf
@@ -39,6 +39,7 @@ interface eth-rt6
router isis 1
net 49.0000.0000.0000.0004.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-topo1/rt5/isisd.conf
index b0fcdede07..e693ca156c 100644
--- a/tests/topotests/isis-sr-topo1/rt5/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt5/isisd.conf
@@ -39,6 +39,7 @@ interface eth-rt6
router isis 1
net 49.0000.0000.0000.0005.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/isis-sr-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-topo1/rt6/isisd.conf
index 16c34dfae6..3b85dbae4e 100644
--- a/tests/topotests/isis-sr-topo1/rt6/isisd.conf
+++ b/tests/topotests/isis-sr-topo1/rt6/isisd.conf
@@ -27,6 +27,7 @@ interface eth-rt5
router isis 1
net 49.0000.0000.0000.0006.00
is-type level-1
+ lsp-gen-interval 2
topology ipv6-unicast
segment-routing on
segment-routing global-block 16000 23999
diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
index 600d640a70..a1662dc411 100755
--- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
+++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
@@ -154,7 +154,7 @@ def teardown_module(mod):
tgen.stop_topology()
-def router_compare_json_output(rname, command, reference):
+def router_compare_json_output(rname, command, reference, count=80, wait=1):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
@@ -163,9 +163,9 @@ def router_compare_json_output(rname, command, reference):
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
- # Run test function until we get an result. Wait at most 80 seconds.
+ # Run test function until we get an result.
test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
- _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ _, diff = topotest.run_and_expect(test_func, None, count, wait)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
@@ -279,10 +279,12 @@ def test_ldp_pseudowires_after_link_down():
tgen.gears["r1"].peer_link_enable("r1-eth1", False)
topotest.sleep(5, "Waiting for the network to reconverge")
- # check if the pseudowire is still up (using an alternate path for nexthop resolution)
+ # check if the pseudowire is still up (using an alternate path
+ # for nexthop resolution). Give some extra wait time.
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
- rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref",
+ count=160, wait=1
)
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index 971bbd0f3b..7b1eead944 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -27,6 +27,8 @@ import sys
from lib import topotest
from lib.topolog import logger
+from lib.topogen import TopoRouter, get_topogen
+
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
@@ -166,6 +168,7 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
ipv4_data = bgp_addr_data.setdefault("ipv4", {})
ipv6_data = bgp_addr_data.setdefault("ipv6", {})
+ l2vpn_data = bgp_addr_data.setdefault("l2vpn", {})
neigh_unicast = (
True
@@ -174,6 +177,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
else False
)
+ l2vpn_evpn = True if l2vpn_data.setdefault("evpn", {}) else False
+
if neigh_unicast:
data_all_bgp = __create_bgp_unicast_neighbor(
tgen,
@@ -184,6 +189,11 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
config_data=data_all_bgp,
)
+ if l2vpn_evpn:
+ data_all_bgp = __create_l2vpn_evpn_address_family(
+ tgen, topo, bgp_data, router, config_data=data_all_bgp
+ )
+
try:
result = create_common_configuration(
tgen, router, data_all_bgp, "bgp", build, load_config
@@ -467,6 +477,166 @@ def __create_bgp_unicast_neighbor(
return config_data
+def __create_l2vpn_evpn_address_family(
+ tgen, topo, input_dict, router, config_data=None
+):
+ """
+ Helper API to create configuration for l2vpn evpn address-family
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring
+ from testcase
+ * `router` : router id to be configured.
+ * `build` : Only for initial setup phase this is set as True.
+ """
+
+ result = False
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ bgp_data = input_dict["address_family"]
+
+ for family_type, family_dict in bgp_data.iteritems():
+ if family_type != "l2vpn":
+ continue
+
+ family_data = family_dict["evpn"]
+ if family_data:
+ config_data.append("address-family l2vpn evpn")
+
+ advertise_data = family_data.setdefault("advertise", {})
+ neighbor_data = family_data.setdefault("neighbor", {})
+ advertise_all_vni_data = family_data.setdefault("advertise-all-vni", None)
+ rd_data = family_data.setdefault("rd", None)
+ no_rd_data = family_data.setdefault("no rd", False)
+ route_target_data = family_data.setdefault("route-target", {})
+
+ if advertise_data:
+ for address_type, unicast_type in advertise_data.items():
+
+ if isinstance(unicast_type, dict):
+ for key, value in unicast_type.items():
+ cmd = "advertise {} {}".format(address_type, key)
+
+ if value:
+ route_map = value.setdefault("route-map", {})
+ advertise_del_action = value.setdefault("delete", None)
+
+ if route_map:
+ cmd = "{} route-map {}".format(cmd, route_map)
+
+ if advertise_del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ if neighbor_data:
+ for neighbor, neighbor_data in neighbor_data.items():
+ ipv4_neighbor = neighbor_data.setdefault("ipv4", {})
+ ipv6_neighbor = neighbor_data.setdefault("ipv6", {})
+
+ if ipv4_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv4"
+ ].split("/")[0]
+
+ if isinstance(action, dict):
+ next_hop_self = action.setdefault("next_hop_self", None)
+ route_maps = action.setdefault("route_maps", {})
+
+ if next_hop_self is not None:
+ if next_hop_self is True:
+ config_data.append(
+ "neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+ elif next_hop_self is False:
+ config_data.append(
+ "no neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+
+ if route_maps:
+ for route_map in route_maps:
+ name = route_map.setdefault("name", {})
+ direction = route_map.setdefault("direction", "in")
+ del_action = route_map.setdefault("delete", False)
+
+ if not name:
+ logger.info(
+ "Router %s: 'name' "
+ "not present in "
+ "input_dict for BGP "
+ "neighbor route name",
+ router,
+ )
+ else:
+ cmd = "neighbor {} route-map {} " "{}".format(
+ neighbor_ip, name, direction
+ )
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ else:
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if ipv6_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv6"
+ ].split("/")[0]
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if advertise_all_vni_data == True:
+ cmd = "advertise-all-vni"
+ config_data.append(cmd)
+ elif advertise_all_vni_data == False:
+ cmd = "no advertise-all-vni"
+ config_data.append(cmd)
+
+ if rd_data:
+ cmd = "rd {}".format(rd_data)
+ config_data.append(cmd)
+
+ if no_rd_data:
+ cmd = "no rd {}".format(no_rd_data)
+ config_data.append(cmd)
+
+ if route_target_data:
+ for rt_type, rt_dict in route_target_data.items():
+ for _rt_dict in rt_dict:
+ rt_value = _rt_dict.setdefault("value", None)
+ del_rt = _rt_dict.setdefault("delete", None)
+
+ if rt_value:
+ cmd = "route-target {} {}".format(rt_type, rt_value)
+ if del_rt:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return config_data
+
+
def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -489,7 +659,7 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
for dest_link, peer in peer_dict["dest_link"].iteritems():
nh_details = topo[name]
- if "vrfs" in topo[router]:
+ if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
remote_as = nh_details["bgp"][0]["local_as"]
else:
remote_as = nh_details["bgp"]["local_as"]
@@ -887,19 +1057,16 @@ def verify_bgp_convergence(tgen, topo, dut=None):
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
state is established,
-
Parameters
----------
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
-
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, dut="r1")
-
Returns
-------
errormsg(str) or True
@@ -3463,3 +3630,552 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut):
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_attributes_for_evpn_routes(
+ tgen,
+ topo,
+ dut,
+ input_dict,
+ rd=None,
+ rt=None,
+ ethTag=None,
+ ipLen=None,
+ rd_peer=None,
+ rt_peer=None,
+):
+ """
+ API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `rd` : route distinguisher
+ * `rt` : route target
+ * `ethTag` : Ethernet Tag
+ * `ipLen` : IP prefix length
+ * `rd_peer` : Peer name from which RD will be auto-generated
+ * `rt_peer` : Peer name from which RT will be auto-generated
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(tgen, topo,
+ input_dict, rd = "10.0.0.33:1")
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if "vrf" in static_route:
+ vrf = static_route["vrf"]
+
+ if type(network) is not list:
+ network = [network]
+
+ for route in network:
+ route = route.split("/")[0]
+ _addr_type = validate_ip_address(route)
+ if "v4" in _addr_type:
+ input_afi = "v4"
+ elif "v6" in _addr_type:
+ input_afi = "v6"
+
+ cmd = "show bgp l2vpn evpn {} json".format(route)
+ evpn_rd_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_rd_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if rd is not None and rd != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rd == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd_peer:
+ index = 1
+ vni_dict = {}
+
+ rnode = tgen.routers()[rd_peer]
+ vrfs = topo["routers"][rd_peer]["vrfs"]
+ for vrf_dict in vrfs:
+ vni_dict[vrf_dict["name"]] = index
+ index += 1
+
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ router_id = afi_data["routerId"]
+
+ rd = "{}:{}".format(router_id, vni_dict[vrf])
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rt == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rt_peer:
+ vni_dict = {}
+
+ rnode = tgen.routers()[rt_peer]
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ as_num = afi_data["as"]
+
+ show_vrf_vni_json = run_frr_cmd(
+ rnode, "show vrf vni json", isjson=True
+ )
+
+ vrfs = show_vrf_vni_json["vrfs"]
+ for vrf_dict in vrfs:
+ if vrf_dict["vrf"] == vrf:
+ vni_dict[vrf_dict["vrf"]] = str(vrf_dict["vni"])
+
+ # If AS is 4 byte, FRR uses only the lower 2 bytes of ASN+VNI
+ # for auto derived RT value.
+ if as_num > 65535:
+ as_bin = bin(as_num)
+ as_bin = as_bin[-16:]
+ as_num = int(as_bin, 2)
+
+ rt = "{}:{}".format(str(as_num), vni_dict[vrf])
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ if vni_dict[vrf] == rt_data["VNI"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " Verifying RT "
+ "value for EVPN "
+ " route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "RT value for EVPN "
+ "route: %s [PASSED]||"
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if rt is not None and rt != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if type(rt) is not list:
+ rt = [rt]
+
+ for _rt in rt:
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(_rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed: "
+ "Verifying RT value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RT"
+ " value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if ethTag is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ethTag value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ethTag"] != ethTag:
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ethTag value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ _rd,
+ route,
+ ethTag,
+ route_data["ethTag"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ethTag value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ethTag,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output" % (dut, _rd, route)
+ )
+ return errormsg
+
+ if ipLen is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ipLen value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ipLen"] != int(ipLen):
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ipLen value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, _rd, route, ipLen, route_data["ipLen"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ipLen value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ipLen,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output " % (dut, route)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_evpn_routes(
+ tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None
+):
+ """
+ API to verify evpn routes using "sh bgp l2vpn evpn"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `route_type` : Route type 5 is supported as of now
+ * `EthTag` : Ethernet tag, by-default is 0
+ * `next_hop` : Prefered nexthop for the evpn routes
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+ result = verify_evpn_routes(tgen, topo, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn routes: ", dut)
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if type(network) is not list:
+ network = [network]
+
+ missing_routes = {}
+ for route in network:
+ rd_keys = 0
+ ip_len = route.split("/")[1]
+ route = route.split("/")[0]
+
+ prefix = "[{}]:[{}]:[{}]:[{}]".format(
+ routeType, EthTag, ip_len, route
+ )
+
+ cmd = "show bgp l2vpn evpn route json"
+ evpn_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if not bool(evpn_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if evpn_value_json["numPrefix"] == 0:
+ errormsg = "[DUT: %s]: No EVPN prefixes exist" % (dut)
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ rd_keys += 1
+ if prefix not in route_data_json:
+ missing_routes[key] = prefix
+
+ if rd_keys == len(missing_routes.keys()):
+ errormsg = (
+ "[DUT: %s]: "
+ "Missing EVPN routes: "
+ "%s [FAILED]!!" % (dut, list(set(missing_routes.values())))
+ )
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ if prefix not in route_data_json:
+ continue
+
+ for paths in route_data_json[prefix]["paths"]:
+ for path in paths:
+ if path["routeType"] != routeType:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying routeType "
+ "for EVPN route: %s "
+ "[FAILED]!! "
+ "Expected: %s, "
+ "Found: %s"
+ % (
+ dut,
+ prefix,
+ routeType,
+ path["routeType"],
+ )
+ )
+ return errormsg
+
+ elif next_hop:
+ for nh_dict in path["nexthops"]:
+ if nh_dict["ip"] != next_hop:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying "
+ "nexthop for "
+ "EVPN route: %s"
+ "[FAILED]!! "
+ "Expected: %s,"
+ " Found: %s"
+ % (
+ dut,
+ prefix,
+ next_hop,
+ nh_dict["ip"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "EVPN route : %s, "
+ "routeType: %s is "
+ "installed "
+ "[PASSED]|| ",
+ dut,
+ prefix,
+ routeType,
+ )
+ return True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return False
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d72d0aa223..e4d72ea2d7 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -933,6 +933,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
)
rnode.run(cmd)
+ if vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "vni {}".format(vni)
+ config_data.append(cmd)
+
+ if del_vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "no vni {}".format(del_vni)
+ config_data.append(cmd)
+
result = create_common_configuration(
tgen, c_router, config_data, "vrf", build=build
)
@@ -984,6 +994,34 @@ def create_interface_in_kernel(
rnode.run(cmd)
+def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False):
+ """
+ Cretae interfaces in kernel for ipv4/ipv6
+ Config is done in Linux Kernel:
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut` : Device for which interfaces to be added
+ * `intf_name` : interface name
+ * `ifaceaction` : False to shutdown and True to bringup the
+ ineterface
+ """
+
+ rnode = tgen.routers()[dut]
+
+ cmd = "ip link set dev"
+ if ifaceaction:
+ action = "up"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+ else:
+ action = "down"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+
def validate_ip_address(ip_address):
"""
Validates the type of ip address
@@ -1042,7 +1080,7 @@ def check_address_types(addr_type=None):
return addr_types
if addr_type not in addr_types:
- logger.error(
+ logger.debug(
"{} not in supported/configured address types {}".format(
addr_type, addr_types
)
@@ -1732,6 +1770,7 @@ def create_route_maps(tgen, input_dict, build=False):
set_action = set_data.setdefault("set_action", None)
nexthop = set_data.setdefault("nexthop", None)
origin = set_data.setdefault("origin", None)
+ ext_comm_list = set_data.setdefault("extcommunity", {})
# Local Preference
if local_preference:
@@ -1796,6 +1835,19 @@ def create_route_maps(tgen, input_dict, build=False):
logger.error("In large_comm_list 'id' not" " provided")
return False
+ if ext_comm_list:
+ rt = ext_comm_list.setdefault("rt", None)
+ del_comm = ext_comm_list.setdefault("delete", None)
+ if rt:
+ cmd = "set extcommunity rt {}".format(rt)
+ if del_comm:
+ cmd = "{} delete".format(cmd)
+
+ rmap_data.append(cmd)
+ else:
+ logger.debug("In ext_comm_list 'rt' not" " provided")
+ return False
+
# Weight
if weight:
rmap_data.append("set weight {}".format(weight))
@@ -2151,6 +2203,243 @@ def addKernelRoute(
return True
+def configure_vxlan(tgen, input_dict):
+ """
+ Add and configure vxlan
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for vxlan config
+
+ Usage:
+ ------
+ input_dict= {
+ "dcg2":{
+ "vxlan":[{
+ "vxlan_name": "vxlan75100",
+ "vxlan_id": "75100",
+ "dstport": 4789,
+ "local_addr": "120.0.0.1",
+ "learning": "no",
+ "delete": True
+ }]
+ }
+ }
+
+ configure_vxlan(tgen, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "vxlan" in input_dict[dut]:
+ for vxlan_dict in input_dict[dut]["vxlan"]:
+ cmd = "ip link "
+
+ del_vxlan = vxlan_dict.setdefault("delete", None)
+ vxlan_names = vxlan_dict.setdefault("vxlan_name", [])
+ vxlan_ids = vxlan_dict.setdefault("vxlan_id", [])
+ dstport = vxlan_dict.setdefault("dstport", None)
+ local_addr = vxlan_dict.setdefault("local_addr", None)
+ learning = vxlan_dict.setdefault("learning", None)
+
+ config_data = []
+ if vxlan_names and vxlan_ids:
+ for vxlan_name, vxlan_id in zip(vxlan_names, vxlan_ids):
+ cmd = "ip link"
+
+ if del_vxlan:
+ cmd = "{} del {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+ else:
+ cmd = "{} add {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+
+ if dstport:
+ cmd = "{} dstport {}".format(cmd, dstport)
+
+ if local_addr:
+ ip_cmd = "ip addr add {} dev {}".format(
+ local_addr, vxlan_name
+ )
+ if del_vxlan:
+ ip_cmd = "ip addr del {} dev {}".format(
+ local_addr, vxlan_name
+ )
+
+ config_data.append(ip_cmd)
+
+ cmd = "{} local {}".format(cmd, local_addr)
+
+ if learning == "no":
+ cmd = "{} nolearning".format(cmd)
+
+ elif learning == "yes":
+ cmd = "{} learning".format(cmd)
+
+ config_data.append(cmd)
+
+ try:
+ for _cmd in config_data:
+ logger.info("[DUT: %s]: Running command: %s", dut, _cmd)
+ rnode.run(_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def configure_brctl(tgen, topo, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for brctl config
+
+ Usage:
+ ------
+ input_dict= {
+ dut:{
+ "brctl": [{
+ "brctl_name": "br100",
+ "addvxlan": "vxlan75100",
+ "vrf": "RED",
+ "stp": "off"
+ }]
+ }
+ }
+
+ configure_brctl(tgen, topo, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "brctl" in input_dict[dut]:
+ for brctl_dict in input_dict[dut]["brctl"]:
+
+ brctl_names = brctl_dict.setdefault("brctl_name", [])
+ addvxlans = brctl_dict.setdefault("addvxlan", [])
+ stp_values = brctl_dict.setdefault("stp", [])
+ vrfs = brctl_dict.setdefault("vrf", [])
+
+ ip_cmd = "ip link set"
+ for brctl_name, vxlan, vrf, stp in zip(
+ brctl_names, addvxlans, vrfs, stp_values
+ ):
+
+ ip_cmd_list = []
+ cmd = "ip link add name {} type bridge stp_state {}".format(brctl_name, stp)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, brctl_name))
+
+ if vxlan:
+ cmd = "{} dev {} master {}".format(ip_cmd, vxlan, brctl_name)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, vxlan))
+
+ if vrf:
+ ip_cmd_list.append(
+ "{} dev {} master {}".format(ip_cmd, brctl_name, vrf)
+ )
+
+ for intf_name, data in topo["routers"][dut]["links"].items():
+ if "vrf" not in data:
+ continue
+
+ if data["vrf"] == vrf:
+ ip_cmd_list.append(
+ "{} up dev {}".format(ip_cmd, data["interface"])
+ )
+
+ try:
+ for _ip_cmd in ip_cmd_list:
+ logger.info("[DUT: %s]: Running command: %s", dut, _ip_cmd)
+ rnode.run(_ip_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def configure_interface_mac(tgen, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for mac config
+
+ input_mac= {
+ "edge1":{
+ "br75100": "00:80:48:BA:d1:00,
+ "br75200": "00:80:48:BA:d1:00
+ }
+ }
+
+ configure_interface_mac(tgen, input_mac)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for intf, mac in input_dict[dut].items():
+ cmd = "ifconfig {} hw ether {}".format(intf, mac)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+
+ try:
+ result = rnode.run(cmd)
+ if len(result) != 0:
+ return result
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ return True
+
+
#############################################
# Verification APIs
#############################################
@@ -2875,3 +3164,283 @@ def verify_create_community_list(tgen, input_dict):
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
+
+
+def verify_cli_json(tgen, input_dict):
+ """
+ API to verify if JSON is available for clis
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: CLIs for which JSON needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "cli": ["show evpn vni detail", show evpn rmac vni all]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for cli in input_dict[dut]["cli"]:
+ logger.info(
+ "[DUT: %s]: Verifying JSON is available for " "CLI %s :", dut, cli
+ )
+
+ test_cli = "{} json".format(cli)
+ ret_json = rnode.vtysh_cmd(test_cli, isjson=True)
+ if not bool(ret_json):
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ elif "unknown" in ret_json or "Unknown" in ret_json:
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ else:
+ logger.info(
+ "CLI : %s JSON format is available: " "\n %s", cli, ret_json
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_evpn_vni(tgen, input_dict):
+ """
+ API to verify evpn vni details using "show evpn vni detail json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vni": [
+ {
+ "75100":{
+ "vrf": "RED",
+ "vxlanIntf": "vxlan75100",
+ "localVtepIp": "120.1.1.1",
+ "sviIntf": "br100"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_evpn_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn vni details :", dut)
+
+ cmd = "show evpn vni detail json"
+ evpn_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vni" in input_dict[dut]:
+ for vni_dict in input_dict[dut]["vni"]:
+ found = False
+ vni = vni_dict["name"]
+ for evpn_vni_json in evpn_all_vni_json:
+ if "vni" in evpn_vni_json:
+ if evpn_vni_json["vni"] != int(vni):
+ continue
+
+ for attribute in vni_dict.keys():
+ if vni_dict[attribute] != evpn_vni_json[attribute]:
+ errormsg = (
+ "[DUT: %s] Verifying "
+ "%s for VNI: %s [FAILED]||"
+ ", EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ attribute,
+ vni,
+ vni_dict[attribute],
+ evpn_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ found = True
+ logger.info(
+ "[DUT: %s] Verifying"
+ " %s for VNI: %s , "
+ "Found Expected : %s ",
+ dut,
+ attribute,
+ vni,
+ evpn_vni_json[attribute],
+ )
+
+ if evpn_vni_json["state"] != "Up":
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " State for VNI: %s is not Up" % (dut, vni)
+ )
+ return errormsg
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " VNI: %s is not present in JSON" % (dut, vni)
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s]: Verifying VNI : %s "
+ "details and state is Up [PASSED]!!",
+ dut,
+ vni,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vni details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_vrf_vni(tgen, input_dict):
+ """
+ API to verify vrf vni details using "show vrf vni json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vrfs": [
+ {
+ "RED":{
+ "vni": 75000,
+ "vxlanIntf": "vxlan75100",
+ "sviIntf": "br100",
+ "routerMac": "00:80:48:ba:d1:00",
+ "state": "Up"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying vrf vni details :", dut)
+
+ cmd = "show vrf vni json"
+ vrf_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(vrf_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vrfs" in input_dict[dut]:
+ for vrfs in input_dict[dut]["vrfs"]:
+ for vrf, vrf_dict in vrfs.items():
+ found = False
+ for vrf_vni_json in vrf_all_vni_json["vrfs"]:
+ if "vrf" in vrf_vni_json:
+ if vrf_vni_json["vrf"] != vrf:
+ continue
+
+ for attribute in vrf_dict.keys():
+ if vrf_dict[attribute] == vrf_vni_json[attribute]:
+ found = True
+ logger.info(
+ "[DUT %s]: VRF: %s, "
+ "verifying %s "
+ ", Found Expected: %s "
+ "[PASSED]!!",
+ dut,
+ vrf,
+ attribute,
+ vrf_vni_json[attribute],
+ )
+ else:
+ errormsg = (
+ "[DUT: %s] VRF: %s, "
+ "verifying %s [FAILED!!] "
+ ", EXPECTED : %s "
+ ", FOUND : %s"
+ % (
+ dut,
+ vrf,
+ attribute,
+ vrf_dict[attribute],
+ vrf_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ errormsg = "[DUT: %s] VRF: %s " "is not present in JSON" % (
+ dut,
+ vrf,
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s] Verifying VRF: %s " " details [PASSED]!!",
+ dut,
+ vrf,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vrf details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index efd5b90685..37b9715010 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -819,7 +819,9 @@ class TopoRouter(TopoGear):
if memleak_file is None:
return
- self.stop()
+ self.stop(False, False)
+ self.stop(wait=True)
+
self.logger.info("running memory leak report")
self.tgen.net[self.name].report_memory_leaks(memleak_file, testname)
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index bffb8208e7..b5fa2ea59b 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -35,6 +35,7 @@ import tempfile
import platform
import difflib
import time
+import signal
from lib.topolog import logger
from copy import deepcopy
@@ -51,6 +52,35 @@ from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.link import Intf
+def gdb_core(obj, daemon, corefiles):
+ gdbcmds = '''
+ info threads
+ bt full
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ up
+ disassemble
+ '''
+ gdbcmds = [['-ex', i.strip()] for i in gdbcmds.strip().split('\n')]
+ gdbcmds = [item for sl in gdbcmds for item in sl]
+
+ daemon_path = os.path.join(obj.daemondir, daemon)
+ backtrace = subprocess.check_output(
+ ['gdb', daemon_path, corefiles[0], '--batch'] + gdbcmds
+ )
+ sys.stderr.write(
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n"
+ % (obj.name, daemon)
+ )
+ sys.stderr.write("%s" % backtrace)
+ return backtrace
class json_cmp_result(object):
"json_cmp result class for better assertion messages"
@@ -422,6 +452,10 @@ def pid_exists(pid):
if pid <= 0:
return False
try:
+ os.waitpid(pid, os.WNOHANG)
+ except:
+ pass
+ try:
os.kill(pid, 0)
except OSError as err:
if err.errno == errno.ESRCH:
@@ -992,8 +1026,8 @@ class Router(Node):
os.system("chmod -R go+rw /tmp/topotests")
# Return count of running daemons
- def countDaemons(self):
- numRunning = 0
+ def listDaemons(self):
+ ret = []
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
errors = ""
if re.search(r"No such file or directory", rundaemons):
@@ -1002,12 +1036,11 @@ class Router(Node):
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
if daemonpid.isdigit() and pid_exists(int(daemonpid)):
- numRunning += 1
- return numRunning
+ ret.append(os.path.basename(d.rstrip().rsplit(".", 1)[0]))
+ return ret
def stopRouter(self, wait=True, assertOnError=True, minErrorVersion="5.1"):
# Stop Running FRR Daemons
- numRunning = 0
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
errors = ""
if re.search(r"No such file or directory", rundaemons):
@@ -1016,24 +1049,36 @@ class Router(Node):
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
logger.info(
"{}: stopping {}".format(
- self.name, os.path.basename(d.rstrip().rsplit(".", 1)[0])
+ self.name, daemonname
)
)
- self.cmd("kill -TERM %s" % daemonpid)
- self.waitOutput()
- if pid_exists(int(daemonpid)):
- numRunning += 1
-
- if wait and numRunning > 0:
- counter = 5
- while counter > 0 and numRunning > 0:
- sleep(2, "{}: waiting for daemons stopping".format(self.name))
- numRunning = self.countDaemons()
+ try:
+ os.kill(int(daemonpid), signal.SIGTERM)
+ except OSError as err:
+ if err.errno == errno.ESRCH:
+ logger.error("{}: {} left a dead pidfile (pid={})".format(self.name, daemonname, daemonpid))
+ else:
+ logger.info("{}: {} could not kill pid {}: {}".format(self.name, daemonname, daemonpid, str(err)))
+
+ if not wait:
+ return errors
+
+ running = self.listDaemons()
+
+ if running:
+ sleep(0.1, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running)))
+ running = self.listDaemons()
+
+ counter = 20
+ while counter > 0 and running:
+ sleep(0.5, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running)))
+ running = self.listDaemons()
counter -= 1
- if wait and numRunning > 0:
+ if running:
# 2nd round of kill if daemons didn't exit
for d in StringIO.StringIO(rundaemons):
daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
@@ -1048,13 +1093,15 @@ class Router(Node):
self.waitOutput()
self.cmd("rm -- {}".format(d.rstrip()))
- if wait:
- errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
- if assertOnError and len(errors) > 0:
- assert "Errors found - details follow:" == 0, errors
+ if not wait:
+ return errors
+
+ errors = self.checkRouterCores(reportOnce=True)
+ if self.checkRouterVersion("<", minErrorVersion):
+ # ignore errors in old versions
+ errors = ""
+ if assertOnError and len(errors) > 0:
+ assert "Errors found - details follow:" == 0, errors
return errors
def removeIPs(self):
@@ -1348,20 +1395,7 @@ class Router(Node):
"{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
)
if len(corefiles) > 0:
- daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output(
- [
- "gdb {} {} --batch -ex bt 2> /dev/null".format(
- daemon_path, corefiles[0]
- )
- ],
- shell=True,
- )
- sys.stderr.write(
- "\n%s: %s crashed. Core file found - Backtrace follows:\n"
- % (self.name, daemon)
- )
- sys.stderr.write("%s" % backtrace)
+ backtrace = gdb_core(self, daemon, corefiles)
traces = (
traces
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n%s"
@@ -1431,20 +1465,7 @@ class Router(Node):
"{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
)
if len(corefiles) > 0:
- daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output(
- [
- "gdb {} {} --batch -ex bt 2> /dev/null".format(
- daemon_path, corefiles[0]
- )
- ],
- shell=True,
- )
- sys.stderr.write(
- "\n%s: %s crashed. Core file found - Backtrace follows:\n"
- % (self.name, daemon)
- )
- sys.stderr.write("%s\n" % backtrace)
+ gdb_core(self, daemon, corefiles)
else:
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
if os.path.isfile(
diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
index 5ae2399e5c..dd42e326ce 100644
--- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -18,6 +21,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -38,6 +44,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -58,6 +67,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -104,6 +116,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -117,6 +132,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
index aedcc5b8f8..f3462e239e 100644
--- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -26,6 +29,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.1"
@@ -39,6 +45,9 @@
{
"type":"SR (OSPF)",
"outLabel":10400,
+ "outLabelStack":[
+ 10400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -52,6 +61,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -65,6 +77,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -78,6 +93,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -91,6 +109,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -104,6 +125,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -117,6 +141,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
index 71e8366137..3d036801d5 100644
--- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -58,6 +70,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
index b5767e1d7d..86ad8721f8 100644
--- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -57,6 +69,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -70,6 +85,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json
index f0738dc540..bfa0ecb849 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-map.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-map.json
@@ -62,6 +62,32 @@
},
"matchDst":"dead:beef::\/64",
"matchMark":314159
+ },
+ {
+ "sequenceNumber":15,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA15",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchDscp":10
+ },
+ {
+ "sequenceNumber":20,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA20",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchEcn":1
}
]
},
diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf
index 298cba2860..45cb7656ab 100644
--- a/tests/topotests/pbr-topo1/r1/pbrd.conf
+++ b/tests/topotests/pbr-topo1/r1/pbrd.conf
@@ -73,6 +73,16 @@ pbr-map ASAKUSA seq 10
match mark 314159
set nexthop c0ff:ee::1
!
+pbr-map ASAKUSA seq 15
+ match dst-ip dead:beef::/64
+ match dscp af11
+ set nexthop c0ff:ee::1
+!
+pbr-map ASAKUSA seq 20
+ match dst-ip dead:beef::/64
+ match ecn 1
+ set nexthop c0ff:ee::1
+!
# Interface policies
int r1-eth1
pbr-policy EVA
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
index ade5bfd501..77b8c2b478 100644
--- a/tests/topotests/pytest.ini
+++ b/tests/topotests/pytest.ini
@@ -1,6 +1,6 @@
# Skip pytests example directory
[pytest]
-norecursedirs = .git example-test example-topojson-test lib docker
+norecursedirs = .git example-test example-topojson-test lib docker evpn_type5_test_topo1
[topogen]
# Default configuration values
diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py
index 9ba0c7e50e..508d1746b3 100755
--- a/tests/topotests/route-scale/test_route_scale.py
+++ b/tests/topotests/route-scale/test_route_scale.py
@@ -112,99 +112,103 @@ def test_converge_protocols():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
-def test_route_install():
- "Test route install for a variety of ecmp"
+def run_one_setup(r1, s):
+ "Run one ecmp config"
- tgen = get_topogen()
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ # Extract params
+ expected_installed = s['expect_in']
+ expected_removed = s['expect_rem']
- installed_file = "{}/r1/installed.routes.json".format(CWD)
- expected_installed = json.loads(open(installed_file).read())
+ count = s['count']
+ wait = s['wait']
- removed_file = "{}/r1/no.routes.json".format(CWD)
- expected_removed = json.loads(open(removed_file).read())
+ logger.info("Testing 1 million routes X {} ecmp".format(s['ecmp']))
- r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("sharp install route 1.0.0.0 \
+ nexthop-group {} 1000000".format(s['nhg']),
+ isjson=False)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group one 1000000", isjson=False)
test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 1 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 1 ecmp removed")
- logger.info(output)
+ success, result = topotest.run_and_expect(test_func, None, count, wait)
+ assert success, "Route scale test install failed:\n{}".format(result)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group two 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 2 ecmp installed")
+ logger.info("1 million routes X {} ecmp installed".format(s['ecmp']))
logger.info(output)
r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 2 ecmp removed")
- logger.info(output)
+ success, result = topotest.run_and_expect(test_func, None, count, wait)
+ assert success, "Route scale test remove failed:\n{}".format(result)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group four 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 4 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 4 ecmp removed")
+ logger.info("1 million routes x {} ecmp removed".format(
+ s['ecmp']))
logger.info(output)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group eight 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 8 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 8 ecmp removed")
- logger.info(output)
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group sixteen 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 16 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 16 ecmp removed")
- logger.info(output)
+def test_route_install():
+ "Test route install for a variety of ecmp"
- r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group thirtytwo 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes X 32 ecmp installed")
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False)
- test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=5)
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("1 million routes x 32 ecmp removed")
- logger.info(output)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ installed_file = "{}/r1/installed.routes.json".format(CWD)
+ expected_installed = json.loads(open(installed_file).read())
+
+ removed_file = "{}/r1/no.routes.json".format(CWD)
+ expected_removed = json.loads(open(removed_file).read())
+
+ # dict keys of params: ecmp number, corresponding nhg name, timeout,
+ # number of times to wait
+ scale_keys = ['ecmp', 'nhg', 'wait', 'count', 'expect_in', 'expect_rem']
+
+ # Table of defaults, used for timeout values and 'expected' objects
+ scale_defaults = dict(zip(scale_keys, [None, None, 7, 30,
+ expected_installed,
+ expected_removed]))
+
+ # List of params for each step in the test; note extra time given
+ # for the highest ecmp steps. Executing 'show' at scale can be costly
+ # so we widen the interval there too.
+ scale_steps = [
+ [1, 'one'], [2, 'two'], [4, 'four'],
+ [8, 'eight'], [16, 'sixteen', 10, 40], [32, 'thirtytwo', 10, 40]
+ ]
+
+ # Build up a list of dicts with params for each step of the test;
+ # use defaults where the step doesn't supply a value
+ scale_setups = []
+ for s in scale_steps:
+ d = dict(zip(scale_keys, s))
+ for k in scale_keys:
+ if k not in d:
+ d[k] = scale_defaults[k]
+
+ scale_setups.append(d)
+
+ # Avoid top ecmp case for runs with < 4G memory
+ p = os.popen('free')
+ l = p.readlines()[1].split()
+ mem = int(l[1])
+ if mem < 4000000:
+ logger.info('Limited memory available: {}, skipping x32 testcase'.format(mem))
+ scale_setups = scale_setups[0:-1]
+
+ # Run each step using the dicts we've built
+ for s in scale_setups:
+ run_one_setup(r1, s)
+
+# Mem leak testcase
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+ tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/__init__.py
diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/sharpd.conf
diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json
new file mode 100644
index 0000000000..61e9bb240b
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/v4_route.json
@@ -0,0 +1,2802 @@
+{
+ "2.1.3.7\/32":[
+ {
+ "prefix":"2.1.3.7\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.8\/32":[
+ {
+ "prefix":"2.1.3.8\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.9\/32":[
+ {
+ "prefix":"2.1.3.9\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.10\/32":[
+ {
+ "prefix":"2.1.3.10\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.11\/32":[
+ {
+ "prefix":"2.1.3.11\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.12\/32":[
+ {
+ "prefix":"2.1.3.12\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.13\/32":[
+ {
+ "prefix":"2.1.3.13\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.14\/32":[
+ {
+ "prefix":"2.1.3.14\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.15\/32":[
+ {
+ "prefix":"2.1.3.15\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.16\/32":[
+ {
+ "prefix":"2.1.3.16\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.17\/32":[
+ {
+ "prefix":"2.1.3.17\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.18\/32":[
+ {
+ "prefix":"2.1.3.18\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.19\/32":[
+ {
+ "prefix":"2.1.3.19\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.20\/32":[
+ {
+ "prefix":"2.1.3.20\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.21\/32":[
+ {
+ "prefix":"2.1.3.21\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.22\/32":[
+ {
+ "prefix":"2.1.3.22\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.23\/32":[
+ {
+ "prefix":"2.1.3.23\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.24\/32":[
+ {
+ "prefix":"2.1.3.24\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.25\/32":[
+ {
+ "prefix":"2.1.3.25\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.26\/32":[
+ {
+ "prefix":"2.1.3.26\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.27\/32":[
+ {
+ "prefix":"2.1.3.27\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.28\/32":[
+ {
+ "prefix":"2.1.3.28\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.29\/32":[
+ {
+ "prefix":"2.1.3.29\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.30\/32":[
+ {
+ "prefix":"2.1.3.30\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.31\/32":[
+ {
+ "prefix":"2.1.3.31\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.32\/32":[
+ {
+ "prefix":"2.1.3.32\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.33\/32":[
+ {
+ "prefix":"2.1.3.33\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.34\/32":[
+ {
+ "prefix":"2.1.3.34\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.35\/32":[
+ {
+ "prefix":"2.1.3.35\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.36\/32":[
+ {
+ "prefix":"2.1.3.36\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.37\/32":[
+ {
+ "prefix":"2.1.3.37\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.38\/32":[
+ {
+ "prefix":"2.1.3.38\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.39\/32":[
+ {
+ "prefix":"2.1.3.39\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.40\/32":[
+ {
+ "prefix":"2.1.3.40\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.41\/32":[
+ {
+ "prefix":"2.1.3.41\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.42\/32":[
+ {
+ "prefix":"2.1.3.42\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.43\/32":[
+ {
+ "prefix":"2.1.3.43\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.44\/32":[
+ {
+ "prefix":"2.1.3.44\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.45\/32":[
+ {
+ "prefix":"2.1.3.45\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.46\/32":[
+ {
+ "prefix":"2.1.3.46\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.47\/32":[
+ {
+ "prefix":"2.1.3.47\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.48\/32":[
+ {
+ "prefix":"2.1.3.48\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.49\/32":[
+ {
+ "prefix":"2.1.3.49\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.50\/32":[
+ {
+ "prefix":"2.1.3.50\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.51\/32":[
+ {
+ "prefix":"2.1.3.51\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.52\/32":[
+ {
+ "prefix":"2.1.3.52\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.53\/32":[
+ {
+ "prefix":"2.1.3.53\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.54\/32":[
+ {
+ "prefix":"2.1.3.54\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.55\/32":[
+ {
+ "prefix":"2.1.3.55\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.56\/32":[
+ {
+ "prefix":"2.1.3.56\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.57\/32":[
+ {
+ "prefix":"2.1.3.57\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.58\/32":[
+ {
+ "prefix":"2.1.3.58\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.59\/32":[
+ {
+ "prefix":"2.1.3.59\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.60\/32":[
+ {
+ "prefix":"2.1.3.60\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.61\/32":[
+ {
+ "prefix":"2.1.3.61\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.62\/32":[
+ {
+ "prefix":"2.1.3.62\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.63\/32":[
+ {
+ "prefix":"2.1.3.63\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.64\/32":[
+ {
+ "prefix":"2.1.3.64\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.65\/32":[
+ {
+ "prefix":"2.1.3.65\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.66\/32":[
+ {
+ "prefix":"2.1.3.66\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.67\/32":[
+ {
+ "prefix":"2.1.3.67\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.68\/32":[
+ {
+ "prefix":"2.1.3.68\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.69\/32":[
+ {
+ "prefix":"2.1.3.69\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.70\/32":[
+ {
+ "prefix":"2.1.3.70\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.71\/32":[
+ {
+ "prefix":"2.1.3.71\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.72\/32":[
+ {
+ "prefix":"2.1.3.72\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.73\/32":[
+ {
+ "prefix":"2.1.3.73\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.74\/32":[
+ {
+ "prefix":"2.1.3.74\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.75\/32":[
+ {
+ "prefix":"2.1.3.75\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.76\/32":[
+ {
+ "prefix":"2.1.3.76\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.77\/32":[
+ {
+ "prefix":"2.1.3.77\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.78\/32":[
+ {
+ "prefix":"2.1.3.78\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.79\/32":[
+ {
+ "prefix":"2.1.3.79\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.80\/32":[
+ {
+ "prefix":"2.1.3.80\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.81\/32":[
+ {
+ "prefix":"2.1.3.81\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.82\/32":[
+ {
+ "prefix":"2.1.3.82\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.83\/32":[
+ {
+ "prefix":"2.1.3.83\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.84\/32":[
+ {
+ "prefix":"2.1.3.84\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.85\/32":[
+ {
+ "prefix":"2.1.3.85\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.86\/32":[
+ {
+ "prefix":"2.1.3.86\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.87\/32":[
+ {
+ "prefix":"2.1.3.87\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.88\/32":[
+ {
+ "prefix":"2.1.3.88\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.89\/32":[
+ {
+ "prefix":"2.1.3.89\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.90\/32":[
+ {
+ "prefix":"2.1.3.90\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.91\/32":[
+ {
+ "prefix":"2.1.3.91\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.92\/32":[
+ {
+ "prefix":"2.1.3.92\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.93\/32":[
+ {
+ "prefix":"2.1.3.93\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.94\/32":[
+ {
+ "prefix":"2.1.3.94\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.95\/32":[
+ {
+ "prefix":"2.1.3.95\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.96\/32":[
+ {
+ "prefix":"2.1.3.96\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.97\/32":[
+ {
+ "prefix":"2.1.3.97\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.98\/32":[
+ {
+ "prefix":"2.1.3.98\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.99\/32":[
+ {
+ "prefix":"2.1.3.99\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.100\/32":[
+ {
+ "prefix":"2.1.3.100\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.101\/32":[
+ {
+ "prefix":"2.1.3.101\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.102\/32":[
+ {
+ "prefix":"2.1.3.102\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.103\/32":[
+ {
+ "prefix":"2.1.3.103\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.104\/32":[
+ {
+ "prefix":"2.1.3.104\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.105\/32":[
+ {
+ "prefix":"2.1.3.105\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.106\/32":[
+ {
+ "prefix":"2.1.3.106\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf
new file mode 100644
index 0000000000..786be19ad4
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/zebra.conf
@@ -0,0 +1,2 @@
+int r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
new file mode 100755
index 0000000000..7b692c75ab
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_netlink.py
+#
+# Copyright (c) 2020 by
+#
+# 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 NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
+#
+
+"""
+test_zebra_netlink.py: Test some basic interactions with kernel using Netlink
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import shutdown_bringup_interface
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+class ZebraTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ tgen.add_router("r1")
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(ZebraTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_zebra_netlink_batching():
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ logger.info(
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ )
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ ptyest.skip("skipped because of preview test failure")
+ r1 = tgen.gears["r1"]
+
+ # Reduce the size of the buffer to hit the limit.
+ r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256")
+
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
+ json_file = "{}/r1/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,)
+ _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ assertmsg = '"r1" JSON output mismatches'
+ assert result is None, assertmsg
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 100")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/cocci.h b/tools/cocci.h
index 8ca42b349f..7d6bb4cd7f 100644
--- a/tools/cocci.h
+++ b/tools/cocci.h
@@ -7,6 +7,18 @@
#define DEFUN_HIDDEN(funcname, cmdname, str, help) \
static int funcname(const struct cmd_element *self, struct vty *vty, \
int argc, struct cmd_token *argv[])
+#define DEFUN_NOSH(funcname, cmdname, str, help) \
+ static int funcname(const struct cmd_element *self, struct vty *vty, \
+ int argc, struct cmd_token *argv[])
+#define DEFPY(funcname, cmdname, str, help) \
+ static int funcname(const struct cmd_element *self, struct vty *vty, \
+ int argc, struct cmd_token *argv[])
+#define DEFPY_HIDDEN(funcname, cmdname, str, help) \
+ static int funcname(const struct cmd_element *self, struct vty *vty, \
+ int argc, struct cmd_token *argv[])
+#define DEFPY_NOSH(funcname, cmdname, str, help) \
+ static int funcname(const struct cmd_element *self, struct vty *vty, \
+ int argc, struct cmd_token *argv[])
#define ENABLE_BGP_VNC 1
#define ALL_LIST_ELEMENTS_RO(list, node, data) \
@@ -85,3 +97,28 @@
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
#define FOREACH_SAFI(safi) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+
+#define frr_with_privs(p) \
+ for (int x = 1; x; x--)
+#define frr_with_mutex(m) \
+ for (int x = 1; x; x--)
+
+#define ALL_LSDB_TYPED_ADVRTR(lsdb, type, adv_router, lsa) \
+ const struct route_node *iterend = \
+ ospf6_lsdb_head(lsdb, 2, type, adv_router, &lsa); \
+ lsa; \
+ lsa = ospf6_lsdb_next(iterend, lsa)
+
+#define ALL_LSDB_TYPED(lsdb, type, lsa) \
+ const struct route_node *iterend = \
+ ospf6_lsdb_head(lsdb, 1, type, 0, &lsa); \
+ lsa; \
+ lsa = ospf6_lsdb_next(iterend, lsa)
+
+#define ALL_LSDB(lsdb, lsa) \
+ const struct route_node *iterend = \
+ ospf6_lsdb_head(lsdb, 0, 0, 0, &lsa); \
+ lsa; \
+ lsa = ospf6_lsdb_next(iterend, lsa)
+
+#define QOBJ_FIELDS struct qobj_node qobj_node;
diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons
index 8bec3c5bb6..0221b0c19e 100644
--- a/tools/etc/frr/daemons
+++ b/tools/etc/frr/daemons
@@ -72,6 +72,11 @@ vrrpd_options=" -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
#watchfrr_options=""
+# To make watchfrr create/join the specified netns, use the following option:
+#watchfrr_options="--netns"
+# This only has an effect in /etc/frr/<somename>/daemons, and you need to
+# start FRR with "/usr/lib/frr/frrinit.sh start <somename>".
+
# for debugging purposes, you can specify a "wrap" command to start instead
# of starting the daemon directly, e.g. to use valgrind on ospfd:
# ospfd_wrap="/usr/bin/valgrind"
diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index 8845df5fc7..11f88e7101 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -38,8 +38,6 @@ PROC_NAME:zebra
CMD_LIST_START
show zebra
show zebra client summary
-show ip zebra route dump json
-show ipv6 zebra route dump json
show ip nht vrf all
show route-map
show memory
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 25922e3bf7..a72e5c2772 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -63,14 +63,17 @@ class VtyshException(Exception):
pass
class Vtysh(object):
- def __init__(self, bindir=None, confdir=None, sockdir=None):
+ def __init__(self, bindir=None, confdir=None, sockdir=None, pathspace=None):
self.bindir = bindir
self.confdir = confdir
+ self.pathspace = pathspace
self.common_args = [os.path.join(bindir or '', 'vtysh')]
if confdir:
self.common_args.extend(['--config_dir', confdir])
if sockdir:
self.common_args.extend(['--vty_socket', sockdir])
+ if pathspace:
+ self.common_args.extend(['-N', pathspace])
def _call(self, args, stdin=None, stdout=None, stderr=None):
kwargs = {}
@@ -728,6 +731,36 @@ def line_exist(lines, target_ctx_keys, target_line, exact_match=True):
return True
return False
+def check_for_exit_vrf(lines_to_add, lines_to_del):
+
+ # exit-vrf is a bit tricky. If the new config is missing it but we
+ # have configs under a vrf, we need to add it at the end to do the
+ # right context changes. If exit-vrf exists in both the running and
+ # new config, we cannot delete it or it will break context changes.
+ add_exit_vrf = False
+ index = 0
+
+ for (ctx_keys, line) in lines_to_add:
+ if add_exit_vrf == True:
+ if ctx_keys[0] != prior_ctx_key:
+ insert_key=(prior_ctx_key),
+ lines_to_add.insert(index, ((insert_key, "exit-vrf")))
+ add_exit_vrf = False
+
+ if ctx_keys[0].startswith('vrf') and line:
+ if line is not "exit-vrf":
+ add_exit_vrf = True
+ prior_ctx_key = (ctx_keys[0])
+ else:
+ add_exit_vrf = False
+ index+=1
+
+ for (ctx_keys, line) in lines_to_del:
+ if line == "exit-vrf":
+ if (line_exist(lines_to_add, ctx_keys, line)):
+ lines_to_del.remove((ctx_keys, line))
+
+ return (lines_to_add, lines_to_del)
def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
@@ -1155,6 +1188,7 @@ def compare_context_objects(newconf, running):
for line in newconf_ctx.lines:
lines_to_add.append((newconf_ctx_keys, line))
+ (lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_unconfigurable_lines(lines_to_add, lines_to_del)
@@ -1174,6 +1208,7 @@ if __name__ == '__main__':
level_group.add_argument('--log-level', help='Log level', default="info",
choices=("critical", "error", "warning", "info", "debug"))
parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False)
+ parser.add_argument('--pathspace', '-N', metavar='NAME', help='Reload specified path/namespace', default=None)
parser.add_argument('filename', help='Location of new frr config file')
parser.add_argument('--overwrite', action='store_true', help='Overwrite frr.conf with running config output', default=False)
parser.add_argument('--bindir', help='path to the vtysh executable', default='/usr/bin')
@@ -1251,10 +1286,13 @@ if __name__ == '__main__':
log.error("Daemon %s is not a valid option for 'show running-config'" % args.daemon)
sys.exit(1)
- vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket)
+ vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace)
# Verify that 'service integrated-vtysh-config' is configured
- vtysh_filename = args.confdir + '/vtysh.conf'
+ if args.pathspace:
+ vtysh_filename = args.confdir + '/' + args.pathspace + '/vtysh.conf'
+ else:
+ vtysh_filename = args.confdir + '/vtysh.conf'
service_integrated_vtysh_config = True
if os.path.isfile(vtysh_filename):
diff --git a/tools/frr.in b/tools/frr.in
index 40862aa4c9..b860797d5b 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -77,7 +77,7 @@ vtysh_b ()
{
# Rember, that all variables have been incremented by 1 in convert_daemon_prios()
if [ "$vtysh_enable" = 2 -a -f $C_PATH/frr.conf ]; then
- $VTYSH -b -n
+ $VTYSH -b
fi
}
diff --git a/tools/frr@.service b/tools/frr@.service
new file mode 100644
index 0000000000..0fa41c74a3
--- /dev/null
+++ b/tools/frr@.service
@@ -0,0 +1,25 @@
+[Unit]
+Description=FRRouting
+Documentation=https://frrouting.readthedocs.io/en/latest/setup.html
+Wants=network.target
+After=network-pre.target systemd-sysctl.service
+Before=network.target
+OnFailure=heartbeat-failed@%n.service
+
+[Service]
+Nice=-5
+Type=forking
+NotifyAccess=all
+StartLimitInterval=3m
+StartLimitBurst=3
+TimeoutSec=2m
+WatchdogSec=60s
+RestartSec=5
+Restart=on-abnormal
+LimitNOFILE=1024
+ExecStart=/usr/lib/frr/frrinit.sh start %I
+ExecStop=/usr/lib/frr/frrinit.sh stop %I
+ExecReload=/usr/lib/frr/frrinit.sh reload %I
+
+[Install]
+WantedBy=multi-user.target
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 2955f74ce3..9a144b2b06 100644
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -16,10 +16,14 @@
#
# This script should be installed in @CFG_SBIN@/frrcommon.sh
+# FRR_PATHSPACE is passed in from watchfrr
+suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}"
+nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}"
+
PATH=/bin:/usr/bin:/sbin:/usr/sbin
D_PATH="@CFG_SBIN@" # /usr/lib/frr
-C_PATH="@CFG_SYSCONF@" # /etc/frr
-V_PATH="@CFG_STATE@" # /var/run/frr
+C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr
+V_PATH="@CFG_STATE@${suffix}" # /var/run/frr
VTYSH="@vtysh_bin@" # /usr/bin/vtysh
FRR_USER="@enable_user@" # frr
FRR_GROUP="@enable_group@" # frr
@@ -61,9 +65,9 @@ vtysh_b () {
[ "$1" = "watchfrr" ] && return 0
[ -r "$C_PATH/frr.conf" ] || return 0
if [ -n "$1" ]; then
- "$VTYSH" -b -n -d "$1"
+ "$VTYSH" `echo $nsopt` -b -d "$1"
else
- "$VTYSH" -b -n
+ "$VTYSH" `echo $nsopt` -b
fi
}
@@ -156,7 +160,7 @@ daemon_start() {
instopt="${inst:+-n $inst}"
eval args="\$${daemon}_options"
- if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then
+ if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then
log_success_msg "Started $dmninst"
vtysh_b "$daemon"
else
@@ -292,9 +296,11 @@ load_old_config() {
}
. "$C_PATH/daemons"
-load_old_config "$C_PATH/daemons.conf"
-load_old_config "/etc/default/frr"
-load_old_config "/etc/sysconfig/frr"
+if [ -z "$FRR_PATHSPACE" ]; then
+ load_old_config "$C_PATH/daemons.conf"
+ load_old_config "/etc/default/frr"
+ load_old_config "/etc/sysconfig/frr"
+fi
if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then
log_warning_msg "watchfrr_options contains a bash array value." \
diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in
index 423d6b9b1d..539ab7d816 100644
--- a/tools/frrinit.sh.in
+++ b/tools/frrinit.sh.in
@@ -30,6 +30,9 @@ else
}
fi
+# "/usr/lib/frr/frrinit.sh start somenamespace"
+FRR_PATHSPACE="$2"
+
self="`dirname $0`"
if [ -r "$self/frrcommon.sh" ]; then
. "$self/frrcommon.sh"
@@ -105,7 +108,7 @@ reload)
NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}"
[ ! -r $NEW_CONFIG_FILE ] && log_failure_msg "Unable to read new configuration file $NEW_CONFIG_FILE" && exit 1
- "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE"
+ "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE" `echo $nsopt`
exit $?
;;
diff --git a/tools/gcc-plugins/README.md b/tools/gcc-plugins/README.md
index 94a9635e76..ab31d0e636 100644
--- a/tools/gcc-plugins/README.md
+++ b/tools/gcc-plugins/README.md
@@ -20,11 +20,14 @@ Can be found at [https://deb.nox.tf/devel/].
GCC requirements
----------------
-To use this plugin, you need a **patched 9.3.0** version of GCC using the
-[gcc-retain-typeinfo.patch] provided in this repo. Without this patch, GCC
-strips type information too early during compilation, leaving to the plugin
-being unable to perform more meaningful type checks. (Specifically, all
-`typedef` types will be "cooked down" to their final type.)
+To use this plugin, you need a **patched 9.3.0** or a **patched 10.1.0**
+version of GCC using the [gcc-retain-typeinfo.patch] provided in this repo.
+
+Without this patch, GCC strips type information too early during compilation,
+leaving to the plugin being unable to perform more meaningful type checks.
+(Specifically, all `typedef` casts will be "cooked down" to their final type.)
+If the patch is missing, `format-test.c` will show 4 false negative/positive
+warnings marked with `(need retain-typeinfo patch)`.
(@eqvinox has discussed this one-line diff with some GCC people on their
IRC channel around mid 2019, the consensus was that the line is an "early
@@ -32,7 +35,7 @@ optimization" and removing it should not be harmful. However, doing so is
likely to break GCC's unit tests since warnings would print different types.)
Other versions of gcc are not supported. gcc 8 previously did work but isn't
-actively tested/maintained. gcc 10 is not supported yet but may work.
+actively tested/maintained.
Usage
diff --git a/tools/gcc-plugins/debian/changelog b/tools/gcc-plugins/debian/changelog
index 62bbbcd46f..a772803b1c 100644
--- a/tools/gcc-plugins/debian/changelog
+++ b/tools/gcc-plugins/debian/changelog
@@ -1,3 +1,9 @@
+gcc-frr-plugin (9.3.0d15+equi1) unstable; urgency=medium
+
+ * update & rebuild for gcc 9.3.0-15+equi1
+
+ -- David Lamparter <equinox-debian@diac24.net> Tue, 14 Jul 2020 19:49:24 +0200
+
gcc-frr-plugin (9.3.0d8+equi2) unstable; urgency=medium
* package created (+equi1 used during development, never released.)
diff --git a/tools/gcc-plugins/debian/control b/tools/gcc-plugins/debian/control
index 6a9b886bef..b9b5134b46 100644
--- a/tools/gcc-plugins/debian/control
+++ b/tools/gcc-plugins/debian/control
@@ -3,7 +3,7 @@ Section: devel
Priority: optional
Maintainer: David Lamparter <equinox-debian@diac24.net>
Build-Depends:
- gcc-9-plugin-dev (=9.3.0-8+equi1),
+ gcc-9-plugin-dev (=9.3.0-15+equi1),
debhelper (>= 12)
Standards-Version: 4.4.1
Homepage: https://www.frrouting.org/
@@ -13,7 +13,7 @@ Vcs-Git: https://github.com/FRRouting/frr.git
Package: gcc-9-frr-plugin
Architecture: linux-any
Depends:
- gcc-9 (=9.3.0-8+equi1),
+ gcc-9 (=9.3.0-15+equi1),
${misc:Depends},
${shlibs:Depends}
Description: GCC plugin for FRRouting
diff --git a/tools/gcc-plugins/debian/source/format b/tools/gcc-plugins/debian/source/format
index af745b310b..89ae9db8f8 100644
--- a/tools/gcc-plugins/debian/source/format
+++ b/tools/gcc-plugins/debian/source/format
@@ -1 +1 @@
-3.0 (git)
+3.0 (native)
diff --git a/tools/gcc-plugins/format-test.c b/tools/gcc-plugins/format-test.c
index b031ca5ece..fb7e41c7be 100644
--- a/tools/gcc-plugins/format-test.c
+++ b/tools/gcc-plugins/format-test.c
@@ -82,6 +82,12 @@ int test(unsigned long long ay)
testfn("%Ld", v_pid_t); // WARN
testfn("%Ld", v_uint64_t); // NOWARN
+ /* retain-typeinfo patch */
+ testfn("%zu", (size_t)v_pid_t); // NOWARN (need retain-typeinfo patch)
+ testfn("%lu", (size_t)v_pid_t); // WARN (need retain-typeinfo patch)
+ testfn("%Lu", (uint64_t)v_pid_t); // NOWARN (need retain-typeinfo patch)
+ testfn("%lu", (uint64_t)v_pid_t); // WARN (need retain-typeinfo patch)
+
testfn("%pI4", &v_long); // WARN
in_addr_t v_in_addr_t;
diff --git a/tools/gcc-plugins/format-test.py b/tools/gcc-plugins/format-test.py
index cc6ca6100e..df2437d5bc 100644
--- a/tools/gcc-plugins/format-test.py
+++ b/tools/gcc-plugins/format-test.py
@@ -10,6 +10,10 @@ for k in list(os.environ.keys()):
if k.startswith('LC_'):
os.environ.pop(k)
+if len(sys.argv) < 2:
+ sys.stderr.write('start as format-test.py gcc-123.45 [-options ...]\n')
+ sys.exit(1)
+
c_re = re.compile(r'//\s+(NO)?WARN')
expect = {}
lines = {}
@@ -25,9 +29,9 @@ with open('format-test.c', 'r') as fd:
else:
expect[lno] = 'nowarn'
-cmd = shlex.split('gcc -Wall -Wextra -Wno-unused -fplugin=./frr-format.so -fno-diagnostics-show-caret -c -o format-test.o format-test.c')
+cmd = shlex.split('-Wall -Wextra -Wno-unused -fplugin=./frr-format.so -fno-diagnostics-show-caret -c -o format-test.o format-test.c')
-gcc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+gcc = subprocess.Popen(sys.argv[1:] + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
sout, serr = gcc.communicate()
gcc.wait()
diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c
index 174f403d48..be56517171 100644
--- a/tools/gcc-plugins/frr-format.c
+++ b/tools/gcc-plugins/frr-format.c
@@ -268,8 +268,7 @@ check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num,
/* We expect a string object type as the format arg. */
if (is_char_ref)
{
- error ("format argument should be a %qs reference but"
- " a string was found", format_name (expected_format_type));
+ error ("format argument should be a %qs reference but a string was found", format_name (expected_format_type));
*no_add_attrs = true;
return false;
}
@@ -360,7 +359,7 @@ decode_format_attr (tree args, function_format_info *info, int validated_p)
if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
{
gcc_assert (!validated_p);
- error ("format string argument follows the args to be formatted");
+ error ("format string argument follows the arguments to be formatted");
return false;
}
@@ -490,10 +489,10 @@ static const format_flag_pair printf_flag_pairs[] =
#define ETAB_SZ 128
static kernel_ext_fmt ext_p[ETAB_SZ] = {
- { NULL }
+ { }
};
static kernel_ext_fmt ext_d[ETAB_SZ] = {
- { NULL }
+ { }
};
static const format_char_info print_char_table[] =
@@ -741,8 +740,8 @@ check_function_format (tree attrs, int nargs, tree *argarray,
break;
}
if (args != 0)
- warning (OPT_Wsuggest_attribute_format, "function %qD "
- "might be a candidate for %qs frr_format attribute",
+ warning (OPT_Wsuggest_attribute_format,
+ "function %qD might be a candidate for %qs %<frr_format%> attribute",
current_function_decl,
format_types[info.format_type].name);
}
@@ -924,7 +923,7 @@ avoid_dollar_number (const char *format)
format++;
if (*format == '$')
{
- warning (OPT_Wformat_, "$ operand number used after format without operand number");
+ warning (OPT_Wformat_, "%<$%> operand number used after format without operand number");
return true;
}
return false;
@@ -955,7 +954,7 @@ finish_dollar_format_checking (format_check_results *res, int pointer_gap_ok)
found_pointer_gap = true;
else
warning_at (res->format_string_loc, OPT_Wformat_,
- "format argument %d unused before used argument %d in $-style format",
+ "format argument %d unused before used argument %d in %<$%>-style format",
i + 1, dollar_max_arg_used);
}
}
@@ -1099,7 +1098,7 @@ check_format_info (function_format_info *info, tree params,
}
if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
&& res.number_other == 0)
- warning_at (loc, OPT_Wformat_extra_args, "unused arguments in $-style format");
+ warning_at (loc, OPT_Wformat_extra_args, "unused arguments in %<$%>-style format");
if (res.number_empty > 0 && res.number_non_literal == 0
&& res.number_other == 0)
warning_at (loc, OPT_Wformat_zero_length, "zero-length %s format string",
@@ -1534,8 +1533,7 @@ flag_chars_t::validate (const format_kind_info *fki,
: s->long_name);
if (ADJ_STD (t->std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
- "%s does not support %s with"
- " the %<%%%c%> %s format",
+ "%s does not support %s with the %<%%%c%> %s format",
C_STD_NAME (t->std), _(long_name),
format_char, fki->name);
}
@@ -2040,8 +2038,7 @@ argument_parser::find_format_char_info (char format_char)
format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
OPT_Wformat_,
- "unknown conversion type character"
- " %qc in format",
+ "unknown conversion type character %qc in format",
format_char);
return NULL;
}
@@ -2128,8 +2125,7 @@ argument_parser::give_y2k_warnings (const format_char_info *fci,
y2k_level = 2;
if (y2k_level == 3)
warning_at (format_string_loc, OPT_Wformat_y2k,
- "%<%%%c%> yields only last 2 digits of "
- "year in some locales", format_char);
+ "%<%%%c%> yields only last 2 digits of year in some locales", format_char);
else if (y2k_level == 2)
warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of year",
@@ -2188,9 +2184,7 @@ argument_parser::handle_conversions (const format_char_info *fci,
format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
OPT_Wformat_,
- "use of %qs length modifier with %qc type"
- " character has either no effect"
- " or undefined behavior",
+ "use of %qs length modifier with %qc type character has either no effect or undefined behavior",
len_modifier.chars, format_char);
/* Heuristic: skip one argument when an invalid length/type
combination is encountered. */
@@ -2249,12 +2243,10 @@ check_argument_type (const format_char_info *fci,
{
if (suppressed)
warning_at (format_string_loc, OPT_Wformat_,
- "operand number specified with "
- "suppressed assignment");
+ "operand number specified with suppressed assignment");
else
warning_at (format_string_loc, OPT_Wformat_,
- "operand number specified for format "
- "taking no argument");
+ "operand number specified for format taking no argument");
}
}
else
@@ -2576,8 +2568,7 @@ check_format_info_main (format_check_results *res,
format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
OPT_Wformat_,
- "%qc directive redundant after prior "
- "occurence of the same", format_char);
+ "%qc directive redundant after prior occurence of the same", format_char);
else if (!color_begin)
format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
@@ -2594,8 +2585,7 @@ check_format_info_main (format_check_results *res,
format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
OPT_Wformat_,
- "%qc conversion used within a quoted "
- "sequence",
+ "%qc conversion used within a quoted sequence",
format_char);
}
@@ -2821,16 +2811,14 @@ check_format_types (const substring_loc &fmt_loc,
&& i == 0
&& cur_param != 0
&& integer_zerop (cur_param))
- warning (OPT_Wformat_, "writing through null pointer "
- "(argument %d)", arg_num);
+ warning (OPT_Wformat_, "writing through null pointer (argument %d)", arg_num);
/* Check for reading through a NULL pointer. */
if (types->reading_from_flag
&& i == 0
&& cur_param != 0
&& integer_zerop (cur_param))
- warning (OPT_Wformat_, "reading through null pointer "
- "(argument %d)", arg_num);
+ warning (OPT_Wformat_, "reading through null pointer (argument %d)", arg_num);
if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
cur_param = TREE_OPERAND (cur_param, 0);
@@ -2849,8 +2837,7 @@ check_format_types (const substring_loc &fmt_loc,
&& (CONSTANT_CLASS_P (cur_param)
|| (DECL_P (cur_param)
&& TREE_READONLY (cur_param))))))
- warning (OPT_Wformat_, "writing into constant object "
- "(argument %d)", arg_num);
+ warning (OPT_Wformat_, "writing into constant object (argument %d)", arg_num);
/* If there are extra type qualifiers beyond the first
indirection, then this makes the types technically
@@ -2861,8 +2848,7 @@ check_format_types (const substring_loc &fmt_loc,
|| TYPE_VOLATILE (cur_type)
|| TYPE_ATOMIC (cur_type)
|| TYPE_RESTRICT (cur_type)))
- warning (OPT_Wformat_, "extra type qualifiers in format "
- "argument (argument %d)",
+ warning (OPT_Wformat_, "extra type qualifiers in format argument (argument %d)",
arg_num);
}
@@ -3095,8 +3081,7 @@ check_kef_type (const substring_loc &fmt_loc,
|| TYPE_VOLATILE (cur_type)
|| TYPE_ATOMIC (cur_type)
|| TYPE_RESTRICT (cur_type)))
- warning (OPT_Wformat_, "extra type qualifiers in format "
- "argument (argument %d)",
+ warning (OPT_Wformat_, "extra type qualifiers in format argument (argument %d)",
arg_num);
}
@@ -3541,17 +3526,24 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted)
/* C-specific implementation of range_label::get_text () vfunc for
range_label_for_type_mismatch. */
+#if BUILDING_GCC_VERSION >= 10000
+#define label_borrow(text) label_text::borrow(text)
+#define label_take(text) label_text::take(text)
+#else
+#define label_borrow(text) label_text((char *)text, false)
+#define label_take(text) label_text(text, true)
+#endif
label_text
frr_range_label_for_type_mismatch::get_text (unsigned /*range_idx*/) const
{
if (m_labelled_type == NULL_TREE)
- return label_text (NULL, false);
+ return label_borrow("(null tree)");
c_pretty_printer cpp;
bool quoted = false;
print_type (&cpp, m_labelled_type, &quoted);
- return label_text (xstrdup (pp_formatted_text (&cpp)), true);
+ return label_take(xstrdup (pp_formatted_text (&cpp)));
}
#define range_label_for_type_mismatch frr_range_label_for_type_mismatch
@@ -3583,7 +3575,7 @@ class range_label_for_format_type_mismatch
char *result = concat (text.m_buffer, p, NULL);
text.maybe_free ();
- return label_text (result, true);
+ return label_take(result);
}
private:
@@ -3695,8 +3687,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
format_warning_at_substring
(fmt_loc, &fmt_label, param_loc, &param_label,
corrected_substring, OPT_Wformat_,
- "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
- "but argument %d has type %qT%s",
+ "%s %<%s%.*s%> expects argument of type %<%s%s%>, but argument %d has type %qT%s",
gettext (kind_descriptions[kind]),
(kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start,
@@ -3716,8 +3707,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
format_warning_at_substring
(fmt_loc, &fmt_label, param_loc, &param_label,
corrected_substring, OPT_Wformat_,
- "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
- "but argument %d has type %qT%s",
+ "%s %<%s%.*s%> expects argument of type %<%T%s%>, but argument %d has type %qT%s",
gettext (kind_descriptions[kind]),
(kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start,
@@ -3875,7 +3865,7 @@ handle_frr_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
if (arg_num != info.first_arg_num)
{
if (!(flags & (int) ATTR_FLAG_BUILT_IN))
- error ("args to be formatted is not %<...%>");
+ error ("arguments to be formatted is not %<...%>");
*no_add_attrs = true;
return NULL_TREE;
}
@@ -4149,7 +4139,7 @@ setup_type (const char *name, tree *dst)
tmp = identifier_global_value (*dst);
if (tmp && TREE_CODE (tmp) != TYPE_DECL)
{
- warning (0, "%<%s%> is not defined as a type", name);
+ warning (0, "%qs is not defined as a type", name);
*dst = NULL;
return;
}
@@ -4316,7 +4306,7 @@ handle_pragma_printfrr_ext (cpp_reader *dummy)
if (0)
{
warning_at (loc, OPT_Wformat_,
- "%<#pragma FRR printfrr_ext%>: duplicate printf format suffix \"%s\"", s);
+ "%<#pragma FRR printfrr_ext%>: duplicate printf format suffix %qs", s);
warning_at (etab->origin_loc, OPT_Wformat_,
"%<#pragma FRR printfrr_ext%>: previous definition was here");
return;
@@ -4328,9 +4318,9 @@ handle_pragma_printfrr_ext (cpp_reader *dummy)
if (!strncmp(s + 2, etab->suffix, MIN(strlen(s + 2), strlen(etab->suffix))))
{
warning_at (loc, OPT_Wformat_,
- "%<#pragma FRR printfrr_ext%>: overlapping printf format suffix \"%s\"", s);
+ "%<#pragma FRR printfrr_ext%>: overlapping printf format suffix %qs", s);
warning_at (etab->origin_loc, OPT_Wformat_,
- "%<#pragma FRR printfrr_ext%>: previous definition for \"%%%c%s\" was here", s[1], etab->suffix);
+ "%<#pragma FRR printfrr_ext%>: previous definition for %<%%%c%s%> was here", s[1], etab->suffix);
return;
}
}
@@ -4388,7 +4378,7 @@ handle_pragma_printfrr_ext (cpp_reader *dummy)
{
switch (ttype) {
case CPP_NAME:
- error_at (loc, "%<#pragma FRR printfrr_ext%>: unexpected identifier. Note the only supported qualifier is \"const\".");
+ error_at (loc, "%<#pragma FRR printfrr_ext%>: unexpected identifier. Note the only supported qualifier is %<const%>");
goto out_drop;
case CPP_MULT:
diff --git a/tools/gcc-plugins/gcc-common.h b/tools/gcc-plugins/gcc-common.h
index 6b6c17231a..ec45de1a53 100644
--- a/tools/gcc-plugins/gcc-common.h
+++ b/tools/gcc-plugins/gcc-common.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/* FRR: imported from Linux kernel on 2019-07-29 */
+/* FRR: imported from Linux kernel on 2020-07-14 */
#ifndef GCC_COMMON_H_INCLUDED
#define GCC_COMMON_H_INCLUDED
@@ -38,7 +38,9 @@
#include "ggc.h"
#include "timevar.h"
+#if BUILDING_GCC_VERSION < 10000
#include "params.h"
+#endif
#if BUILDING_GCC_VERSION <= 4009
#include "pointer-set.h"
@@ -852,6 +854,7 @@ static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree l
return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT);
}
+#if BUILDING_GCC_VERSION < 10000
template <>
template <>
inline bool is_a_helper<const ggoto *>::test(const_gimple gs)
@@ -865,6 +868,7 @@ inline bool is_a_helper<const greturn *>::test(const_gimple gs)
{
return gs->code == GIMPLE_RETURN;
}
+#endif
static inline gasm *as_a_gasm(gimple stmt)
{
diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c
index 8dccbac3ae..eaab932228 100644
--- a/tools/gen_northbound_callbacks.c
+++ b/tools/gen_northbound_callbacks.c
@@ -194,7 +194,7 @@ static void generate_callback(const struct nb_callback_info *ncinfo,
case NB_OP_MODIFY:
case NB_OP_DESTROY:
case NB_OP_MOVE:
- printf("\tswitch (event) {\n"
+ printf("\tswitch (args->event) {\n"
"\tcase NB_EV_VALIDATE:\n"
"\tcase NB_EV_PREPARE:\n"
"\tcase NB_EV_ABORT:\n"
diff --git a/tools/start-stop-daemon.c b/tools/start-stop-daemon.c
index 7ad2a84500..0e4cf271f3 100644
--- a/tools/start-stop-daemon.c
+++ b/tools/start-stop-daemon.c
@@ -410,8 +410,7 @@ static void parse_schedule_item(const char *string, struct schedule_item *item)
item->type = sched_signal;
} else {
badusage(
- "invalid schedule item (must be [-]<signal-name>, "
- "-<signal-number>, <timeout> or `forever'");
+ "invalid schedule item (must be [-]<signal-name>, -<signal-number>, <timeout> or `forever'");
}
}
@@ -436,8 +435,7 @@ static void parse_schedule(const char *schedule_str)
parse_schedule_item(schedule_str, &schedule[1]);
if (schedule[1].type != sched_timeout) {
badusage(
- "--retry takes timeout, or schedule list"
- " of at least two items");
+ "--retry takes timeout, or schedule list of at least two items");
}
schedule[2].type = sched_signal;
schedule[2].value = SIGKILL;
@@ -451,8 +449,7 @@ static void parse_schedule(const char *schedule_str)
: (ptrdiff_t)strlen(schedule_str);
if (str_len >= (ptrdiff_t)sizeof(item_buf))
badusage(
- "invalid schedule item: far too long"
- " (you must delimit items with slashes)");
+ "invalid schedule item: far too long (you must delimit items with slashes)");
memcpy(item_buf, schedule_str, str_len);
item_buf[str_len] = 0;
schedule_str = slash ? slash + 1 : NULL;
@@ -461,8 +458,7 @@ static void parse_schedule(const char *schedule_str)
if (schedule[count].type == sched_forever) {
if (repeatat >= 0)
badusage(
- "invalid schedule: `forever'"
- " appears more than once");
+ "invalid schedule: `forever' appears more than once");
repeatat = count;
continue;
}
@@ -574,8 +570,7 @@ static void parse_options(int argc, char *const *argv)
if (signal_str != NULL) {
if (parse_signal(signal_str, &signal_nr) != 0)
badusage(
- "signal value must be numeric or name"
- " of signal (KILL, INTR, ...)");
+ "signal value must be numeric or name of signal (KILL, INTR, ...)");
}
if (schedule_str != NULL) {
diff --git a/tools/stringmangle.py b/tools/stringmangle.py
new file mode 100644
index 0000000000..a2eb37336a
--- /dev/null
+++ b/tools/stringmangle.py
@@ -0,0 +1,59 @@
+# 2020 by David Lamparter, placed in the public domain.
+
+import sys
+import os
+import re
+import argparse
+
+wrap_res = [
+ (re.compile(r'(?<!\\n)"\s*\n\s*"', re.M), r''),
+]
+pri_res = [
+ (re.compile(r'(PRI[udx][0-9]+)\s*\n\s*"', re.M), r'\1"'),
+ (re.compile(r'"\s*PRI([udx])32\s*"'), r'\1'),
+ (re.compile(r'"\s*PRI([udx])32'), r'\1"'),
+ (re.compile(r'"\s*PRI([udx])16\s*"'), r'h\1'),
+ (re.compile(r'"\s*PRI([udx])16'), r'h\1"'),
+ (re.compile(r'"\s*PRI([udx])8\s*"'), r'hh\1'),
+ (re.compile(r'"\s*PRI([udx])8'), r'hh\1"'),
+]
+
+def main():
+ argp = argparse.ArgumentParser(description = 'C string mangler')
+ argp.add_argument('--unwrap', action = 'store_const', const = True)
+ argp.add_argument('--pri8-16-32', action = 'store_const', const = True)
+ argp.add_argument('files', type = str, nargs = '+')
+ args = argp.parse_args()
+
+ regexes = []
+ if args.unwrap:
+ regexes.extend(wrap_res)
+ if args.pri8_16_32:
+ regexes.extend(pri_res)
+ if len(regexes) == 0:
+ sys.stderr.write('no action selected to execute\n')
+ sys.exit(1)
+
+ l = 0
+
+ for fn in args.files:
+ sys.stderr.write(fn + '\033[K\r')
+ with open(fn, 'r') as ifd:
+ data = ifd.read()
+
+ newdata = data
+ n = 0
+ for regex, repl in regexes:
+ newdata, m = regex.subn(repl, newdata)
+ n += m
+
+ if n > 0:
+ sys.stderr.write('changed: %s\n' % fn)
+ with open(fn + '.new', 'w') as ofd:
+ ofd.write(newdata)
+ os.rename(fn + '.new', fn)
+ l += 1
+
+ sys.stderr.write('%d files changed.\n' % (l))
+
+main()
diff --git a/tools/subdir.am b/tools/subdir.am
index 723a87d100..e159d82d4c 100644
--- a/tools/subdir.am
+++ b/tools/subdir.am
@@ -48,6 +48,7 @@ EXTRA_DIST += \
tools/frr-reload \
tools/frr-reload.py \
tools/frr.service \
+ tools/frr@.service \
tools/generate_support_bundle.py \
tools/multiple-bgpd.sh \
tools/rrcheck.pl \
diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c
index 3ef9fd90aa..d3f9b0c730 100644
--- a/vrrpd/vrrp.c
+++ b/vrrpd/vrrp.c
@@ -867,8 +867,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
if (pkt->hdr.vrid != r->vr->vrid) {
DEBUGD(&vrrp_dbg_proto,
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Datagram invalid: Advertisement contains VRID %" PRIu8
- " which does not match our instance",
+ "Datagram invalid: Advertisement contains VRID %hhu which does not match our instance",
r->vr->vrid, family2str(r->family), pkt->hdr.vrid);
return -1;
}
@@ -888,8 +887,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
if (r->vr->version == 2 && !adveq) {
DEBUGD(&vrrp_dbg_proto,
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Datagram invalid: Received advertisement with advertisement interval %" PRIu8
- " unequal to our configured value %u",
+ "Datagram invalid: Received advertisement with advertisement interval %hhu unequal to our configured value %u",
r->vr->vrid, family2str(r->family),
pkt->hdr.v2.adver_int,
MAX(r->vr->advertisement_interval / 100, 1));
@@ -901,8 +899,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
if (pkt->hdr.naddr != r->addrs->count)
DEBUGD(&vrrp_dbg_proto,
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Datagram has %" PRIu8
- " addresses, but this VRRP instance has %u",
+ "Datagram has %hhu addresses, but this VRRP instance has %u",
r->vr->vrid, family2str(r->family), pkt->hdr.naddr,
r->addrs->count);
@@ -926,8 +923,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
&& addrcmp > 0)) {
zlog_info(
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Received advertisement from %s w/ priority %" PRIu8
- "; switching to Backup",
+ "Received advertisement from %s w/ priority %hhu; switching to Backup",
r->vr->vrid, family2str(r->family), sipstr,
pkt->hdr.priority);
THREAD_OFF(r->t_adver_timer);
@@ -946,8 +942,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
/* Discard advertisement */
DEBUGD(&vrrp_dbg_proto,
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Discarding advertisement from %s (%" PRIu8
- " <= %" PRIu8 " & %s <= %s)",
+ "Discarding advertisement from %s (%hhu <= %hhu & %s <= %s)",
r->vr->vrid, family2str(r->family), sipstr,
pkt->hdr.priority, r->priority, sipstr, dipstr);
}
@@ -975,8 +970,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src,
/* Discard advertisement */
DEBUGD(&vrrp_dbg_proto,
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
- "Discarding advertisement from %s (%" PRIu8
- " < %" PRIu8 " & preempt = true)",
+ "Discarding advertisement from %s (%hhu < %hhu & preempt = true)",
r->vr->vrid, family2str(r->family), sipstr,
pkt->hdr.priority, r->priority);
}
@@ -2353,11 +2347,11 @@ int vrrp_config_write_global(struct vty *vty)
/* FIXME: needs to be udpated for full YANG conversion. */
if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes)
- vty_out(vty, "vrrp default priority %" PRIu8 "\n", vd.priority);
+ vty_out(vty, "vrrp default priority %hhu\n", vd.priority);
if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes)
vty_out(vty,
- "vrrp default advertisement-interval %" PRIu16 "\n",
+ "vrrp default advertisement-interval %u\n",
vd.advertisement_interval * CS2MS);
if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes)
@@ -2380,7 +2374,7 @@ static unsigned int vrrp_hash_key(const void *arg)
const struct vrrp_vrouter *vr = arg;
char key[IFNAMSIZ + 64];
- snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid);
+ snprintf(key, sizeof(key), "%s@%u", vr->ifp->name, vr->vrid);
return string_hash_make(key);
}
diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c
index ce274d4810..94f10757a2 100644
--- a/vrrpd/vrrp_main.c
+++ b/vrrpd/vrrp_main.c
@@ -114,6 +114,7 @@ struct quagga_signal_t vrrp_signals[] = {
static const struct frr_yang_module_info *const vrrp_yang_modules[] = {
&frr_filter_info,
+ &frr_vrf_info,
&frr_interface_info,
&frr_vrrpd_info,
};
diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c
index c11e0352de..f814963fe5 100644
--- a/vrrpd/vrrp_northbound.c
+++ b/vrrpd/vrrp_northbound.c
@@ -40,12 +40,31 @@ static int lib_interface_vrrp_vrrp_group_create(struct nb_cb_create_args *args)
uint8_t version = 3;
struct vrrp_vrouter *vr;
- if (args->event != NB_EV_APPLY)
+ vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id");
+ version = yang_dnode_get_enum(args->dnode, "./version");
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(args->dnode, NULL, false);
+ if (ifp) {
+ vr = vrrp_lookup(ifp, vrid);
+ if (vr && vr->autoconf) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "Virtual Router with ID %d already exists on interface '%s'; created by VRRP autoconfiguration",
+ vrid, ifp->name);
+ return NB_ERR_VALIDATION;
+ }
+ }
+ return NB_OK;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
return NB_OK;
+ case NB_EV_APPLY:
+ break;
+ }
ifp = nb_running_get_entry(args->dnode, NULL, true);
- vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id");
- version = yang_dnode_get_enum(args->dnode, "./version");
vr = vrrp_vrouter_create(ifp, vrid, version);
nb_running_set_entry(args->dnode, vr);
@@ -103,7 +122,7 @@ lib_interface_vrrp_vrrp_group_get_keys(struct nb_cb_get_keys_args *args)
const struct vrrp_vrouter *vr = args->list_entry;
args->keys->num = 1;
- snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%" PRIu32,
+ snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u",
vr->vrid);
return NB_OK;
diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c
index e4fee2d792..3cb13bd71b 100644
--- a/vrrpd/vrrp_packet.c
+++ b/vrrpd/vrrp_packet.c
@@ -222,13 +222,12 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
/* IP total length check */
VRRP_PKT_VCHECK(
ntohs(ip->ip_len) == read,
- "IPv4 packet length field does not match # received bytes; %" PRIu16
- "!= %zu",
+ "IPv4 packet length field does not match # received bytes; %hu!= %zu",
ntohs(ip->ip_len), read);
/* TTL check */
VRRP_PKT_VCHECK(ip->ip_ttl == 255,
- "IPv4 TTL is %" PRIu8 "; should be 255",
+ "IPv4 TTL is %hhu; should be 255",
ip->ip_ttl);
*pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
@@ -256,7 +255,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
uint8_t *hoplimit = CMSG_DATA(c);
VRRP_PKT_VCHECK(*hoplimit == 255,
- "IPv6 Hop Limit is %" PRIu8 "; should be 255",
+ "IPv6 Hop Limit is %hhu; should be 255",
*hoplimit);
*pkt = (struct vrrp_pkt *)buf;
@@ -293,11 +292,11 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
- "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "",
+ "Bad VRRP checksum %hx; should be %hx",
(*pkt)->hdr.chksum, chksum);
/* Type check */
- VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8,
+ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
(*pkt)->hdr.vertype & 0x0f);
/* Exact size check */
@@ -309,7 +308,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
/* auth type check */
if (version == 2)
VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
- "Bad authentication type %" PRIu8,
+ "Bad authentication type %hhu",
(*pkt)->hdr.v2.auth_type);
/* Addresses check */
@@ -318,7 +317,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
- "Bad IP address, #%" PRIu8, i);
+ "Bad IP address, #%hhu", i);
p += addrsz;
}
diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c
index b6388cc5ba..3165ea119a 100644
--- a/vrrpd/vrrp_vty.c
+++ b/vrrpd/vrrp_vty.c
@@ -52,7 +52,7 @@
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
*/
-DEFPY(vrrp_vrid,
+DEFPY_YANG(vrrp_vrid,
vrrp_vrid_cmd,
"[no] vrrp (1-255)$vrid [version (2-3)]",
NO_STR
@@ -89,7 +89,7 @@ void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
*/
-DEFPY(vrrp_shutdown,
+DEFPY_YANG(vrrp_shutdown,
vrrp_shutdown_cmd,
"[no] vrrp (1-255)$vrid shutdown",
NO_STR
@@ -115,7 +115,7 @@ void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
*/
-DEFPY(vrrp_priority,
+DEFPY_YANG(vrrp_priority,
vrrp_priority_cmd,
"vrrp (1-255)$vrid priority (1-254)",
VRRP_STR
@@ -131,7 +131,7 @@ DEFPY(vrrp_priority,
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
*/
-DEFPY(no_vrrp_priority,
+DEFPY_YANG(no_vrrp_priority,
no_vrrp_priority_cmd,
"no vrrp (1-255)$vrid priority [(1-254)]",
NO_STR
@@ -158,7 +158,7 @@ void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
* XPath:
* /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
*/
-DEFPY(vrrp_advertisement_interval,
+DEFPY_YANG(vrrp_advertisement_interval,
vrrp_advertisement_interval_cmd,
"vrrp (1-255)$vrid advertisement-interval (10-40950)",
VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
@@ -179,7 +179,7 @@ DEFPY(vrrp_advertisement_interval,
* XPath:
* /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
*/
-DEFPY(no_vrrp_advertisement_interval,
+DEFPY_YANG(no_vrrp_advertisement_interval,
no_vrrp_advertisement_interval_cmd,
"no vrrp (1-255)$vrid advertisement-interval [(10-40950)]",
NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
@@ -205,7 +205,7 @@ void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
* XPath:
* /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
*/
-DEFPY(vrrp_ip,
+DEFPY_YANG(vrrp_ip,
vrrp_ip_cmd,
"[no] vrrp (1-255)$vrid ip A.B.C.D",
NO_STR
@@ -233,7 +233,7 @@ void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
* XPath:
* /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
*/
-DEFPY(vrrp_ip6,
+DEFPY_YANG(vrrp_ip6,
vrrp_ip6_cmd,
"[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
NO_STR
@@ -260,7 +260,7 @@ void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
/*
* XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
*/
-DEFPY(vrrp_preempt,
+DEFPY_YANG(vrrp_preempt,
vrrp_preempt_cmd,
"[no] vrrp (1-255)$vrid preempt",
NO_STR
@@ -284,7 +284,7 @@ void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
}
/* XXX: yang conversion */
-DEFPY(vrrp_autoconfigure,
+DEFPY_YANG(vrrp_autoconfigure,
vrrp_autoconfigure_cmd,
"[no] vrrp autoconfigure [version (2-3)]",
NO_STR
@@ -304,7 +304,7 @@ DEFPY(vrrp_autoconfigure,
}
/* XXX: yang conversion */
-DEFPY(vrrp_default,
+DEFPY_YANG(vrrp_default,
vrrp_default_cmd,
"[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
NO_STR
@@ -470,8 +470,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
- ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid);
- ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version);
+ ttable_add_row(tt, "%s|%u", "Virtual Router ID", vr->vrid);
+ ttable_add_row(tt, "%s|%hhu", "Protocol Version", vr->version);
ttable_add_row(tt, "%s|%s", "Autoconfigured",
vr->autoconf ? "Yes" : "No");
ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No");
@@ -492,10 +492,10 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6);
ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4);
ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6);
- ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority);
- ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)",
+ ttable_add_row(tt, "%s|%hhu", "Priority", vr->priority);
+ ttable_add_row(tt, "%s|%hhu", "Effective Priority (v4)",
vr->v4->priority);
- ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)",
+ ttable_add_row(tt, "%s|%hhu", "Effective Priority (v6)",
vr->v6->priority);
ttable_add_row(tt, "%s|%s", "Preempt Mode",
vr->preempt_mode ? "Yes" : "No");
@@ -509,21 +509,21 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
ttable_add_row(tt, "%s|%d ms",
"Master Advertisement Interval (v6)",
vr->v6->master_adver_interval * CS2MS);
- ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)",
+ ttable_add_row(tt, "%s|%u", "Advertisements Tx (v4)",
vr->v4->stats.adver_tx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)",
+ ttable_add_row(tt, "%s|%u", "Advertisements Tx (v6)",
vr->v6->stats.adver_tx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)",
+ ttable_add_row(tt, "%s|%u", "Advertisements Rx (v4)",
vr->v4->stats.adver_rx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)",
+ ttable_add_row(tt, "%s|%u", "Advertisements Rx (v6)",
vr->v6->stats.adver_rx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)",
+ ttable_add_row(tt, "%s|%u", "Gratuitous ARP Tx (v4)",
vr->v4->stats.garp_tx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)",
+ ttable_add_row(tt, "%s|%u", "Neigh. Adverts Tx (v6)",
vr->v6->stats.una_tx_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)",
+ ttable_add_row(tt, "%s|%u", "State transitions (v4)",
vr->v4->stats.trans_cnt);
- ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)",
+ ttable_add_row(tt, "%s|%u", "State transitions (v6)",
vr->v6->stats.trans_cnt);
ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)",
vr->v4->skew_time * CS2MS);
@@ -583,7 +583,7 @@ static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2)
/* clang-format off */
-DEFPY(vrrp_vrid_show,
+DEFPY_YANG(vrrp_vrid_show,
vrrp_vrid_show_cmd,
"show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]",
SHOW_STR
@@ -624,7 +624,7 @@ DEFPY(vrrp_vrid_show,
return CMD_SUCCESS;
}
-DEFPY(vrrp_vrid_show_summary,
+DEFPY_YANG(vrrp_vrid_show_summary,
vrrp_vrid_show_summary_cmd,
"show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary",
SHOW_STR
@@ -653,7 +653,7 @@ DEFPY(vrrp_vrid_show_summary,
continue;
ttable_add_row(
- tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s",
+ tt, "%s|%u|%hhu|%d|%d|%s|%s",
vr->ifp->name, vr->vrid, vr->priority,
vr->v4->addrs->count, vr->v6->addrs->count,
vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master"
@@ -674,7 +674,7 @@ DEFPY(vrrp_vrid_show_summary,
}
-DEFPY(debug_vrrp,
+DEFPY_YANG(debug_vrrp,
debug_vrrp_cmd,
"[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]",
NO_STR
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index 794e1f4c73..60c31bd847 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -40,7 +40,7 @@ sub scan_file {
$cppadd = $fabricd ? "-DFABRICD=1" : "";
- open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |");
+ open (FH, "@CPP@ -P -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |");
local $/; undef $/;
$line = <FH>;
if (!close (FH)) {
@@ -48,7 +48,7 @@ sub scan_file {
}
# ?: makes a group non-capturing
- @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|ALIAS|ALIAS_HIDDEN)\s*\(.+?\));?\s?\s?\n/sg);
+ @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|DEFUN_YANG|ALIAS|ALIAS_HIDDEN|ALIAS_YANG|DEFPY|DEFPY_HIDDEN|DEFPY_YANG)\s*\(.+?\));?\s?\s?\n/sg);
@install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg);
# DEFUN process
@@ -98,7 +98,10 @@ sub scan_file {
elsif ($file =~ /lib\/if\.c$/) {
$protocol = "VTYSH_INTERFACE";
}
- elsif ($file =~ /lib\/(filter|filter_cli|lib_vty)\.c$/) {
+ elsif ($file =~ /lib\/(filter|filter_cli)\.c$/) {
+ $protocol = "VTYSH_ACL";
+ }
+ elsif ($file =~ /lib\/lib_vty\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/agentx\.c$/) {
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 4bf62d130e..d3fc8bc338 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1481,13 +1481,6 @@ static struct cmd_node rpki_node = {
.prompt = "%s(config-rpki)# ",
};
-static struct cmd_node rpki_vrf_node = {
- .name = "rpki",
- .node = RPKI_VRF_NODE,
- .parent_node = VRF_NODE,
- .prompt = "%s(config-vrf-rpki)# ",
-};
-
#if HAVE_BFDD > 0
static struct cmd_node bfd_node = {
.name = "bfd",
@@ -1670,25 +1663,12 @@ DEFUNSH(VTYSH_BGPD, address_family_ipv6_labeled_unicast,
}
DEFUNSH(VTYSH_BGPD,
- no_rpki,
- no_rpki_cmd,
- "no rpki",
- NO_STR
- "rpki\n")
-{
- return CMD_SUCCESS;
-}
-
-DEFUNSH(VTYSH_BGPD,
rpki,
rpki_cmd,
"rpki",
"Enable rpki and enter rpki configuration mode\n")
{
- if (vty->node == CONFIG_NODE)
- vty->node = RPKI_NODE;
- else
- vty->node = RPKI_VRF_NODE;
+ vty->node = RPKI_NODE;
return CMD_SUCCESS;
}
@@ -3093,9 +3073,7 @@ DEFUN (vtysh_write_memory,
* ourselves
*/
if (!used_watchfrr) {
- printf("\nWarning: attempting direct configuration write without "
- "watchfrr.\nFile permissions and ownership may be "
- "incorrect, or write may fail.\n\n");
+ printf("\nWarning: attempting direct configuration write without watchfrr.\nFile permissions and ownership may be incorrect, or write may fail.\n\n");
ret = vtysh_write_config_integrated();
}
return ret;
@@ -3838,7 +3816,6 @@ void vtysh_init_vty(void)
install_node(&vty_node);
install_node(&rpki_node);
install_node(&bmp_node);
- install_node(&rpki_vrf_node);
#if HAVE_BFDD > 0
install_node(&bfd_node);
install_node(&bfd_peer_node);
@@ -4083,15 +4060,9 @@ void vtysh_init_vty(void)
install_element(BMP_NODE, &vtysh_end_all_cmd);
install_element(CONFIG_NODE, &rpki_cmd);
- install_element(CONFIG_NODE, &no_rpki_cmd);
- install_element(VRF_NODE, &rpki_cmd);
- install_element(VRF_NODE, &no_rpki_cmd);
install_element(RPKI_NODE, &rpki_exit_cmd);
install_element(RPKI_NODE, &rpki_quit_cmd);
install_element(RPKI_NODE, &vtysh_end_all_cmd);
- install_element(RPKI_VRF_NODE, &rpki_exit_cmd);
- install_element(RPKI_VRF_NODE, &rpki_quit_cmd);
- install_element(RPKI_VRF_NODE, &vtysh_end_all_cmd);
/* EVPN commands */
install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index 97ae8b4890..d2675a81b9 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -52,9 +52,10 @@ DECLARE_MGROUP(MVTYSH)
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD
-#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD
+#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA
+#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD
-#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD|VTYSH_BGPD
+#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD
#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD
/* Daemons who can process nexthop-group configs */
#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index ab0c2b65c3..85221b8b45 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -35,6 +35,7 @@ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line")
vector configvec;
PREDECL_LIST(config_master);
+PREDECL_HASH(config_master_hash);
struct config {
/* Configuration node name. */
@@ -51,6 +52,7 @@ struct config {
/* Node entry for the typed Red-black tree */
struct config_master_item rbt_item;
+ struct config_master_hash_item hash_item;
};
struct list *config_top;
@@ -79,29 +81,49 @@ static void config_del(struct config *config)
XFREE(MTYPE_VTYSH_CONFIG, config);
}
+static int config_cmp(const struct config *c1, const struct config *c2)
+{
+ return strcmp(c1->name, c2->name);
+}
+
+static uint32_t config_hash(const struct config *c)
+{
+ return string_hash_make(c->name);
+}
+
DECLARE_LIST(config_master, struct config, rbt_item)
+DECLARE_HASH(config_master_hash, struct config, hash_item, config_cmp,
+ config_hash)
+
+/*
+ * The config_master_head is a list for order of receipt
+ * The hash is for quick lookup under this NODE
+ */
+struct configuration {
+ struct config_master_head master;
+ struct config_master_hash_head hash_master;
+};
static struct config *config_get(int index, const char *line)
{
struct config *config, *config_loop;
- struct config_master_head *master;
+ struct configuration *configuration;
+ struct config lookup;
config = config_loop = NULL;
- master = vector_lookup_ensure(configvec, index);
+ configuration = vector_lookup_ensure(configvec, index);
- if (!master) {
- master = XMALLOC(MTYPE_VTYSH_CONFIG, sizeof(struct config_master_head));
- config_master_init(master);
- vector_set_index(configvec, index, master);
+ if (!configuration) {
+ configuration = XMALLOC(MTYPE_VTYSH_CONFIG,
+ sizeof(struct configuration));
+ config_master_init(&configuration->master);
+ config_master_hash_init(&configuration->hash_master);
+ vector_set_index(configvec, index, configuration);
}
- frr_each (config_master, master, config_loop) {
- if (strcmp(config_loop->name, line) == 0) {
- config = config_loop;
- break;
- }
- }
+ lookup.name = (char *)line;
+ config = config_master_hash_find(&configuration->hash_master, &lookup);
if (!config) {
config = config_new();
@@ -110,7 +132,8 @@ static struct config *config_get(int index, const char *line)
config->line->cmp = (int (*)(void *, void *))line_cmp;
config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
config->index = index;
- config_master_add_tail(master, config);
+ config_master_add_tail(&configuration->master, config);
+ config_master_hash_add(&configuration->hash_master, config);
}
return config;
}
@@ -265,22 +288,13 @@ void vtysh_config_parse_line(void *arg, const char *line)
config_add_line(config->line, line);
} else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) {
config_add_line_uniq_end(config->line, line);
- } else if ((strncmp(line, " rpki", strlen(" rpki")) == 0)
- && config->index == VRF_NODE) {
- config_add_line(config->line, line);
- config->index = RPKI_VRF_NODE;
} else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE
|| config->index == VTY_NODE
|| config->index == VRF_NODE
|| config->index == NH_GROUP_NODE)
config_add_line_uniq(config->line, line);
- else if (config->index == RPKI_VRF_NODE
- && strncmp(line, " exit",
- strlen(" exit")) == 0) {
- config_add_line(config->line, line);
- config->index = VRF_NODE;
- } else
+ else
config_add_line(config->line, line);
} else
config_add_line(config_top, line);
@@ -412,8 +426,6 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(MPLS_NODE, line);
else if (strncmp(line, "bfd", strlen("bfd")) == 0)
config = config_get(BFD_NODE, line);
- else if (strncmp(line, "rpki", strlen("rpki")) == 0)
- config = config_get(RPKI_NODE, line);
else {
if (strncmp(line, "log", strlen("log")) == 0
|| strncmp(line, "hostname", strlen("hostname"))
@@ -449,7 +461,7 @@ void vtysh_config_dump(void)
struct listnode *node, *nnode;
struct listnode *mnode, *mnnode;
struct config *config;
- struct config_master_head *master;
+ struct configuration *configuration;
char *line;
unsigned int i;
@@ -459,8 +471,11 @@ void vtysh_config_dump(void)
vty_out(vty, "!\n");
for (i = 0; i < vector_active(configvec); i++)
- if ((master = vector_slot(configvec, i)) != NULL) {
- while ((config = config_master_pop(master))) {
+ if ((configuration = vector_slot(configvec, i)) != NULL) {
+ while ((config = config_master_pop(
+ &configuration->master))) {
+ config_master_hash_del(
+ &configuration->hash_master, config);
/* Don't print empty sections for interface.
* Route maps on the
* other hand could have a legitimate empty
@@ -488,9 +503,10 @@ void vtysh_config_dump(void)
}
for (i = 0; i < vector_active(configvec); i++)
- if ((master = vector_slot(configvec, i)) != NULL) {
- config_master_fini(master);
- XFREE(MTYPE_VTYSH_CONFIG, master);
+ if ((configuration = vector_slot(configvec, i)) != NULL) {
+ config_master_fini(&configuration->master);
+ config_master_hash_fini(&configuration->hash_master);
+ XFREE(MTYPE_VTYSH_CONFIG, configuration);
vector_slot(configvec, i) = NULL;
}
list_delete_all_node(config_top);
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 25d1bf7db0..1ca219a26c 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -421,14 +421,12 @@ int main(int argc, char **argv, char **env)
if (markfile + writeconfig + dryrun + boot_flag > 1) {
fprintf(stderr,
- "Invalid combination of arguments. Please specify at "
- "most one of:\n\t-b, -C, -m, -w\n");
+ "Invalid combination of arguments. Please specify at most one of:\n\t-b, -C, -m, -w\n");
return 1;
}
if (inputfile && (writeconfig || boot_flag)) {
fprintf(stderr,
- "WARNING: Combinining the -f option with -b or -w is "
- "NOT SUPPORTED since its\nresults are inconsistent!\n");
+ "WARNING: Combinining the -f option with -b or -w is NOT SUPPORTED since its\nresults are inconsistent!\n");
}
snprintf(vtysh_config, sizeof(vtysh_config), "%s%s%s", sysconfdir,
diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c
index 2db612adca..d1003ad5fa 100644
--- a/watchfrr/watchfrr.c
+++ b/watchfrr/watchfrr.c
@@ -29,6 +29,7 @@
#include "lib_errors.h"
#include "zlog_targets.h"
#include "network.h"
+#include "printfrr.h"
#include <getopt.h>
#include <sys/un.h>
@@ -174,6 +175,7 @@ struct daemon {
#define OPTION_MINRESTART 2000
#define OPTION_MAXRESTART 2001
#define OPTION_DRY 2002
+#define OPTION_NETNS 2003
static const struct option longopts[] = {
{"daemon", no_argument, NULL, 'd'},
@@ -190,6 +192,9 @@ static const struct option longopts[] = {
{"max-restart-interval", required_argument, NULL, OPTION_MAXRESTART},
{"pid-file", required_argument, NULL, 'p'},
{"blank-string", required_argument, NULL, 'b'},
+#ifdef GNU_LINUX
+ {"netns", optional_argument, NULL, OPTION_NETNS},
+#endif
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}};
@@ -244,7 +249,12 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\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\
--l, --loglevel Set the logging level (default is %d).\n\
+-N, --pathspace Insert prefix into config & socket paths\n"
+#ifdef GNU_LINUX
+" --netns Create and/or use Linux network namespace. If no name is\n"
+" given, uses the value from `-N`.\n"
+#endif
+"-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\
@@ -346,8 +356,7 @@ static int restart_kill(struct thread *t_kill)
time_elapsed(&delay, &restart->time);
zlog_warn(
- "Warning: %s %s child process %d still running after "
- "%ld seconds, sending signal %d",
+ "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));
@@ -476,8 +485,7 @@ static int run_job(struct restart_info *restart, const char *cmdtype,
if (gs.loglevel > LOG_DEBUG + 1)
zlog_debug(
- "postponing %s %s: "
- "elapsed time %ld < retry interval %ld",
+ "postponing %s %s: elapsed time %ld < retry interval %ld",
cmdtype, restart->name, (long)delay.tv_sec,
restart->interval);
return -1;
@@ -646,8 +654,7 @@ static int handle_read(struct thread *t_read)
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",
+ "read returned bad echo response of %d bytes (expecting %u): %.*s",
(int)rc, (unsigned int)sizeof(resp), (int)rc, buf);
daemon_down(dmn, why);
return 0;
@@ -659,14 +666,12 @@ static int handle_read(struct thread *t_read)
if (delay.tv_sec < gs.timeout) {
dmn->state = DAEMON_UP;
zlog_warn(
- "%s state -> up : echo response received after %ld.%06ld "
- "seconds",
+ "%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",
+ "%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)
@@ -698,20 +703,18 @@ static void daemon_send_ready(int exitcode)
zlog_notice("all daemons up, doing startup-complete notify");
else if (gs.numdown < gs.numdaemons)
flog_err(EC_WATCHFRR_CONNECTION,
- "startup did not complete within timeout"
- " (%d/%d daemons running)",
+ "startup did not complete within timeout (%d/%d daemons running)",
gs.numdaemons - gs.numdown, gs.numdaemons);
else {
flog_err(EC_WATCHFRR_CONNECTION,
- "all configured daemons failed to start"
- " -- exiting watchfrr");
+ "all configured daemons failed to start -- exiting watchfrr");
exit(exitcode);
}
frr_detach();
- snprintf(started, sizeof(started), "%s%s", frr_vtydir,
+ snprintf(started, sizeof(started), "%s/%s", frr_vtydir,
"watchfrr.started");
fp = fopen(started, "w");
if (fp)
@@ -948,8 +951,7 @@ static void try_restart(struct daemon *dmn)
1);
else
zlog_debug(
- "%s: postponing restart attempt because master %s daemon "
- "not up [%s], or phased restart in progress",
+ "%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]);
return;
@@ -958,8 +960,7 @@ static void try_restart(struct daemon *dmn)
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]",
+ "postponing phased global restart: restart already in progress [%s], or outstanding child processes [%d]",
phase_str[gs.phase], gs.numpids);
return;
}
@@ -970,8 +971,7 @@ static void try_restart(struct daemon *dmn)
< gs.special->restart.interval) {
if (gs.loglevel > LOG_DEBUG + 1)
zlog_debug(
- "postponing phased global restart: "
- "elapsed time %ld < retry interval %ld",
+ "postponing phased global restart: elapsed time %ld < retry interval %ld",
(long)delay.tv_sec,
gs.special->restart.interval);
return;
@@ -987,8 +987,7 @@ static int wakeup_unresponsive(struct thread *t_wakeup)
dmn->t_wakeup = NULL;
if (dmn->state != DAEMON_UNRESPONSIVE)
flog_err(EC_WATCHFRR_CONNECTION,
- "%s: no longer unresponsive (now %s), "
- "wakeup should have been cancelled!",
+ "%s: no longer unresponsive (now %s), wakeup should have been cancelled!",
dmn->name, state_str[dmn->state]);
else {
SET_WAKEUP_UNRESPONSIVE(dmn);
@@ -1006,8 +1005,7 @@ static int wakeup_no_answer(struct thread *t_wakeup)
if (dmn->ignore_timeout)
return 0;
flog_err(EC_WATCHFRR_CONNECTION,
- "%s state -> unresponsive : no response yet to ping "
- "sent %ld seconds ago",
+ "%s state -> unresponsive : no response yet to ping sent %ld seconds ago",
dmn->name, gs.timeout);
SET_WAKEUP_UNRESPONSIVE(dmn);
try_restart(dmn);
@@ -1066,8 +1064,7 @@ void watchfrr_status(struct vty *vty)
else if (dmn->state == DAEMON_DOWN &&
time_elapsed(&delay, &dmn->restart.time)->tv_sec
< dmn->restart.interval)
- vty_out(vty, " restarting in %jd seconds"
- " (%jds backoff interval)\n",
+ vty_out(vty, " restarting in %jd seconds (%jds backoff interval)\n",
(intmax_t)dmn->restart.interval
- (intmax_t)delay.tv_sec,
(intmax_t)dmn->restart.interval);
@@ -1115,6 +1112,148 @@ static int startup_timeout(struct thread *t_wakeup)
return 0;
}
+#ifdef GNU_LINUX
+
+#include <sys/mount.h>
+#include <sched.h>
+
+#define NETNS_RUN_DIR "/var/run/netns"
+
+static void netns_create(int dirfd, const char *nsname)
+{
+ /* make /var/run/netns shared between mount namespaces
+ * just like iproute2 sets it up
+ */
+ if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
+ if (errno != EINVAL) {
+ perror("mount");
+ exit(1);
+ }
+
+ if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none",
+ MS_BIND | MS_REC, NULL)) {
+ perror("mount");
+ exit(1);
+ }
+
+ if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC,
+ NULL)) {
+ perror("mount");
+ exit(1);
+ }
+ }
+
+ /* need an empty file to mount on top of */
+ int nsfd = openat(dirfd, nsname, O_CREAT | O_RDONLY | O_EXCL, 0);
+
+ if (nsfd < 0) {
+ fprintf(stderr, "failed to create \"%s/%s\": %s\n",
+ NETNS_RUN_DIR, nsname, strerror(errno));
+ exit(1);
+ }
+ close(nsfd);
+
+ if (unshare(CLONE_NEWNET)) {
+ perror("unshare");
+ unlinkat(dirfd, nsname, 0);
+ exit(1);
+ }
+
+ char *dstpath = asprintfrr(MTYPE_TMP, "%s/%s", NETNS_RUN_DIR, nsname);
+
+ /* bind-mount so the namespace has a name and is persistent */
+ if (mount("/proc/self/ns/net", dstpath, "none", MS_BIND, NULL) < 0) {
+ fprintf(stderr, "failed to bind-mount netns to \"%s\": %s\n",
+ dstpath, strerror(errno));
+ unlinkat(dirfd, nsname, 0);
+ exit(1);
+ }
+
+ XFREE(MTYPE_TMP, dstpath);
+}
+
+static void netns_setup(const char *nsname)
+{
+ int dirfd, nsfd;
+
+ dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ if (errno == ENOTDIR) {
+ fprintf(stderr, "error: \"%s\" is not a directory!\n",
+ NETNS_RUN_DIR);
+ exit(1);
+ } else if (errno == ENOENT) {
+ if (mkdir(NETNS_RUN_DIR, 0755)) {
+ fprintf(stderr, "error: \"%s\": mkdir: %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ fprintf(stderr, "error: \"%s\": opendir: %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "error: \"%s\": %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ }
+
+ nsfd = openat(dirfd, nsname, O_RDONLY);
+ if (nsfd < 0 && errno != ENOENT) {
+ fprintf(stderr, "error: \"%s/%s\": %s\n",
+ NETNS_RUN_DIR, nsname, strerror(errno));
+ exit(1);
+ }
+ if (nsfd < 0)
+ netns_create(dirfd, nsname);
+ else {
+ if (setns(nsfd, CLONE_NEWNET)) {
+ perror("setns");
+ exit(1);
+ }
+ close(nsfd);
+ }
+ close(dirfd);
+
+ /* make sure loopback is up... weird things happen otherwise.
+ * ioctl is perfectly fine for this, don't need netlink...
+ */
+ int sockfd;
+ struct ifreq ifr = { };
+
+ strlcpy(ifr.ifr_name, "lo", sizeof(ifr.ifr_name));
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)) {
+ perror("ioctl(SIOCGIFFLAGS, \"lo\")");
+ exit(1);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr)) {
+ perror("ioctl(SIOCSIFFLAGS, \"lo\")");
+ exit(1);
+ }
+ }
+ close(sockfd);
+}
+
+#else /* !GNU_LINUX */
+
+static void netns_setup(const char *nsname)
+{
+ fprintf(stderr, "network namespaces are only available on Linux\n");
+ exit(1);
+}
+#endif
+
static void watchfrr_init(int argc, char **argv)
{
const char *special = "zebra";
@@ -1204,11 +1343,13 @@ int main(int argc, char **argv)
{
int opt;
const char *blankstr = NULL;
+ const char *netns = NULL;
+ bool netns_en = false;
frr_preinit(&watchfrr_di, argc, argv);
progname = watchfrr_di.progname;
- frr_opt_add("b:dk:l:i:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, "");
+ frr_opt_add("b:di:k:l:N:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, "");
gs.restart.name = "all";
while ((opt = frr_getopt(argc, argv, NULL)) != EOF) {
@@ -1273,6 +1414,16 @@ int main(int argc, char **argv)
frr_help_exit(1);
}
} break;
+ case OPTION_NETNS:
+ netns_en = true;
+ if (strchr(optarg, '/')) {
+ fprintf(stderr,
+ "invalid network namespace name \"%s\" (may not contain slashes)\n",
+ optarg);
+ frr_help_exit(1);
+ }
+ netns = optarg;
+ break;
case 'i': {
char garbage[3];
int period;
@@ -1364,6 +1515,17 @@ int main(int argc, char **argv)
gs.restart.interval = gs.min_restart_interval;
+ /* env variable for the processes that we start */
+ if (watchfrr_di.pathspace)
+ setenv("FRR_PATHSPACE", watchfrr_di.pathspace, 1);
+ else
+ unsetenv("FRR_PATHSPACE");
+
+ if (netns_en && !netns)
+ netns = watchfrr_di.pathspace;
+ if (netns_en && netns && netns[0])
+ netns_setup(netns);
+
master = frr_init();
watchfrr_error_init();
watchfrr_init(argc, argv);
diff --git a/yang/embedmodel.py b/yang/embedmodel.py
index 624a11da9d..0a25c93da7 100644
--- a/yang/embedmodel.py
+++ b/yang/embedmodel.py
@@ -20,6 +20,8 @@ if not os.path.isdir(outdir):
# to make it even harder.
re_name = re.compile(r'\bmodule\s+([^\s]+)\s+\{')
+re_subname = re.compile(r'\bsubmodule\s+([^\s]+)\s+\{')
+re_mainname = re.compile(r'\bbelongs-to\s+([^\s]+)\s+\{')
re_rev = re.compile(r'\brevision\s+([\d-]+)\s+\{')
@@ -34,6 +36,8 @@ static const char model[] =
static struct yang_module_embed embed = {
\t.mod_name = "%s",
\t.mod_rev = "%s",
+\t.sub_mod_name = "%s",
+\t.sub_mod_rev = "%s",
\t.data = model,
\t.format = %s,
};
@@ -62,6 +66,10 @@ def escape(line):
with open(inname, 'r') as fd:
data = fd.read()
+sub_name = ""
+rev = ""
+sub_rev = ""
+
# XML support isn't actively used currently, but it's here in case the need
# arises. It does avoid the regex'ing.
if '<?xml' in data:
@@ -71,8 +79,15 @@ if '<?xml' in data:
rev = xml.find('{urn:ietf:params:xml:ns:yang:yin:1}revision').get('date')
fmt = 'LYS_YIN'
else:
- name = re_name.search(data).group(1)
- rev = re_rev.search(data).group(1)
+ search_name = re_name.search(data)
+ if search_name :
+ name = search_name.group(1)
+ rev = re_rev.search(data).group(1)
+ else :
+ search_name = re_subname.search(data)
+ sub_name = search_name.group(1)
+ name = re_mainname.search(data).group(1)
+ sub_rev = re_rev.search(data).group(1)
fmt = 'LYS_YANG'
if name is None or rev is None:
@@ -82,4 +97,4 @@ lines = [escape(row) for row in data.split('\n')]
text = '\\n"\n\t"'.join(lines)
with open(outname, 'w') as fd:
- fd.write(template % (text, escape(name), escape(rev), fmt))
+ fd.write(template % (text, escape(name), escape(rev), escape(sub_name), escape(sub_rev), fmt))
diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang
index 13bad27b19..4b7785e690 100644
--- a/yang/frr-bfdd.yang
+++ b/yang/frr-bfdd.yang
@@ -185,6 +185,13 @@ module frr-bfdd {
default true;
description "Disables or enables the session administratively";
}
+
+ leaf passive-mode {
+ type boolean;
+ default false;
+ description
+ "Don't attempt to start session establishment.";
+ }
}
grouping session-echo {
@@ -204,6 +211,18 @@ module frr-bfdd {
}
}
+ grouping session-multi-hop {
+ description "BFD session multi hop settings.";
+
+ leaf minimum-ttl {
+ type uint8 {
+ range 1..254;
+ }
+ description
+ "Minimum expected TTL on received packets.";
+ }
+ }
+
grouping session-states {
/*
* Local settings.
@@ -363,6 +382,7 @@ module frr-bfdd {
uses session-common;
uses session-echo;
+ uses session-multi-hop;
}
container sessions {
@@ -438,6 +458,7 @@ module frr-bfdd {
}
uses session-common;
+ uses session-multi-hop;
container stats {
uses session-states;
diff --git a/yang/frr-bgp-bmp.yang b/yang/frr-bgp-bmp.yang
new file mode 100644
index 0000000000..344448f104
--- /dev/null
+++ b/yang/frr-bgp-bmp.yang
@@ -0,0 +1,203 @@
+submodule frr-bgp-bmp {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import frr-bgp-types {
+ prefix frr-bt;
+ }
+
+ include frr-bgp-common-multiprotocol;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This submodule defines a model for managing FRR BGP BMP.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping bmp-incoming-session {
+ container incoming-session {
+ list session-list {
+ key "address tcp-port";
+ leaf address {
+ type inet:ip-address;
+ description
+ "IPv4 address to listen on.";
+ }
+
+ leaf tcp-port {
+ type uint32;
+ }
+ }
+ }
+ }
+
+ grouping bmp-outgoing-session {
+ container outgoing-session {
+ list session-list {
+ key "hostname tcp-port";
+ leaf hostname {
+ type string;
+ }
+
+ leaf tcp-port {
+ type uint32;
+ }
+
+ leaf min-retry-time {
+ type uint32 {
+ range "100..86400000";
+ }
+ units "miliseconds";
+ default "30000";
+ description
+ "Minimum connection retry interval.";
+ }
+
+ leaf max-retry-time {
+ type uint32 {
+ range "100..86400000";
+ }
+ units "miliseconds";
+ default "720000";
+ description
+ "Maximum connection retry interval.";
+ }
+ }
+ }
+ }
+
+ grouping bmp-afi-safis {
+ container afi-safis {
+ description
+ "List of address-families associated with the BGP
+ instance.";
+ list afi-safi {
+ key "afi-safi-name";
+ description
+ "AFI, SAFI configuration available for the
+ neighbour or group.";
+ uses mp-afi-safi-config;
+
+ uses mp-all-afi-safi-list-contents;
+ }
+ }
+ }
+
+ grouping bmp-afi-safi-common-config {
+ container common-config {
+ leaf pre-policy {
+ type boolean;
+ default "false";
+ description
+ "Send state before policy and filter processing.";
+ }
+
+ leaf post-policy {
+ type boolean;
+ default "false";
+ description
+ "Send state after policy and filter processing.";
+ }
+ }
+ }
+
+ grouping global-bmp-config {
+ description
+ "Structural grouping used to include filter
+ configuration for BMP.";
+ container bmp-config {
+ description
+ "BMP related parameters.";
+ list target-list {
+ key "target-name";
+ leaf target-name {
+ type string;
+ description
+ "Targets group name.";
+ }
+
+ uses bmp-incoming-session;
+
+ uses bmp-outgoing-session;
+
+ leaf mirror {
+ type boolean;
+ default "false";
+ description
+ "When set to 'TRUE' it send BMP route mirroring messages.";
+ }
+
+ leaf stats-time {
+ type uint32 {
+ range "100..86400000";
+ }
+ units "miliseconds";
+ description
+ "Interval to send BMP Stats.";
+ }
+
+ leaf ipv4-access-list {
+ type frr-bt:access-list-ref;
+ description
+ "Access list to restrict BMP sessions.";
+ }
+
+ leaf ipv6-access-list {
+ type frr-bt:access-list-ref;
+ description
+ "Access list to restrict BMP sessions.";
+ }
+
+ uses bmp-afi-safis;
+ }
+
+ leaf mirror-buffer-limit {
+ type uint32 {
+ range "0..4294967294";
+ }
+ units "bytes";
+ description
+ "Maximum memory used for buffered mirroring messages.";
+ }
+ }
+ }
+}
diff --git a/yang/frr-bgp-common-multiprotocol.yang b/yang/frr-bgp-common-multiprotocol.yang
new file mode 100644
index 0000000000..aefdf02ba6
--- /dev/null
+++ b/yang/frr-bgp-common-multiprotocol.yang
@@ -0,0 +1,209 @@
+submodule frr-bgp-common-multiprotocol {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ import frr-routing {
+ prefix frr-rt;
+ }
+
+ include frr-bgp-common;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module contains general data definitions for use in BGP
+ Multiprotocol.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping mp-afi-safi-config {
+ description
+ "Configuration parameters used for all BGP AFI-SAFIs.";
+ leaf afi-safi-name {
+ type identityref {
+ base frr-rt:afi-safi-type;
+ }
+ description
+ "AFI, SAFI.";
+ }
+ }
+
+ grouping mp-all-afi-safi-list-contents {
+ description
+ "A common grouping used for contents of the list that is used
+ for AFI-SAFI entries.";
+ container ipv4-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv4-unicast')" {
+ description
+ "Include this container for IPv4 Unicast specific
+ configuration.";
+ }
+ description
+ "IPv4 unicast configuration options.";
+ }
+
+ container ipv6-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv6-unicast')" {
+ description
+ "Include this container for IPv6 Unicast specific
+ configuration.";
+ }
+ description
+ "IPv6 unicast configuration options.";
+ }
+
+ container ipv4-labeled-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv4-labeled-unicast')" {
+ description
+ "Include this container for IPv4 Labeled Unicast specific
+ configuration.";
+ }
+ description
+ "IPv4 Labeled Unicast configuration options.";
+ }
+
+ container ipv6-labeled-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv6-labeled-unicast')" {
+ description
+ "Include this container for IPv6 Labeled Unicast specific
+ configuration.";
+ }
+ description
+ "IPv6 Labeled Unicast configuration options.";
+ }
+
+ container l3vpn-ipv4-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-unicast')" {
+ description
+ "Include this container for IPv4 Unicast L3VPN specific
+ configuration.";
+ }
+ description
+ "Unicast IPv4 L3VPN configuration options.";
+ }
+
+ container l3vpn-ipv6-unicast {
+ when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-unicast')" {
+ description
+ "Include this container for unicast IPv6 L3VPN specific
+ configuration.";
+ }
+ description
+ "Unicast IPv6 L3VPN configuration options.";
+ }
+
+ container l3vpn-ipv4-multicast {
+ when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-multicast')" {
+ description
+ "Include this container for multicast IPv4 L3VPN specific
+ configuration.";
+ }
+ description
+ "Multicast IPv4 L3VPN configuration options.";
+ }
+
+ container l3vpn-ipv6-multicast {
+ when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-multicast')" {
+ description
+ "Include this container for multicast IPv6 L3VPN specific
+ configuration.";
+ }
+ description
+ "Multicast IPv6 L3VPN configuration options.";
+ }
+
+ container l2vpn-vpls {
+ when "derived-from-or-self(../afi-safi-name, 'l2vpn-vpls')" {
+ description
+ "Include this container for BGP-signalled VPLS specific
+ configuration.";
+ }
+ description
+ "BGP-signalled VPLS configuration options.";
+ }
+
+ container l2vpn-evpn {
+ when "derived-from-or-self(../afi-safi-name, 'l2vpn-evpn')" {
+ description
+ "Include this container for BGP EVPN specific
+ configuration.";
+ }
+ description
+ "BGP EVPN configuration options.";
+ }
+
+ container ipv4-multicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv4-multicast')" {
+ description
+ "Include this container for IPv4 multicast specific
+ configuration.";
+ }
+ description
+ "IPv4 multicast configuration options.";
+ }
+
+ container ipv6-multicast {
+ when "derived-from-or-self(../afi-safi-name, 'ipv6-multicast')" {
+ description
+ "Include this container for IPv6 multicast specific
+ configuration.";
+ }
+ description
+ "IPv6 multicast configuration options.";
+ }
+
+ container ipv4-flowspec {
+ when "derived-from-or-self(../afi-safi-name, 'ipv4-flowspec')" {
+ description
+ "Include this container for IPv4 flowspec specific
+ configuration.";
+ }
+ description
+ "IPv4 flowspec configuration options.";
+ }
+
+ container ipv6-flowspec {
+ when "derived-from-or-self(../afi-safi-name, 'ipv6-flowspec')" {
+ description
+ "Include this container for IPv6 flowspec specific
+ configuration.";
+ }
+ description
+ "IPv6 flowspec configuration options.";
+ }
+ }
+}
diff --git a/yang/frr-bgp-common-structure.yang b/yang/frr-bgp-common-structure.yang
new file mode 100644
index 0000000000..8162527e90
--- /dev/null
+++ b/yang/frr-bgp-common-structure.yang
@@ -0,0 +1,807 @@
+submodule frr-bgp-common-structure {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import frr-interface {
+ prefix frr-interface;
+ }
+
+ import ietf-bgp-types {
+ prefix bt;
+ }
+
+ import frr-bgp-types {
+ prefix frr-bt;
+ }
+
+ include frr-bgp-common;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This submodule contains general data definitions for use in BGP.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping structure-neighbor-group-ebgp-multihop {
+ description
+ "Structural grouping used to include EBGP multi-hop
+ configuration for both BGP neighbors and peer groups.";
+ container ebgp-multihop {
+ description
+ "EBGP multi-hop parameters for the BGP group.";
+ choice hop-count-choice {
+ case default-hop-count {
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "When enabled the referenced group or neighbors are
+ permitted to be indirectly connected - including cases
+ where the TTL can be decremented between the BGP peers.";
+ }
+ }
+
+ case max-hop-count {
+ leaf multihop-ttl {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Time-to-live value to use when packets are sent to the
+ referenced group or neighbors and ebgp-multihop is
+ enabled.";
+ }
+ }
+ }
+
+ leaf disable-connected-check {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it enforces EBGP neighbors perform multihop.";
+ }
+ }
+ }
+
+ grouping neighbor-local-as-options {
+ container local-as {
+ leaf local-as {
+ type inet:as-number;
+ mandatory true;
+ description
+ "The local autonomous system number that is to be used when
+ establishing sessions with the remote peer or peer group, if
+ this differs from the global BGP router autonomous system
+ number.";
+ }
+
+ leaf no-prepend {
+ when "../local-as != 0";
+ type boolean;
+ default "false";
+ description
+ "Do not prepend local-as to updates from EBGP peers. When
+ set to 'true' it will not prepend local-as to updates. When
+ set to 'false' it will prepend local-as to updates.";
+ }
+
+ leaf no-replace-as {
+ type boolean;
+ default "false";
+ description
+ "Do not prepend local-as to updates from IBGP peers.";
+ }
+ }
+ }
+
+ grouping neighbor-bfd-options {
+ container bfd-options {
+ leaf enable {
+ type boolean;
+ default "false";
+ description
+ "BFD support.";
+ }
+
+ leaf detect-multiplier {
+ when "../enable = 'true'";
+ type uint8 {
+ range "2..255";
+ }
+ default "3";
+ description
+ "Detect multiplier.";
+ }
+
+ leaf required-min-rx {
+ when "../enable = 'true'";
+ type uint16 {
+ range "50..60000";
+ }
+ units "miliseconds";
+ default "300";
+ description
+ "Required min receive interval.";
+ }
+
+ leaf desired-min-tx {
+ when "../enable = 'true'";
+ type uint16 {
+ range "50..60000";
+ }
+ units "miliseconds";
+ default "300";
+ description
+ "Desired min transmit interval.";
+ }
+
+ leaf session-type {
+ when "../enable = 'true'";
+ type frr-bt:bfd-session-type;
+ default "not-configured";
+ description
+ "BFD session type.";
+ }
+
+ leaf check-cp-failure {
+ when "../enable = 'true'";
+ type boolean;
+ default "false";
+ description
+ "Link dataplane status with BGP control plane.";
+ }
+ }
+ }
+
+ grouping neighbor-remote-as {
+ container neighbor-remote-as {
+ leaf remote-as-type {
+ type frr-bt:as-type;
+ mandatory true;
+ description
+ "Remote AS type.";
+ }
+
+ leaf remote-as {
+ when "../remote-as-type = 'as-specified'";
+ type inet:as-number;
+ description
+ "The remote autonomous system number received in
+ the BGP OPEN message.";
+ reference
+ "RFC 4271";
+ }
+ }
+ }
+
+ grouping neighbor-update-source {
+ description
+ "Source of routing updates.";
+ container update-source {
+ description
+ "Source of routing updates config.";
+ choice source {
+ case ip-based {
+ leaf ip {
+ type inet:ip-address;
+ description
+ "IPv4 address/IPv6 address.";
+ }
+ }
+
+ case interface-based {
+ leaf interface {
+ type frr-interface:interface-ref {
+ require-instance false;
+ }
+ description
+ "The local interface.";
+ }
+ }
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-add-paths {
+ description
+ "Structural grouping used to include ADD-PATHs configuration
+ and state for both BGP neighbors and peer groups.";
+ container add-paths {
+ description
+ "Parameters relating to the advertisement and receipt of
+ multiple paths for a single NLRI (add-paths).";
+ reference
+ "RFC 7911: ADD-PATH.";
+ leaf path-type {
+ type frr-bt:add-path-type;
+ default "none";
+ description
+ "Enable ability to receive multiple path advertisements for
+ an NLRI from the neighbor or group.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-as-path-options {
+ description
+ "Structural grouping used to include AS_PATH manipulation
+ configuration both BGP neighbors and peer groups.";
+ container as-path-options {
+ description
+ "AS_PATH manipulation parameters for the BGP neighbor or
+ group.";
+ choice allowas-in {
+ case occurence-based {
+ leaf allow-own-as {
+ type uint8 {
+ range "1..10";
+ }
+ description
+ "Specify the number of occurrences of the local BGP
+ speaker's AS that can occur within the AS_PATH before it
+ is rejected.";
+ }
+ }
+
+ case origin-based {
+ leaf allow-own-origin-as {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' only accept my AS in the as-path
+ if the route was originated in my AS.";
+ }
+ }
+ }
+
+ leaf replace-peer-as {
+ type boolean;
+ default "false";
+ description
+ "Replace occurrences of the peer's AS in the AS_PATH with
+ the local autonomous system number. This is same as override
+ ASN CLI.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-capability-options {
+ description
+ "Structural grouping used to include capability
+ configuration for both BGP neighbors and peer groups.";
+ container capability-options {
+ description
+ "Capability manipulation parameters for the BGP neighbor or
+ group.";
+ leaf dynamic-capability {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' dynamic capability is advertise to this peer.";
+ }
+
+ leaf strict-capability {
+ type boolean;
+ default "false";
+ description
+ "Strict capability negotiation match. When set to 'true'
+ remote and local capabilities are strictly compared
+ if capabilities are different, send Unsupported Capability
+ error then reset connection.";
+ }
+
+ leaf extended-nexthop-capability {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' extended next-hop capability is advertise
+ to this peer.";
+ }
+
+ leaf capability-negotiate {
+ type boolean;
+ default "true";
+ description
+ "When set to 'true' sending Capability Negotiation in the open
+ message is supressed to this peer.";
+ }
+
+ leaf override-capability {
+ type boolean;
+ default "false";
+ description
+ "Overrides the result of Capability Negotiation, ignoring remote
+ peer's capability value, when set to 'true'.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-default-originate-options {
+ description
+ "Structural grouping used to include default-originate
+ configuration for both BGP neighbors and peer groups.";
+ container default-originate-options {
+ description
+ "default originate parameters for the BGP neighbor or
+ group.";
+ leaf send-default-route {
+ type boolean;
+ default "false";
+ description
+ "If set to 'true', send the default-route to the neighbour(s).";
+ }
+
+ leaf rmap-policy-export {
+ type frr-bt:rmap-ref;
+ description
+ "Route-map to specify criteria to originate default.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-prefix-limit {
+ container prefix-limit {
+ description
+ "Parameters relating to the prefix limit for the AFI-SAFI.";
+ list direction-list {
+ key "direction";
+ leaf direction {
+ type frr-bt:direction;
+ description
+ "Prefix limit applied on Tx route-updates or Rx route-updates.";
+ }
+
+ leaf max-prefixes {
+ type uint32;
+ mandatory true;
+ description
+ "Maximum number of prefixes that will be accepted from the
+ neighbour.";
+ }
+
+ container prefix-limit-options {
+ when "../direction = 'in'";
+ choice options {
+ case warning {
+ leaf warning-only {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' only give warning message when limit
+ is exceeded.";
+ }
+ }
+
+ case restart {
+ leaf restart-timer {
+ type uint16;
+ units "minutes";
+ description
+ "Time interval in seconds after which the BGP session is
+ re-established after being torn down due to exceeding the
+ max-prefix limit.";
+ }
+ }
+
+ case threshold {
+ leaf shutdown-threshold-pct {
+ type bt:percentage;
+ description
+ "Threshold on number of prefixes that can be received from
+ a neighbour before generation of warning messages or log
+ entries. Expressed as a percentage of max-prefixes.";
+ }
+ }
+
+ case threshold-restart {
+ leaf tr-shutdown-threshold-pct {
+ type bt:percentage;
+ description
+ "Threshold on number of prefixes that can be received from
+ a neighbour before generation of warning messages or log
+ entries. Expressed as a percentage of max-prefixes.";
+ }
+
+ leaf tr-restart-timer {
+ type uint32;
+ units "minutes";
+ description
+ "Time interval in seconds after which the BGP session is
+ re-established after being torn down due to exceeding the
+ max-prefix limit.";
+ }
+ }
+
+ case threshold-warning {
+ leaf tw-shutdown-threshold-pct {
+ type bt:percentage;
+ description
+ "Threshold on number of prefixes that can be received from
+ a neighbour before generation of warning messages or log
+ entries. Expressed as a percentage of max-prefixes.";
+ }
+
+ leaf tw-warning-only {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' only give warning message when limit
+ is exceeded.";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ grouping structure-neighbor-nexthop-self {
+ container nexthop-self {
+ description
+ "Parameters relating to the nexthop-self for the AFI-SAFI.";
+ leaf next-hop-self {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', EBGP learned routes are announced with the
+ local speaker's nexthop.";
+ }
+
+ leaf next-hop-self-force {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', EBGP learned routes are announced with the
+ local speaker's nexthop.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-private-as {
+ container private-as {
+ description
+ "Parameters relating to the private-as for the AFI-SAFI.";
+ leaf remove-private-as-all {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', private ASNs are removed from outbound updates;
+ applies to all AS numbers.";
+ }
+
+ leaf remove-private-as-all-replace {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', private ASNs are replaced by the local
+ speaker's ASN in all outbound updates; applies to all AS numbers.";
+ }
+
+ leaf remove-private-as {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', removes private ASNs in outbound updates;
+ applies to all AS numbers.";
+ }
+
+ leaf remove-private-as-replace {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', private ASNs are replaced with the local
+ speaker's ASN in all outbound updates; applies to all AS numbers.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-weight {
+ container weight {
+ description
+ "Parameters relating to the weight for the AFI-SAFI.";
+ leaf weight-attribute {
+ type uint16 {
+ range "0..65535";
+ }
+ description
+ "Set default weight for routes from this neighbor.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-route-reflector {
+ container route-reflector {
+ description
+ "Parameters relating to the route-reflector for the AFI-SAFI.";
+ leaf route-reflector-client {
+ type boolean;
+ default "false";
+ description
+ "Configure a neighbor as route reflector client.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-route-server {
+ container route-server {
+ description
+ "Parameters relating to the route-server for the AFI-SAFI.";
+ leaf route-server-client {
+ type boolean;
+ default "false";
+ description
+ "Configure a neighbor as route server client.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-send-community {
+ container send-community {
+ description
+ "Parameters relating to the send-community for the AFI-SAFI.";
+ leaf send-community {
+ type boolean;
+ default "true";
+ description
+ "Send standard community attribute to this neighbor.";
+ }
+
+ leaf send-ext-community {
+ type boolean;
+ default "true";
+ description
+ "Send extended community attribute to this neighbor.";
+ }
+
+ leaf send-large-community {
+ type boolean;
+ default "false";
+ description
+ "Send large community attribute to this neighbor.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-admin-shutdown {
+ description
+ "Structural grouping used to include admin-shutdown
+ configuration for both BGP neighbors and peer groups.";
+ container admin-shutdown {
+ description
+ "BGP Administrative Shutdown Communication.";
+ leaf enable {
+ type boolean;
+ mandatory true;
+ description
+ "When set to 'true', BGP shutdown communication is enabled.";
+ }
+
+ leaf message {
+ type string;
+ description
+ "Shutdown message.";
+ reference
+ "draft-ietf-idr-shutdown-06";
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-graceful-restart {
+ description
+ "Structural grouping used to include graceful-restart
+ configuration for both BGP neighbors and peer groups.";
+ container graceful-restart {
+ description
+ "BGP Graceful restart feature.";
+ choice mode {
+ case graceful-restart-mode {
+ leaf enable {
+ type boolean;
+ default "false";
+ description
+ "Enable or disable the graceful-restart capability.
+ Setting this value to 'true' enables the graceful-restart
+ and helper both at peer level. Setting this value to 'false'
+ disables graceful restart and helper mode. The peer will inherit
+ global configuration.";
+ }
+ }
+
+ case graceful-restart-helper-mode {
+ leaf graceful-restart-helper {
+ type boolean;
+ default "false";
+ description
+ "Setting this value to 'true' enables helper mode for the peer
+ Setting this value to 'false' disables the helper mode. The
+ peer will inherit global configuration.";
+ }
+ }
+
+ case graceful-restart-disable-mode {
+ leaf graceful-restart-disable {
+ type boolean;
+ default "false";
+ description
+ "Setting this value to 'true' disables the graceful-restart
+ and helper Mode. Setting this value to 'false' causes the peer
+ to inherit global configuration.";
+ }
+ }
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-soft-reconfiguration {
+ description
+ "Structural grouping used to include soft-reconfiguration
+ configuration for both BGP neighbors and peer groups.";
+ leaf soft-reconfiguration {
+ type boolean;
+ default "false";
+ description
+ "Allow inbound soft reconfiguration for this neighbor.";
+ }
+ }
+
+ grouping structure-neighbor-group-attr-unchanged {
+ description
+ "Structural grouping used to include BGP route propagation
+ rules configuration for both BGP neighbors and peer groups.";
+ container attr-unchanged {
+ description
+ "BGP route propagation rules configuration.";
+ leaf as-path-unchanged {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' as-path attribute is propagated unchanged.";
+ }
+
+ leaf next-hop-unchanged {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' next-hop attribute is propagated unchanged.";
+ }
+
+ leaf med-unchanged {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' med attribute is propagated unchanged.";
+ }
+ }
+ }
+
+ grouping structure-neighbor-group-orf-capability {
+ description
+ "Structural grouping used to include orf
+ configuration for both BGP neighbors and peer groups.";
+ container orf-capability {
+ choice orf-update {
+ case send {
+ leaf orf-send {
+ type boolean;
+ default "false";
+ description
+ "Setting to 'true' advertises the ORF capability.";
+ }
+ }
+
+ case receive {
+ leaf orf-receive {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it receives the orf capability.";
+ }
+ }
+
+ case both {
+ leaf orf-both {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it advertises/receives the orf capability.";
+ }
+ }
+ }
+ }
+ }
+
+ grouping structure-neighbor-config-timers {
+ description
+ "Structural grouping used to include per neighbor timers
+ configuration for both BGP neighbors and peer groups.";
+ container timers {
+ leaf advertise-interval {
+ type uint16 {
+ range "0..600";
+ }
+ units "seconds";
+ description
+ "Minimum interval between sending BGP routing updates.";
+ }
+
+ leaf connect-time {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "BGP connect timer.";
+ }
+
+ uses neighbor-timers;
+ }
+ }
+
+ grouping structure-neighbor-group-filter-config {
+ description
+ "Structural grouping used to include filter
+ configuration for both BGP neighbors and peer groups.";
+ container filter-config {
+ description
+ "BGP Policy configuration for both BGP neighbors and groups.";
+ uses rmap-policy-import;
+
+ uses rmap-policy-export;
+
+ uses plist-policy-import;
+
+ uses plist-policy-export;
+
+ uses access-list-policy-import;
+
+ uses access-list-policy-export;
+
+ uses as-path-filter-list-policy-import;
+
+ uses as-path-filter-list-policy-export;
+
+ uses unsupress-map-policy-import;
+
+ uses unsupress-map-policy-export;
+ }
+ }
+}
diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang
new file mode 100644
index 0000000000..188cf856db
--- /dev/null
+++ b/yang/frr-bgp-common.yang
@@ -0,0 +1,1108 @@
+submodule frr-bgp-common {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ import ietf-bgp-types {
+ prefix bt;
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import frr-bgp-types {
+ prefix frr-bt;
+ }
+
+ import frr-route-types {
+ prefix frr-rt-type;
+ }
+
+ import frr-vrf {
+ prefix frr-vrf;
+ }
+
+ import ietf-routing-types {
+ prefix rt-types;
+ }
+
+ import frr-interface {
+ prefix frr-interface;
+ }
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This submodule contains general data definitions for use in BGP.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping rmap-policy-import {
+ leaf rmap-import {
+ type frr-bt:rmap-ref;
+ }
+ }
+
+ grouping rmap-policy-export {
+ leaf rmap-export {
+ type frr-bt:rmap-ref;
+ }
+ }
+
+ grouping unsupress-map-policy-import {
+ leaf unsupress-map-import {
+ type frr-bt:rmap-ref;
+ }
+ }
+
+ grouping unsupress-map-policy-export {
+ leaf unsupress-map-export {
+ type frr-bt:rmap-ref;
+ }
+ }
+
+ grouping plist-policy-import {
+ leaf plist-import {
+ type frr-bt:plist-ref;
+ }
+ }
+
+ grouping plist-policy-export {
+ leaf plist-export {
+ type frr-bt:plist-ref;
+ }
+ }
+
+ grouping access-list-policy-import {
+ leaf access-list-import {
+ type frr-bt:access-list-ref;
+ }
+ }
+
+ grouping access-list-policy-export {
+ leaf access-list-export {
+ type frr-bt:access-list-ref;
+ }
+ }
+
+ grouping as-path-filter-list-policy-import {
+ leaf as-path-filter-list-import {
+ type frr-bt:as-path-filter-ref;
+ }
+ }
+
+ grouping as-path-filter-list-policy-export {
+ leaf as-path-filter-list-export {
+ type frr-bt:as-path-filter-ref;
+ }
+ }
+
+ grouping route-selection-options {
+ description
+ "Configuration relating to route selection options.";
+ container route-selection-options {
+ description
+ "Parameters relating to options for route selection.";
+ leaf always-compare-med {
+ type boolean;
+ default "false";
+ description
+ "Compare multi-exit discriminator (MED) value from
+ different ASes when selecting the best route. The
+ default behaviour is to only compare MEDs for paths
+ received from the same AS.";
+ }
+
+ leaf deterministic-med {
+ type boolean;
+ default "false";
+ description
+ "Compare multi-exit discriminator (MED) value from
+ Same ASes when selecting the best route.";
+ }
+
+ leaf confed-med {
+ type boolean;
+ default "false";
+ description
+ "Compare multi-exit discriminator (MED) value from
+ different Sub ASes when selecting the best route.";
+ }
+
+ leaf missing-as-worst-med {
+ type boolean;
+ default "false";
+ description
+ "Missing multi-exit discriminator (MED) value is
+ considered as value of infinity, making the path
+ least desirable when selecting the best route.";
+ }
+
+ leaf aspath-confed {
+ type boolean;
+ default "false";
+ description
+ "Compare path lengths including confederation sets
+ and sequences in selecting a route.";
+ }
+
+ leaf ignore-as-path-length {
+ type boolean;
+ default "false";
+ description
+ "Ignore the AS path length when selecting the best path.
+ The default is to use the AS path length and prefer paths
+ with shorter length.";
+ }
+
+ leaf external-compare-router-id {
+ type boolean;
+ default "true";
+ description
+ "When comparing similar routes received from external BGP
+ peers, use the router-id as a criterion to select the
+ active path.";
+ }
+
+ leaf allow-multiple-as {
+ type boolean;
+ default "false";
+ description
+ "Allow multi-path to use paths from different neighbouring
+ ASes. The default is to only consider multiple paths
+ from the same neighbouring AS.";
+ }
+
+ leaf multi-path-as-set {
+ when "../allow-multiple-as = 'true'";
+ type boolean;
+ default "false";
+ description
+ "Multi-path with AS-SET, When set to 'true' it adds AS set
+ information for aggregate routes, When set to 'false' it
+ prevents AS set generation.";
+ }
+ }
+ }
+
+ grouping med-config {
+ description
+ "Configuration relating to MED.";
+ container med-config {
+ leaf enable-med-admin {
+ type boolean;
+ default "false";
+ description
+ "Flag to enable receiving of MED metric attribute
+ in routing updates.";
+ }
+ leaf max-med-admin {
+ type uint32 {
+ range "0..4294967295";
+ }
+ default "4294967294";
+ description
+ "Tells the router to announce routes with this MED value
+ This MED value is applicable for indefinite time.";
+ }
+
+ leaf max-med-onstart-up-time {
+ type uint32 {
+ range "5..86400";
+ }
+ units "seconds";
+ description
+ "Tells the router to announce routes with MED value,
+ This MED value is applicable for this duration during
+ start-up.";
+ }
+
+ leaf max-med-onstart-up-value {
+ type uint32 {
+ range "0..4294967295";
+ }
+ default "4294967294";
+ description
+ "Tells the router to announce routes with this MED value
+ This MED value is applicable for start-up time.";
+ }
+ }
+ }
+
+ grouping route-reflector-config {
+ description
+ "Grouping used to include route reflector
+ configuration for BGP global.";
+ container route-reflector {
+ description
+ "Route reflector parameters for the BGP global.";
+ leaf route-reflector-cluster-id {
+ type bt:rr-cluster-id-type;
+ description
+ "Route Reflector cluster ID to use when local router is
+ configured as a route reflector. Commonly set at the
+ group level, but allows a different cluster ID to be set
+ for each neighbor.";
+ reference
+ "RFC 4456: BGP Route Reflection: An Alternative to
+ Full Mesh.";
+ }
+
+ leaf no-client-reflect {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', this disables route redistribution
+ by the Route Reflector. It is set 'true' when the client is
+ fully meshed to prevent sending of redundant route
+ advertisements.";
+ reference
+ "TODO: Add reference when IETF writes a draft describing
+ this.";
+ }
+
+ leaf allow-outbound-policy {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true', this allow RR to modify the attributes of the
+ reflected IBGP routes as per the out route-map. It is set 'false'
+ RR will not modify the attributes of the reflected IBGP routes as
+ per the out route-map rules.";
+ }
+ }
+ }
+
+ grouping global-bgp-config {
+ leaf instance-type-view {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' BGP instance type is view.
+ When set to 'false' BGP instance type is regular.";
+ }
+
+ leaf ebgp-multihop-connected-route-check {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it will disable checking if nexthop is connected
+ on EBGP sessions. When set to 'false' it will enable checking if
+ nexthop is connected on EBGP sessions.";
+ }
+
+ leaf fast-external-failover {
+ type boolean;
+ default "true";
+ description
+ "It's an interface tracking mechanism. When set to 'true' don't
+ immediately reset session if a link to a directly connected
+ external peer goes down. When set to 'false' it will immediately
+ reset session if a link to a directly connected external peer goes down.";
+ }
+
+ leaf local-pref {
+ type uint32;
+ default "100";
+ description
+ "BGP local preference attribute sent to internal peers to
+ indicate the degree of preference for externally learned
+ routes. The route with the highest local preference
+ value is preferred.";
+ reference
+ "RFC 4271.";
+ }
+
+ leaf default-shutdown {
+ type boolean;
+ default "false";
+ description
+ "Apply administrative shutdown to newly configured peers.";
+ }
+
+ leaf ebgp-requires-policy {
+ type boolean;
+ default "true";
+ description
+ "When set to 'true' BGP speaker requires in and out policy
+ for EBGP peers (RFC8212).";
+ }
+
+ leaf show-hostname {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' BGP show hostname in certain command outputs.";
+ }
+
+ leaf show-nexthop-hostname {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' BGP show hostname for nexthop in certain
+ command outputs.";
+ }
+ }
+
+ grouping global-neighbor-config {
+ container global-neighbor-config {
+ leaf dynamic-neighbors-limit {
+ type uint32 {
+ range "1..5000";
+ }
+ description
+ "Maximum number of BGP Dynamic Neighbors that can be created.";
+ }
+
+ leaf log-neighbor-changes {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' BGP will start logging neighbor up/down and reset reason.
+ When set to 'false' BGP will stop logging neighbor up/down and reset reason.";
+ }
+
+ container packet-quanta-config {
+ leaf wpkt-quanta {
+ type uint32 {
+ range "1..64";
+ }
+ default "64";
+ description
+ "How many packets to write to peer socket per run.";
+ }
+
+ leaf rpkt-quanta {
+ type uint32 {
+ range "1..10";
+ }
+ default "10";
+ description
+ "How many packets to read from peer socket per I/O cycle.";
+ }
+ }
+ }
+ }
+
+ grouping global-update-group-config {
+ container global-update-group-config {
+ leaf subgroup-pkt-queue-size {
+ type uint32 {
+ range "20..100";
+ }
+ default "40";
+ description
+ "Subgroup packet queue size.";
+ }
+
+ leaf coalesce-time {
+ type uint32 {
+ range "0..4294967295";
+ }
+ units "miliseconds";
+ default "1000";
+ description
+ "Configures the Subgroup coalesce timer.";
+ }
+ }
+ }
+
+ grouping global-network-config {
+ leaf import-check {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' bgp creates entries for network statements
+ if a matching prefix exists in the rib. When set to 'false' bgp
+ creates entries for networks that the router cannot reach.";
+ }
+ }
+
+ grouping neighbor-timers {
+ leaf hold-time {
+ type uint16 {
+ range "0 | 3..65535";
+ }
+ units "seconds";
+ default "180";
+ description
+ "Time interval (in seconds) for the HoldTimer established
+ with the peer. When read as operational data (ro), the
+ value of this object is calculated by this BGP speaker,
+ using the smaller of the values in hold-time that was
+ configured (rw) in the running datastore and the Hold Time
+ received in the OPEN message.
+
+ This value must be at least three seconds
+ if it is not zero (0).
+
+ If the Hold Timer has not been established
+ with the peer this object MUST have a value
+ of zero (0).
+
+ If the configured value of hold-time object was
+ a value of (0), then when read this object MUST have a
+ value of (0) also.";
+ reference
+ "RFC 4271";
+ }
+
+ leaf keepalive {
+ type uint16 {
+ range "0..65535";
+ }
+ units "seconds";
+ default "60";
+ description
+ "When used as a configuration (rw) value, this 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 configured value for hold-time.
+
+ 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.;
+
+ The actual time interval for the KEEPALIVE messages is
+ indicated by operational value of keepalive. That value
+ of this object is calculated by this BGP speaker such that,
+ when compared with hold-time, it has the same proportion
+ that keepalive has, compared with hold-time. A
+ reasonable maximum value for this timer would be one third
+ of that of hold-time.";
+ reference
+ "RFC 4271";
+ }
+ }
+
+ grouping global-config-timers {
+ container global-config-timers {
+ leaf rmap-delay-time {
+ type uint16 {
+ range "0..600";
+ }
+ units "seconds";
+ default "5";
+ description
+ "Time to wait before processing route-map changes.";
+ }
+
+ leaf update-delay-time {
+ type uint16 {
+ range "0..3600";
+ }
+ units "seconds";
+ description
+ "Time to force initial delay for best-path and updates.";
+ }
+
+ leaf establish-wait-time {
+ type uint16 {
+ range "1..3600";
+ }
+ units "seconds";
+ description
+ "Time to force initial delay for updates.";
+ }
+
+ leaf connect-retry-interval {
+ type uint16 {
+ range "1..max";
+ }
+ units "seconds";
+ default "120";
+ description
+ "Time interval (in seconds) for the ConnectRetryTimer. The
+ suggested value for this timer is 120 seconds.";
+ reference
+ "RFC 4271, This is the value used
+ to initialize the 'ConnectRetryTimer'.";
+ }
+
+ uses neighbor-timers;
+ }
+ }
+
+ grouping graceful-restart-config {
+ description
+ "Configuration parameters relating to BGP graceful restart.";
+ choice mode {
+ case graceful-restart-mode {
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "Enable or disable the graceful-restart capability. When set to 'true'
+ it will enable graceful restart and helper both globally. When set
+ to 'false' it will enable the default behaviour global helper mode.";
+ }
+ }
+
+ case graceful-restart-disable-mode {
+ leaf graceful-restart-disable {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it will disable graceful restart and helper both
+ globally. when set to 'false' it will enable the default behaviour
+ global helper mode.";
+ }
+ }
+ }
+
+ leaf preserve-fw-entry {
+ type boolean;
+ default "false";
+ description
+ "Sets F-bit indication that fib is preserved while doing Graceful Restart.
+ When set to 'true' Zebra would preserve the FIB entry on the restarting
+ node.";
+ }
+
+ leaf restart-time {
+ type uint16 {
+ range "1..3600";
+ }
+ units "seconds";
+ default "120";
+ description
+ "Estimated time (in seconds) for the local BGP speaker to
+ restart a session. This value is advertise in the graceful
+ restart BGP capability. This is a 12-bit value, referred to
+ as Restart Time in RFC4724. Per RFC4724, the suggested
+ default value is <= the hold-time value. This timer is
+ applicable for helper node.";
+ reference
+ "RFC 4724: Graceful Restart Mechanism for BGP.";
+ }
+
+ leaf stale-routes-time {
+ type uint16 {
+ range "1..3600";
+ }
+ units "seconds";
+ default "360";
+ description
+ "An upper-bound on the time that stale routes will be
+ retained by a router after a session is restarted. If an
+ End-of-RIB (EOR) marker is received prior to this timer
+ expiring stale-routes will be flushed upon its receipt - if
+ no EOR is received, then when this timer expires stale paths
+ will be purged. This timer is applicable for restarting node.";
+ reference
+ "RFC 4724: Graceful Restart Mechanism for BGP.";
+ }
+
+ leaf selection-deferral-time {
+ type uint16 {
+ range "0..3600";
+ }
+ units "seconds";
+ default "360";
+ description
+ "An upper-bound on the time that restarting router defers
+ the route selection process after restart.";
+ reference
+ "RFC 4724: Graceful Restart Mechanism for BGP.";
+ }
+
+ leaf rib-stale-time {
+ type uint16 {
+ range "1..3600";
+ }
+ units "seconds";
+ default "500";
+ description
+ "An upper-bound on the time that helper router holds the
+ stale routes in Zebra, When this timer gets expired Zebra
+ removes the stale routes.";
+ }
+ }
+
+ grouping global-group-use-multiple-paths {
+ description
+ "Common grouping used for both global and groups which provides
+ configuration parameters relating to use of multiple paths.";
+ container use-multiple-paths {
+ description
+ "Parameters related to the use of multiple paths for the
+ same NLRI.";
+ container ebgp {
+ description
+ "Multi-Path parameters for EBGP.";
+ leaf maximum-paths {
+ type uint16;
+ default "64";
+ description
+ "Maximum number of parallel paths to consider when using
+ BGP multi-path. The default is use a single path.";
+ }
+ }
+
+ container ibgp {
+ description
+ "Multi-Path parameters for IBGP.";
+ leaf maximum-paths {
+ type uint16;
+ default "64";
+ description
+ "Maximum number of parallel paths to consider when using
+ IBGP multi-path. The default is to use a single path.";
+ }
+
+ leaf cluster-length-list {
+ when "../maximum-paths != 0";
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' route with the shortest cluster-list
+ length is used. The cluster-list reflects the IBGP
+ reflection path the route has taken. It's the part
+ of route selection algo.";
+ }
+ }
+ }
+ }
+
+ grouping global-redistribute {
+ description
+ "List of route redistribution per AFI.";
+ list redistribution-list {
+ key "route-type route-instance";
+ leaf route-type {
+ type frr-rt-type:frr-route-types;
+ description
+ "Protocol route type.";
+ }
+
+ leaf route-instance {
+ type uint16 {
+ range "0..65535";
+ }
+ description
+ "Protocol Instance.";
+ }
+
+ leaf metric {
+ type uint32 {
+ range "0..4294967295";
+ }
+ description
+ "Metric for redistributed routes.";
+ }
+
+ leaf rmap-policy-import {
+ type frr-bt:rmap-ref;
+ description
+ "Route-map to be applied for redistributed routes into the bgp.";
+ }
+ }
+ }
+
+ grouping mp-afi-safi-network-config {
+ leaf label-index {
+ type rt-types:mpls-label;
+ description
+ "Label index to associate with the prefix.";
+ }
+
+ leaf rmap-policy-export {
+ type frr-bt:rmap-ref;
+ description
+ "Route-map to modify the attributes for Routes going out
+ via BGP updates.";
+ }
+ }
+
+ grouping mp-afi-safi-agg-route-config {
+ leaf as-set {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' AS set path information is generated
+ for aggregate address. When set to 'false' AS set path
+ information is not generated.";
+ }
+
+ leaf summary-only {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it advertise only the aggregate route
+ and suppress the advertisement of all the component routes.
+ When set to 'false' all more-specific routes summarized
+ by the aggregate route are advertised.";
+ }
+
+ leaf rmap-policy-export {
+ type frr-bt:rmap-ref;
+ description
+ "Apply route map to aggregate network.";
+ }
+ }
+
+ grouping admin-distance {
+ container admin-distance {
+ description
+ "Administrative distance (or preference) assigned to
+ routes received from different sources
+ (external, internal, and local).";
+ leaf external {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Administrative distance for routes learned from
+ external BGP (EBGP).";
+ }
+
+ leaf internal {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Administrative distance for routes learned from
+ internal BGP (IBGP).";
+ }
+
+ leaf local {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Administrative distance for routes learned from
+ local.";
+ }
+ }
+ }
+
+ grouping distance-per-route-config {
+ leaf distance {
+ type uint8 {
+ range "1..255";
+ }
+ mandatory true;
+ description
+ "Administrative distance for route.";
+ }
+
+ leaf access-list-policy-export {
+ type frr-bt:access-list-ref;
+ description
+ "Access-list policy applied on routes going from BGP to Zebra.";
+ }
+ }
+
+ grouping route-flap-dampening {
+ container route-flap-dampening {
+ description
+ "Dampening feature";
+ leaf enable {
+ type boolean;
+ default "false";
+ description
+ "Enable route flap dampening.";
+ }
+
+ leaf reach-decay {
+ when "../enable = 'true'";
+ type uint8 {
+ range "1..45";
+ }
+ units "seconds";
+ default "15";
+ description
+ "This value specifies the time desired for the instability
+ metric value to reach one-half of its current value when
+ the route is reachable. This half-life value determines
+ the rate at which the metric value is decayed. A smaller
+ half-life value makes a suppressed route reusable sooner
+ than a larger value. The accumulated penalty will be reduced
+ to half after this duration.";
+ }
+
+ leaf reuse-above {
+ when "../enable = 'true'";
+ type uint16 {
+ range "1..20000";
+ }
+ default "750";
+ description
+ "This is the value of the instability metric at which a
+ suppressed route becomes unsuppressed if it is reachable
+ but currently suppressed. The value assigned to
+ reuse-below must be less than suppress-above.";
+ }
+
+ leaf suppress-above {
+ when "../enable = 'true'";
+ type uint16 {
+ range "1..20000";
+ }
+ default "2000";
+ description
+ "This is the value of the instability metric at which
+ route suppression takes place. A route is not installed
+ in the forwarding information base (FIB), or announced
+ even if it is reachable during the period that it is
+ suppressed.";
+ }
+
+ leaf unreach-decay {
+ when "../enable = 'true'";
+ type uint8 {
+ range "1..255";
+ }
+ units "seconds";
+ default "60";
+ description
+ "This value acts the same as reach-decay except that it
+ specifies the rate at which the instability metric is
+ decayed when a route is unreachable. It should have a
+ value greater than or equal to reach-decay.";
+ }
+ }
+ }
+
+ grouping flow-spec-config {
+ container flow-spec-config {
+ description
+ "Flow spec feature.";
+ leaf interface {
+ type frr-interface:interface-ref {
+ require-instance false;
+ }
+ description
+ "The local interface.";
+ }
+ }
+ }
+
+ grouping global-graceful-shutdown {
+ description
+ "Structural grouping used to include graceful-shutdown
+ configuration for both BGP neighbors and peer groups.";
+ container graceful-shutdown {
+ description
+ "BGP Graceful shutdown feature.";
+ leaf enable {
+ type boolean;
+ default "false";
+ description
+ "Enable graceful-shutdown feature.";
+ }
+ }
+ }
+
+ grouping global-filter-config {
+ description
+ "Structural grouping used to include filter
+ configuration for BGP RIB table.";
+ container filter-config {
+ description
+ "BGP table to RIB route download filter.";
+ uses rmap-policy-export;
+ }
+ }
+
+ grouping route-distinguisher-params {
+ description
+ "Route distinguisher value as per RFC4364.";
+ leaf rd {
+ type rt-types:route-distinguisher;
+ description
+ "Route distinguisher value as per RFC4364.";
+ }
+ }
+
+ grouping vpn-label-params {
+ description
+ "Label value for VRF.";
+ choice label-allocation-mode {
+ case manual {
+ leaf label {
+ type rt-types:mpls-label;
+ description
+ "Label index to associate with the prefix.";
+ }
+ }
+
+ case auto {
+ leaf label-auto {
+ type boolean;
+ default "false";
+ description
+ "Automatically assign a label.";
+ }
+ }
+ }
+ }
+
+ grouping vpn-nexthop-params {
+ description
+ "Specify next hop to use for VRF advertised prefixes.";
+ leaf nexthop {
+ type inet:ip-address;
+ description
+ "Nexthop IP address.";
+ }
+ }
+
+ grouping rt-list {
+ description
+ "Route Target list";
+ leaf-list import-rt-list {
+ type rt-types:route-target;
+ description
+ "For routes leaked from vpn to current address-family: match any.";
+ }
+
+ leaf-list export-rt-list {
+ type rt-types:route-target;
+ description
+ "For routes leaked from current address-family to vpn: set.";
+ }
+ }
+
+ grouping vpn-route-target-params {
+ description
+ "Route Target value.";
+ leaf redirect-rt {
+ type rt-types:route-target;
+ description
+ "Flow-spec redirect type route target.";
+ }
+
+ choice rt-direction {
+ case import-export {
+ uses rt-list;
+ }
+ case both {
+ leaf-list rt-list {
+ type rt-types:route-target;
+ description
+ "Both import: match any and export: set.";
+ }
+ }
+ }
+ }
+
+ grouping vpn-import-params {
+ description
+ "VPN route leaking parameters.";
+ leaf import-vpn {
+ type boolean;
+ default "false";
+ description
+ "Import routes from default instance VPN RIB.";
+ }
+
+ leaf export-vpn {
+ type boolean;
+ default "false";
+ description
+ "Export routes to default instance VPN RIB.";
+ }
+
+ list import-vrf-list {
+ key "vrf";
+ description
+ "List of VRFs to import routes from.";
+ leaf vrf {
+ type frr-vrf:vrf-ref {
+ require-instance false;
+ }
+ description
+ "Routing instance.";
+ }
+ }
+
+ uses rmap-policy-import;
+
+ uses rmap-policy-export;
+ }
+
+ grouping global-afi-safi-vpn-config {
+ container vpn-config {
+ uses route-distinguisher-params;
+
+ uses vpn-label-params;
+
+ uses vpn-nexthop-params;
+
+ uses vpn-import-params;
+
+ uses vpn-route-target-params;
+ }
+ }
+
+ grouping global-afi-safi-vpn-network-config {
+ list network-config {
+ key "rd";
+ description
+ "A list of rd.";
+ uses route-distinguisher-params;
+
+ list prefix-list {
+ key "prefix";
+ description
+ "A list of prefix.";
+ leaf prefix {
+ type inet:ip-prefix;
+ description
+ "IP destination prefix.";
+ }
+
+ leaf label-index {
+ type uint32;
+ mandatory true;
+ description
+ "Label index to associate with the prefix.";
+ }
+
+ leaf rmap-policy-export {
+ type frr-bt:rmap-ref;
+ description
+ "Route-map to modify the attributes for Routes going out
+ via BGP updates.";
+ }
+ }
+ }
+ }
+}
diff --git a/yang/frr-bgp-neighbor.yang b/yang/frr-bgp-neighbor.yang
new file mode 100644
index 0000000000..3b8d63c447
--- /dev/null
+++ b/yang/frr-bgp-neighbor.yang
@@ -0,0 +1,137 @@
+submodule frr-bgp-neighbor {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ include frr-bgp-common-structure;
+ include frr-bgp-common-multiprotocol;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This submodule contains general data definitions for use in BGP neighbor.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping neighbor-parameters {
+ leaf password {
+ type string {
+ length "1..254";
+ }
+ description
+ "Actual password.";
+ }
+
+ leaf ttl-security {
+ type uint8;
+ description
+ "BGP Time To Live (TTL) security check.";
+ reference
+ "RFC 5082: The Generalized TTL Security Mechanism
+ (GTSM),
+ RFC 7454: BGP Operations and Security.";
+ }
+
+ leaf solo {
+ type boolean;
+ default "false";
+ description
+ "Solo peer - part of its own update group.";
+ }
+
+ leaf enforce-first-as {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it will enforce the first AS for EBGP routes.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "An optional textual description (intended primarily for use
+ with a peer or group.";
+ }
+
+ leaf passive-mode {
+ type boolean;
+ default "false";
+ description
+ "Don't send open messages to this neighbor.";
+ }
+
+ uses structure-neighbor-group-capability-options;
+
+ uses neighbor-update-source;
+
+ uses neighbor-remote-as;
+
+ uses structure-neighbor-group-ebgp-multihop;
+
+ uses neighbor-local-as-options;
+
+ uses neighbor-bfd-options;
+
+ uses structure-neighbor-group-admin-shutdown;
+
+ uses structure-neighbor-group-graceful-restart;
+
+ uses structure-neighbor-config-timers;
+
+ container afi-safis {
+ description
+ "List of address-families associated with the BGP
+ instance.";
+ list afi-safi {
+ key "afi-safi-name";
+ description
+ "AFI, SAFI configuration available for the
+ neighbour or group.";
+ uses mp-afi-safi-config;
+
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "This leaf indicates whether the IPv4 Unicast AFI, SAFI is
+ enabled for the neighbour or group.";
+ }
+
+ uses mp-all-afi-safi-list-contents;
+ }
+ }
+ }
+}
diff --git a/yang/frr-bgp-peer-group.yang b/yang/frr-bgp-peer-group.yang
new file mode 100644
index 0000000000..3ce628d2b7
--- /dev/null
+++ b/yang/frr-bgp-peer-group.yang
@@ -0,0 +1,89 @@
+submodule frr-bgp-peer-group {
+ yang-version 1.1;
+
+ belongs-to frr-bgp {
+ prefix "bgp";
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ include frr-bgp-common-structure;
+ include frr-bgp-neighbor;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This submodule contains general data definitions for use in BGP
+ peer group.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ grouping bgp-peer-group-base {
+ description
+ "Parameters related to a BGP group.";
+ leaf peer-group-name {
+ type string;
+ description
+ "Name of the BGP peer-group.";
+ }
+
+ leaf-list ipv4-listen-range {
+ type inet:ipv4-address;
+ description
+ "Configure BGP dynamic neighbors listen range.";
+ }
+
+ leaf-list ipv6-listen-range {
+ type inet:ipv6-address;
+ description
+ "Configure BGP dynamic neighbors listen range.";
+ }
+
+ uses neighbor-parameters;
+ }
+
+ grouping bgp-peer-group-list {
+ description
+ "The list of BGP peer groups.";
+ list peer-group {
+ key "peer-group-name";
+ description
+ "List of BGP peer-groups configured on the local system -
+ uniquely identified by peer-group name.";
+ uses bgp-peer-group-base;
+ }
+ }
+}
diff --git a/yang/frr-bgp-rpki.yang b/yang/frr-bgp-rpki.yang
new file mode 100644
index 0000000000..e9b6752f26
--- /dev/null
+++ b/yang/frr-bgp-rpki.yang
@@ -0,0 +1,209 @@
+module frr-bgp-rpki {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/frr-bgp-rpki";
+ prefix frr-bgp-rpki;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import frr-vrf {
+ prefix frr-vrf;
+ }
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module defines a model for managing FRR BGP RPKI.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ typedef transport-type {
+ type enumeration {
+ enum "TCP" {
+ value 1;
+ description
+ "Connection to server is TCP based.";
+ }
+ enum "SSH" {
+ value 2;
+ description
+ "Connection to server is SSH based.";
+ }
+ }
+ }
+
+ grouping bgp-rpki-timers {
+ container rpki-timers {
+ description
+ "RPKI timers config.";
+ leaf polling-time {
+ type uint32 {
+ range "1..86400";
+ }
+ units "seconds";
+ default "3600";
+ description
+ "Set the number of seconds the router waits until the
+ router asks the cache again for updated data.";
+ }
+
+ leaf expire-time {
+ type uint32 {
+ range "600..172800";
+ }
+ units "seconds";
+ default "7200";
+ description
+ "Set the expire interval.";
+ }
+
+ leaf retry-time {
+ type uint16 {
+ range "1..7200";
+ }
+ units "seconds";
+ default "600";
+ description
+ "Set the retry interval.";
+ }
+ }
+ }
+
+ grouping bgp-rpki-cache-server {
+ container rpki-cache-server {
+ description
+ "Add a cache server to the socket.";
+ list cache-list {
+ key "preference";
+ leaf preference {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Preference of the cache server.";
+ }
+
+ leaf cache-type {
+ type transport-type;
+ mandatory true;
+ description
+ "Specifies a transport method for the RPKI cache.";
+ }
+
+ choice server {
+ case ip-address {
+ leaf ip-address {
+ type inet:ip-address;
+ mandatory true;
+ }
+ }
+
+ case host-name {
+ leaf ip-host-address {
+ type inet:host;
+ mandatory true;
+ }
+ }
+ }
+
+ container transport {
+ container tcp {
+ when "../../cache-type = 'TCP'";
+ description
+ "TCP server details.";
+ leaf tcp-port {
+ type uint32;
+ }
+ }
+
+ container ssh {
+ when "../../cache-type = 'SSH'";
+ description
+ "SSH login details";
+ leaf ssh-port {
+ type uint32 {
+ range "1..65535";
+ }
+ description
+ "SSH port on which session gets opened.";
+ }
+
+ leaf user-name {
+ type string;
+ description
+ "SSH username to establish an SSH connection to the
+ cache server.";
+ }
+
+ leaf private-key {
+ type string;
+ description
+ "Local path that includes the private key file of the router.";
+ }
+
+ leaf public-key {
+ type string;
+ description
+ "Local path that includes the public key file of the router.";
+ }
+
+ leaf server-public-ley {
+ type string;
+ description
+ "Server public key.";
+ }
+ }
+ }
+ }
+ }
+ }
+
+ augment "/frr-vrf:lib/frr-vrf:vrf" {
+ container bgp-rpki {
+ description
+ "RPKI configuration parameters.";
+ leaf enable {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it enables the RPKI.";
+ }
+
+ uses bgp-rpki-timers;
+
+ uses bgp-rpki-cache-server;
+ }
+ }
+}
diff --git a/yang/frr-bgp-types.yang b/yang/frr-bgp-types.yang
new file mode 100644
index 0000000000..0afdea1ba6
--- /dev/null
+++ b/yang/frr-bgp-types.yang
@@ -0,0 +1,154 @@
+module frr-bgp-types {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/bgp-types";
+ prefix frr-bt;
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module contains general data definitions for use in BGP.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ typedef rmap-ref {
+ type string;
+ }
+
+ typedef plist-ref {
+ type string;
+ }
+
+ typedef access-list-ref {
+ type string;
+ }
+
+ typedef as-path-filter-ref {
+ type string;
+ }
+
+ typedef bgp-instance-type {
+ type enumeration {
+ enum "default" {
+ value 1;
+ description
+ "BGP instance default.";
+ }
+ enum "vrf" {
+ value 2;
+ description
+ "BGP instance vrf.";
+ }
+ enum "view" {
+ value 3;
+ description
+ "BGP instance view.";
+ }
+ }
+ }
+
+ typedef as-type {
+ type enumeration {
+ enum "as-specified" {
+ value 1;
+ description
+ "AS has explicitly specified value.";
+ }
+ enum "internal" {
+ value 2;
+ description
+ "Internal BGP peer.";
+ }
+ enum "external" {
+ value 3;
+ description
+ "External BGP peer.";
+ }
+ }
+ }
+
+ typedef add-path-type {
+ type enumeration {
+ enum "all" {
+ value 1;
+ description
+ "To advertise all paths to a neighbor.";
+ }
+ enum "per-as" {
+ value 2;
+ description
+ "To advertise the best path per each neighboring AS.";
+ }
+ enum "none" {
+ value 3;
+ description
+ "Add path feature is disabled.";
+ }
+ }
+ }
+
+ typedef bfd-session-type {
+ type enumeration {
+ enum "single-hop" {
+ value 1;
+ description
+ "Single hop session.";
+ }
+ enum "multi-hop" {
+ value 2;
+ description
+ "Multiple hop session.";
+ }
+ enum "not-configured" {
+ value 3;
+ description
+ "Not Configured.";
+ }
+ }
+ }
+
+ typedef direction {
+ type enumeration {
+ enum "in" {
+ value 1;
+ description
+ "IN, ingress, Rx.";
+ }
+ enum "out" {
+ value 2;
+ description
+ "OUT, egress, Tx.";
+ }
+ }
+ }
+}
diff --git a/yang/frr-bgp.yang b/yang/frr-bgp.yang
new file mode 100644
index 0000000000..3e5b1da7d3
--- /dev/null
+++ b/yang/frr-bgp.yang
@@ -0,0 +1,1239 @@
+module frr-bgp {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/bgp";
+ prefix frr-bgp;
+
+ import frr-routing {
+ prefix frr-rt;
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import ietf-routing-types {
+ prefix rt-types;
+ }
+
+ import frr-interface {
+ prefix frr-interface;
+ }
+
+ include "frr-bgp-common-structure";
+
+ include "frr-bgp-common";
+
+ include "frr-bgp-common-multiprotocol";
+
+ include "frr-bgp-neighbor";
+
+ include "frr-bgp-peer-group";
+
+ include "frr-bgp-bmp";
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module defines a model for managing FRR bgpd daemon.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ identity bgp {
+ base frr-rt:routing-protocol;
+ description
+ "BGP protocol.";
+ }
+
+ grouping mp-afi-unicast-common {
+ uses global-group-use-multiple-paths;
+
+ uses global-redistribute;
+
+ uses admin-distance;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" {
+ container bgp {
+ when "../frr-rt:type = 'frr-bgp:bgp'" {
+ description
+ "BGP protocol augmentation of ietf-routing module
+ control-plane-protocol.";
+ }
+ description
+ "Top-level configuration for the BGP router.";
+ container global {
+ presence "Enables global configuration of BGP";
+ description
+ "Global configuration for the BGP router.";
+ leaf local-as {
+ type inet:as-number;
+ mandatory true;
+ description
+ "Local autonomous system number of the router. Uses
+ the 32-bit as-number type from the model in RFC 6991.";
+ }
+
+ uses frr-rt:router-id;
+
+ container confederation {
+ description
+ "Configuration options specifying parameters when the
+ local router is within an autonomous system which is
+ part of a BGP confederation.";
+ leaf identifier {
+ type inet:as-number;
+ description
+ "Confederation identifier for the autonomous system.";
+ }
+
+ leaf-list member-as {
+ type inet:as-number;
+ description
+ "Remote autonomous systems that are to be treated
+ as part of the local confederation.";
+ }
+ }
+
+ uses med-config;
+
+ uses route-reflector-config;
+
+ uses route-selection-options;
+
+ uses global-neighbor-config;
+
+ container graceful-restart {
+ description
+ "Parameters relating the graceful restart mechanism for
+ BGP.";
+ uses graceful-restart-config;
+ }
+
+ uses global-update-group-config;
+
+ uses global-config-timers;
+
+ uses global-bgp-config;
+
+ uses global-network-config;
+
+ uses global-graceful-shutdown;
+
+ uses global-bmp-config;
+
+ container afi-safis {
+ description
+ "List of address-families associated with the BGP
+ instance.";
+ list afi-safi {
+ key "afi-safi-name";
+ description
+ "AFI, SAFI configuration available for the
+ neighbour or group.";
+ uses mp-afi-safi-config;
+
+ uses mp-all-afi-safi-list-contents;
+ }
+ }
+ }
+
+ container neighbors {
+ description
+ "Configuration for BGP neighbors.";
+ list neighbor {
+ key "remote-address";
+ description
+ "List of BGP neighbors configured on the local system,
+ uniquely identified by remote IPv[46] address.";
+ leaf remote-address {
+ type inet:ip-address;
+ description
+ "The remote IP address of this entry's BGP peer.";
+ }
+
+ leaf local-interface {
+ type frr-interface:interface-ref {
+ require-instance false;
+ }
+ description
+ "Neighbor's interface name.";
+ }
+
+ leaf local-port {
+ type inet:port-number {
+ range "0..65535";
+ }
+ description
+ "Neighbor's BGP TCP port number.";
+ }
+
+ leaf peer-group {
+ type leafref {
+ path "../../../peer-groups/peer-group/peer-group-name";
+ }
+ description
+ "The peer-group with which this neighbor is associated.";
+ }
+
+ uses neighbor-parameters;
+ }
+
+ list unnumbered-neighbor {
+ key "interface";
+ description
+ "List of BGP neighbors configured on the local system,
+ uniquely identified by interfaces.";
+ leaf interface {
+ type frr-interface:interface-ref {
+ require-instance false;
+ }
+ description
+ "The local interface of this entry's BGP peer.";
+ }
+
+ leaf v6only {
+ type boolean;
+ default "false";
+ description
+ "When set to 'true' it will create a neighbor with v6
+ link local only.";
+ }
+
+ leaf peer-group {
+ type leafref {
+ path "../../../peer-groups/peer-group/peer-group-name";
+ }
+ description
+ "The peer-group with which this neighbor is associated.";
+ }
+
+ uses neighbor-parameters;
+ }
+ }
+
+ container peer-groups {
+ description
+ "Configuration for BGP peer-groups.";
+ uses bgp-peer-group-list;
+ }
+ }
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-unicast" {
+ list network-config {
+ key "prefix";
+ description
+ "A list of network routes.";
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "IPv4 destination prefix.";
+ }
+
+ leaf backdoor {
+ type boolean;
+ default "false";
+ description
+ "Specify a BGP backdoor route.";
+ }
+
+ uses mp-afi-safi-network-config;
+ }
+
+ list aggregate-route {
+ key "prefix";
+ description
+ "A list of aggregated routes.";
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "IPv4 destination prefix.";
+ }
+
+ uses mp-afi-safi-agg-route-config;
+ }
+
+ list admin-distance-route {
+ key "prefix";
+ description
+ "A list of routes with a particular admin distance.";
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "IPv4 destination prefix.";
+ }
+
+ uses distance-per-route-config;
+ }
+
+ uses route-flap-dampening;
+
+ uses mp-afi-unicast-common;
+
+ uses global-filter-config;
+
+ uses global-afi-safi-vpn-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-unicast" {
+ list network-config {
+ key "prefix";
+ description
+ "A list of network routes.";
+ leaf prefix {
+ type inet:ipv6-prefix;
+ description
+ "IPv6 destination prefix.";
+ }
+
+ leaf backdoor {
+ type boolean;
+ default "false";
+ description
+ "Specify a BGP backdoor route.";
+ }
+
+ uses mp-afi-safi-network-config;
+ }
+
+ list aggregate-route {
+ key "prefix";
+ description
+ "A list of aggregated routes.";
+ leaf prefix {
+ type inet:ipv6-prefix;
+ description
+ "IPv6 destination prefix.";
+ }
+
+ uses mp-afi-safi-agg-route-config;
+ }
+
+ list admin-distance-route {
+ key "prefix";
+ description
+ "A list of routes with a particular admin distance.";
+ leaf prefix {
+ type inet:ipv6-prefix;
+ description
+ "IPv6 destination prefix.";
+ }
+
+ uses distance-per-route-config;
+ }
+
+ uses mp-afi-unicast-common;
+
+ uses global-filter-config;
+
+ uses global-afi-safi-vpn-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-labeled-unicast" {
+ uses global-group-use-multiple-paths;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-labeled-unicast" {
+ uses global-group-use-multiple-paths;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-multicast" {
+ list network-config {
+ key "prefix";
+ description
+ "A list of network routes.";
+ leaf prefix {
+ type rt-types:ipv4-multicast-group-address;
+ description
+ "IPv4 multicast destination prefix.";
+ }
+
+ leaf backdoor {
+ type boolean;
+ default "false";
+ description
+ "Specify a BGP backdoor route.";
+ }
+
+ uses mp-afi-safi-network-config;
+ }
+
+ list aggregate-route {
+ key "prefix";
+ description
+ "A list of aggregated routes.";
+ leaf prefix {
+ type rt-types:ipv4-multicast-group-address;
+ description
+ "IPv4 multicast destination prefix.";
+ }
+
+ uses mp-afi-safi-agg-route-config;
+ }
+
+ list admin-distance-route {
+ key "prefix";
+ description
+ "A list of routes with a particular admin distance.";
+ leaf prefix {
+ type rt-types:ipv4-multicast-group-address;
+ description
+ "IPv4 multicast destination prefix.";
+ }
+ }
+
+ uses admin-distance;
+
+ uses route-flap-dampening;
+
+ uses global-filter-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-multicast" {
+ list network-config {
+ key "prefix";
+ description
+ "A list of network routes.";
+ leaf prefix {
+ type rt-types:ipv6-multicast-group-address;
+ description
+ "IPv6 multicast destination prefix.";
+ }
+
+ leaf backdoor {
+ type boolean;
+ default "false";
+ description
+ "Specify a BGP backdoor route.";
+ }
+
+ uses mp-afi-safi-network-config;
+ }
+
+ list aggregate-route {
+ key "prefix";
+ description
+ "A list of aggregated routes.";
+ leaf prefix {
+ type rt-types:ipv6-multicast-group-address;
+ description
+ "IPv6 multicast destination prefix.";
+ }
+
+ uses mp-afi-safi-agg-route-config;
+ }
+
+ list admin-distance-route {
+ key "prefix";
+ description
+ "A list of routes with a particular admin distance.";
+ leaf prefix {
+ type rt-types:ipv6-multicast-group-address;
+ description
+ "IPv6 multicast destination prefix.";
+ }
+ }
+
+ uses admin-distance;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-flowspec" {
+ uses flow-spec-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv4-unicast" {
+ uses global-afi-safi-vpn-network-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv6-unicast" {
+ uses global-afi-safi-vpn-network-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-unicast" {
+ uses bmp-afi-safi-common-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-multicast" {
+ uses bmp-afi-safi-common-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-unicast" {
+ uses bmp-afi-safi-common-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-multicast" {
+ uses bmp-afi-safi-common-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-default-originate-options;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-weight;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-group-filter-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-unicast" {
+ leaf nexthop-local-unchanged {
+ type boolean;
+ default "false";
+ description
+ "Configure treatment of outgoing link-local nexthop attribute.
+ When set to 'true' it leaves link-local nexthop unchanged
+ for this peer.";
+ }
+
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l2vpn-evpn" {
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-default-originate-options;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-weight;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-group-filter-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-unicast" {
+ leaf nexthop-local-unchanged {
+ type boolean;
+ default "false";
+ description
+ "Configure treatment of outgoing link-local nexthop attribute.
+ When set to 'true' it leaves link-local nexthop unchanged
+ for this peer.";
+ }
+
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn" {
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-default-originate-options;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-weight;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-group-filter-config;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-unicast" {
+ leaf nexthop-local-unchanged {
+ type boolean;
+ default "false";
+ description
+ "Configure treatment of outgoing link-local nexthop attribute.
+ When set to 'true' it leaves link-local nexthop unchanged
+ for this peer.";
+ }
+
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-multicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-labeled-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-group-orf-capability;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv4-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv6-unicast" {
+ uses structure-neighbor-group-add-paths;
+
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-prefix-limit;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-private-as;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-send-community;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-weight;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn" {
+ uses structure-neighbor-group-as-path-options;
+
+ uses structure-neighbor-group-attr-unchanged;
+
+ uses structure-neighbor-nexthop-self;
+
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-flowspec" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+ }
+}
diff --git a/yang/frr-deviations-bgp-datacenter.yang b/yang/frr-deviations-bgp-datacenter.yang
new file mode 100644
index 0000000000..9d2725b253
--- /dev/null
+++ b/yang/frr-deviations-bgp-datacenter.yang
@@ -0,0 +1,106 @@
+module frr-deviations-bgp-datacenter {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/frr-deviations-bgp-datacenter";
+ prefix frr-deviations-bgp-dc;
+
+ import frr-routing {
+ prefix frr-rt;
+ }
+
+ import frr-bgp {
+ prefix frr-bgp;
+ }
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development
+ List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module defines deviations for the frr-bgp module with
+ datacenter profile.
+
+ Copyright 2020 FRRouting
+
+ 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.
+
+ 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
+ HOLDER 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.";
+
+ revision 2019-12-03 {
+ description
+ "Initial revision.";
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:connect-retry-interval" {
+ deviate replace {
+ default "10";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:hold-time" {
+ deviate replace {
+ default "9";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:keepalive" {
+ deviate replace {
+ default "3";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:route-selection-options/frr-bgp:deterministic-med" {
+ deviate replace {
+ default "false";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:import-check" {
+ deviate replace {
+ default "true";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-hostname" {
+ deviate replace {
+ default "true";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-nexthop-hostname" {
+ deviate replace {
+ default "true";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:ebgp-requires-policy" {
+ deviate replace {
+ default "false";
+ }
+ }
+
+ deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-neighbor-config/frr-bgp:log-neighbor-changes" {
+ deviate replace {
+ default "true";
+ }
+ }
+}
diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang
index 78db201ea1..c9e09bef4b 100644
--- a/yang/frr-filter.yang
+++ b/yang/frr-filter.yang
@@ -49,28 +49,6 @@ module frr-filter {
/*
* Types.
*/
- typedef access-list-standard {
- description "Standard IPv4 access list (any, host or a prefix)";
- type uint16 {
- range "1..99 | 1300..1999";
- }
- }
-
- typedef access-list-extended {
- description
- "Extended IPv4 access list (source / destination any, hosts or prefixes)";
- type uint16 {
- range "100..199 | 2000..2699";
- }
- }
-
- typedef access-list-legacy {
- description "Standard/Extended IPv4 access list";
- type uint16 {
- range "1..199 | 1300..2699";
- }
- }
-
typedef access-list-name {
description "Access list name formatting";
type string {
@@ -103,79 +81,6 @@ module frr-filter {
* Configuration data.
*/
container lib {
- list access-list-legacy {
- description "Access list legacy instance";
-
- key "number";
-
- leaf number {
- description "Access list sequence value";
- type access-list-legacy;
- }
-
- leaf remark {
- description "Access list remark";
- type string;
- }
-
- list entry {
- description "Access list legacy entry";
-
- key "sequence";
-
- leaf sequence {
- description "Access list sequence value";
- type access-list-sequence;
- }
-
- leaf action {
- description "Access list action on match";
- type access-list-action;
- mandatory true;
- }
-
- choice value {
- description
- "Standard access list: value to match.
- Extended access list: source value to match.";
- mandatory true;
-
- leaf host {
- description "Host to match";
- type inet:ipv4-address;
- }
- leaf network {
- description "Network to match";
- type inet:ipv4-prefix;
- }
- leaf any {
- description "Match any";
- type empty;
- }
- }
-
- choice extended-value {
- when "../number >= 100 and ../number <= 199 or
- ../number >= 2000 and ../number <= 2699";
- description "Destination value to match";
- mandatory true;
-
- leaf destination-host {
- description "Host to match";
- type inet:ipv4-address;
- }
- leaf destination-network {
- description "Network to match";
- type inet:ipv4-prefix;
- }
- leaf destination-any {
- description "Match any";
- type empty;
- }
- }
- }
- }
-
list access-list {
description "Access list instance";
@@ -232,15 +137,66 @@ module frr-filter {
case ipv4-prefix {
when "../type = 'ipv4'";
- leaf ipv4-prefix {
- description "Configure IPv4 prefix to match";
- type inet:ipv4-prefix;
- }
+ choice style {
+ description "Access list entry style selection: zebra or cisco.";
+ mandatory true;
+
+ case zebra {
+ leaf ipv4-prefix {
+ description "Configure IPv4 prefix to match";
+ type inet:ipv4-prefix;
+ }
+
+ leaf ipv4-exact-match {
+ description "Exact match of prefix";
+ type boolean;
+ default false;
+ }
+ }
+ case cisco {
+ leaf host {
+ description "Host to match";
+ type inet:ipv4-address;
+ }
+ leaf network {
+ description "Network to match";
+ type inet:ipv4-prefix;
+ }
+ leaf source-any {
+ /*
+ * Was `any`, however it conflicts with `any` leaf
+ * outside this choice.
+ */
+ description "Match any";
+ type empty;
+ }
+ }
- leaf ipv4-exact-match {
- description "Exact match of prefix";
- type boolean;
- default false;
+ choice extended-value {
+ /*
+ * Legacy note: before using the new access-list format the
+ * cisco styled list only accepted identifiers using numbers
+ * and they had the following restriction:
+ *
+ * when "../number >= 100 and ../number <= 199 or
+ * ../number >= 2000 and ../number <= 2699";
+ */
+ description "Destination value to match";
+ mandatory true;
+
+ leaf destination-host {
+ description "Host to match";
+ type inet:ipv4-address;
+ }
+ leaf destination-network {
+ description "Network to match";
+ type inet:ipv4-prefix;
+ }
+ leaf destination-any {
+ description "Match any";
+ type empty;
+ }
+ }
}
}
case ipv6-prefix {
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index befdc3467d..1bb693a1ef 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -361,11 +361,18 @@ module frr-isisd {
"IS-type of this circuit.";
}
- leaf bfd-monitoring {
- type boolean;
- default "false";
- description
- "Monitor IS-IS peers on this circuit.";
+ container bfd-monitoring {
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "Monitor IS-IS peers on this circuit.";
+ }
+ leaf profile {
+ type string;
+ description
+ "Let BFD use a pre-configured profile.";
+ }
}
container csnp-interval {
diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang
index 779c56df7f..0cb0f93ee4 100644
--- a/yang/frr-nexthop.yang
+++ b/yang/frr-nexthop.yang
@@ -156,7 +156,7 @@ module frr-nexthop {
}
leaf vrf {
- type frr-vrf:vrf-ref;
+ type string;
description
"The nexthop vrf name, if different from the route.";
}
@@ -167,7 +167,7 @@ module frr-nexthop {
}
leaf interface {
- type frr-interface:interface-ref;
+ type string;
description
"The nexthop egress interface.";
}
@@ -188,6 +188,16 @@ module frr-nexthop {
"Nexthop is directly connected.";
}
+ leaf srte-color {
+ when "../nh-type = 'ip4' or
+ ../nh-type = 'ip6' or
+ ../nh-type = 'ip4-ifindex' or
+ ../nh-type = 'ip6-ifindex'";
+ type uint32;
+ description
+ "The nexthop SR-TE color";
+ }
+
uses rt-types:mpls-label-stack {
description
"Nexthop's MPLS label stack.";
@@ -236,7 +246,8 @@ module frr-nexthop {
grouping nexthop-grouping {
list nexthop {
- key "nh-type gateway interface";
+ key "nh-type vrf gateway interface";
+ min-elements 1;
description
"A list of nexthop objects.";
uses frr-nexthop-attributes;
diff --git a/yang/frr-ospfd.yang b/yang/frr-ospfd.yang
new file mode 100644
index 0000000000..324b66dd98
--- /dev/null
+++ b/yang/frr-ospfd.yang
@@ -0,0 +1,995 @@
+module frr-ospfd {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/ospfd";
+ prefix frr-ospfd;
+
+ import frr-routing {
+ prefix frr-rt;
+ }
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import frr-interface {
+ prefix frr-interface;
+ }
+
+ import frr-route-types {
+ prefix frr-route-types;
+ }
+
+ organization
+ "FRRouting";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org>
+ FRR Development List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module defines a model for managing FRR OSPFd information.
+ This YANG module augments the basic routing with additional
+ OSPF information";
+
+ revision 2020-07-21 {
+ description
+ "Initial revision.";
+ }
+
+ identity ospf {
+ base frr-rt:routing-protocol;
+ description
+ "'OSPF' routing ospf-protocol.";
+ }
+
+ /* Policy types to be removed later, once policy Yang finalized */
+ typedef rmap-ref {
+ type string;
+ }
+
+ typedef plist-ref {
+ type string;
+ }
+
+ typedef access-list-ref {
+ type string;
+ }
+
+ typedef ospf-area-id {
+ type union {
+ type inet:ipv4-address;
+ type uint32;
+ }
+ description "OSPF Area ID.";
+ }
+
+ grouping nssa-grouping {
+ container nssa {
+ presence "Present if the nssa is enabled";
+ leaf no-summary {
+ type boolean;
+ default "false";
+ description
+ "Do not inject inter-area routes into nssa";
+ }
+
+ leaf translate-always {
+ type boolean;
+ default "false";
+ description
+ "NSSA-ABR to always translate";
+ }
+
+ leaf translate-candidate {
+ type boolean;
+ default "false";
+ description
+ "NSSA-ABR for translate election";
+ }
+
+ leaf translate-never {
+ type boolean;
+ default "false";
+ description
+ "NSSA-ABR to never translate";
+ }
+ }
+ }
+
+ grouping range-grouping {
+ container ranges {
+ list range {
+ key "prefix";
+ description
+ "A list of range objects";
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "Area range prefix";
+ }
+
+ leaf advertise {
+ type boolean;
+ description
+ "Advertise this range";
+ }
+
+ leaf not-advertise {
+ type boolean;
+ default "false";
+ description
+ "Do not advertise this range";
+ }
+
+ leaf cost {
+ type uint32 {
+ range "0..16777215";
+ }
+ description
+ "Metric for this range";
+ }
+
+ leaf substitute {
+ type inet:ipv4-prefix;
+ description
+ "Network prefix to be announced instead of range";
+ }
+ }
+ }
+ }
+
+ grouping stub-grouping {
+ container stub {
+ presence "Present when area is stub";
+ leaf no-summary {
+ type boolean;
+ default "false";
+ description
+ "Do not inject inter-area routes into stub";
+ }
+ }
+ }
+
+ grouping shortcut-grouping {
+ container shortcut {
+ leaf default {
+ type boolean;
+ default "false";
+ description
+ "Default shortcutting behavior";
+ }
+
+ leaf disable {
+ type boolean;
+ description
+ "Disable shortcutting through the area";
+ }
+
+ leaf enable {
+ type boolean;
+ description
+ "Enable shortcutting through the area";
+ }
+ }
+ }
+
+ grouping authentication-group {
+ container authentication {
+ presence "Enable authentication.";
+ description
+ "Enable authentication on this virtual link.";
+ leaf message-digest {
+ type boolean;
+ description
+ "Use message-digest authentication.";
+ }
+
+ leaf null {
+ type boolean;
+ description
+ "Use null authentication.";
+ }
+ }
+
+ list message-digest-key {
+ key "key-id";
+ leaf key-id {
+ type uint8;
+ description
+ "Key id";
+ }
+
+ leaf mds-key {
+ type string;
+ description
+ "The OSPF password.";
+ }
+ }
+
+ leaf authentication-key {
+ type string;
+ description
+ "The OSPF password.";
+ }
+ }
+
+ grouping virtual-link-group {
+ list virtual-link {
+ key "neighbor area-id";
+ description
+ "Virtual link parameters.";
+ leaf neighbor {
+ type inet:ipv4-address;
+ description
+ "Router ID of the remote ABR.";
+ }
+
+ leaf area-id {
+ mandatory true;
+ type ospf-area-id;
+ }
+
+ uses authentication-group;
+
+ container timers {
+ leaf dead-interval {
+ type uint16;
+ units "seconds";
+ description
+ "Interval time after which a neighbor is declared down.";
+ }
+
+ leaf hello-interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Time between HELLO packets.";
+ }
+
+ leaf retransmit-interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Time between retransmitting lost link state advertisements.";
+ }
+
+ leaf transmit-delay {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Link state transmit delay.";
+ }
+ }
+ }
+ }
+
+ grouping area-groupings {
+ container areas {
+ list area {
+ key "area-id";
+ description
+ "A list of area objects";
+ leaf area-id {
+ type ospf-area-id;
+ }
+
+ container authentication {
+ presence "Enable authentication";
+ leaf message-digest {
+ type boolean;
+ description
+ "Use message-digest authentication";
+ }
+ }
+
+ leaf default-cost {
+ type uint32 {
+ range "0..16777215";
+ }
+ description
+ "Advertised default summary cost";
+ }
+
+ leaf export-list {
+ type access-list-ref;
+ description
+ "Filter for networks announced to other areas.";
+ }
+
+ leaf import-list {
+ type access-list-ref;
+ description
+ "Filter for networks from other areas announced to the specified one.";
+ }
+
+ container filter-list {
+ leaf prefix {
+ type plist-ref;
+ description
+ "Filter networks between OSPF areas.";
+ }
+
+ leaf in {
+ type boolean;
+ }
+
+ leaf out {
+ type boolean;
+ }
+ }
+
+ uses nssa-grouping;
+
+ uses range-grouping;
+
+ uses stub-grouping;
+
+ uses shortcut-grouping;
+
+ uses virtual-link-group;
+ }
+ }
+ }
+
+ /* router ospf attributes */
+ grouping route-ospf-leaf-attributes {
+ leaf auto-cost-reference-bandwidth {
+ type uint32 {
+ range "1..4294967";
+ }
+ units "Mbits";
+ description
+ "The reference bandwidth in terms of Mbits per second.";
+ }
+
+ leaf capability-opaque {
+ type boolean;
+ default "false";
+ description
+ "Opaque LSA.";
+ }
+
+ leaf compatible-rfc1583 {
+ type boolean;
+ description
+ "Compatible with RFC 1583.";
+ }
+
+ leaf default-metric {
+ type uint32 {
+ range "0..16777214";
+ }
+ description
+ "Metric of redistributed routes.";
+ }
+
+ leaf write-multiplier {
+ type uint8 {
+ range "1..100";
+ }
+ description
+ "Maximum number of interfaces serviced per write.";
+ }
+
+ container router-info {
+ choice router-info {
+ case as {
+ leaf as {
+ type boolean;
+ description
+ "Enable the Router Information functionality with AS flooding scope.";
+ }
+ }
+ case area {
+ leaf area {
+ type ospf-area-id;
+ description
+ "Enable the Router Information functionality with Area flooding scope.";
+ }
+ }
+ }
+ }
+ }
+
+ grouping metric-common-group {
+ leaf metric {
+ type uint32 {
+ range "0..16777214";
+ }
+ description
+ "OSPF default metric.";
+ }
+
+ leaf metric-type {
+ type enumeration {
+ enum "type-1" {
+ value 1;
+ }
+ enum "type-2" {
+ value 2;
+ }
+ }
+ description
+ "Metric type (1/2).";
+ }
+
+ leaf route-map {
+ type rmap-ref;
+ description
+ "Route map reference.";
+ }
+ }
+
+ grouping default-info-group {
+ container default-information {
+ leaf originate {
+ type boolean;
+ description
+ "Advertise a default route";
+ }
+
+ leaf always {
+ type boolean;
+ description
+ "Always advertise default route.";
+ }
+
+ uses metric-common-group;
+ }
+ }
+
+ grouping redistribute-group {
+ list redistribute {
+ key "protocol";
+ leaf protocol {
+ type frr-route-types:frr-route-types-v4;
+ description
+ "Protocol.";
+ }
+
+ uses metric-common-group;
+ }
+ }
+
+ grouping distance-group {
+ container distance {
+ leaf admin-value {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Admin value.";
+ }
+
+ container ospf {
+ leaf external {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Distance for external routes.";
+ }
+
+ leaf inter-area {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Distance for inter-area routes.";
+ }
+
+ leaf intra-area {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Distance for intra-area routes.";
+ }
+ }
+ }
+ }
+
+ grouping distribute-list-group {
+ container distribute-list {
+ list dlist {
+ key "name protocol";
+ leaf name {
+ type string;
+ description
+ "Filter networks in routing updates.";
+ }
+
+ leaf protocol {
+ type frr-route-types:frr-route-types-v4;
+ description
+ "Out protocol.";
+ }
+ }
+ }
+ }
+
+ grouping max-metric-group {
+ container max-metric {
+ container router-lsa {
+ description
+ "Advertise own Router-LSA with infinite distance (stub router).";
+ leaf administrative {
+ type boolean;
+ description
+ "Administratively applied, for an indefinite period.";
+ }
+
+ leaf on-shutdown {
+ type uint8 {
+ range "5..100";
+ }
+ description
+ "Advertise stub-router prior to full shutdown of OSPF.";
+ }
+
+ leaf on-startup {
+ type uint32 {
+ range "5..86400";
+ }
+ description
+ "Automatically advertise stub Router-LSA on startup of OSPF.";
+ }
+ }
+ }
+ }
+
+ grouping mpls-te-group {
+ container mpls-te {
+ leaf on {
+ type boolean;
+ description
+ "Enable the MPLS-TE functionality.";
+ }
+
+ leaf router-address {
+ type inet:ipv4-address;
+ description
+ "Stable IP address of the advertising router.";
+ }
+
+ container inter-as {
+ leaf as {
+ type boolean;
+ description
+ "AS native mode self originate INTER-AS LSA with Type 11 (as flooding scope).";
+ }
+
+ leaf area {
+ type ospf-area-id;
+ description
+ "AREA native mode self originate INTER-AS LSA with Type 10 (area flooding scope).";
+ }
+ }
+ }
+ }
+
+ grouping ospf-group {
+ container ospf {
+ leaf abr-type {
+ type enumeration {
+ enum "cisco" {
+ value 1;
+ description
+ "Alternative ABR, Cisco implementation.";
+ }
+ enum "ibm" {
+ value 2;
+ description
+ "Alternative ABR, IBM implementation.";
+ }
+ enum "shortcut" {
+ value 3;
+ description
+ "Shortcut ABR.";
+ }
+ enum "standard" {
+ value 4;
+ description
+ "Standard behavior (RFC2328).";
+ }
+ }
+ }
+
+ leaf opaque-lsa {
+ type boolean;
+ description
+ "Enable the Opaque-LSA capability (RFC2370)";
+ }
+
+ leaf rfc1583compatibility {
+ type boolean;
+ description
+ "Enable the RFC 1583 compatibility flag.";
+ }
+
+ leaf router-id {
+ type inet:ipv4-address;
+ description
+ "Router-id for the OSPF process.";
+ }
+
+ leaf write-multiplier {
+ type uint8 {
+ range "1..100";
+ }
+ description
+ "Write multiplier.";
+ }
+ }
+ }
+
+ grouping timer-group {
+ container timers {
+ leaf refresh-interval {
+ type uint16 {
+ range "10..1800";
+ }
+ units "seconds";
+ description
+ "The maximum time between distinct originations of any particular
+ LSA, value in units seconds.";
+ }
+
+ leaf lsa-min-arrival {
+ type uint32 {
+ range "0..600000";
+ }
+ units "milliseconds";
+ description
+ "Minimum delay in receiving new version of an LSA.";
+ }
+
+ container throttle {
+ leaf lsa-all {
+ type uint16 {
+ range "0..5000";
+ }
+ units "milliseconds";
+ description
+ "LSA delay between transmissions.";
+ }
+
+ leaf spf {
+ type uint32 {
+ range "0..600000";
+ }
+ units "milliseconds";
+ description
+ "Delay from first change received till SPF calculation.";
+ }
+ }
+ }
+ }
+
+ grouping segment-routing-group {
+ container segment-routing {
+ container global-block {
+ description
+ "Segment Routing Global Block label range.";
+ must "./upper-bound > ./lower-bound";
+ leaf lower-bound {
+ type uint32 {
+ range "0..1048575";
+ }
+ default "16000";
+ }
+
+ leaf upper-bound {
+ type uint32 {
+ range "0..1048575";
+ }
+ default "23999";
+ }
+ }
+
+ container srlb {
+ description
+ "Local blocks to be advertised.";
+ must "./upper-bound > ./lower-bound";
+ leaf lower-bound {
+ type uint32;
+ default "15000";
+ description
+ "Lower value in the label range.";
+ }
+ leaf upper-bound {
+ type uint32;
+ default "15999";
+ description
+ "Upper value in the label range.";
+ }
+ }
+
+ leaf node-msd {
+ type uint8 {
+ range "1..16";
+ }
+ description
+ "Maximum Stack Depth for this router.";
+ }
+
+ leaf on {
+ type boolean;
+ description
+ "Enable Segment Routing.";
+ }
+
+ list prefix-sid {
+ key "index";
+ leaf index {
+ type uint16;
+ description
+ "SID index for this prefix.";
+ }
+
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "Prefix SID.";
+ }
+
+ leaf last-hop-behavior {
+ type enumeration {
+ enum "explicit-null" {
+ value 0;
+ description
+ "Use explicit-null for the SID.";
+ }
+ enum "no-php" {
+ value 1;
+ description
+ "Do not use Penultimate Hop Popping (PHP)
+ for the SID.";
+ }
+ enum "php" {
+ value 2;
+ description
+ "Use PHP for the SID.";
+ }
+ }
+ default "php";
+ description
+ "Configure last hop behavior.";
+ }
+ }
+ }
+ }
+
+ grouping ospf-list-group {
+ list neighbor {
+ key "ip";
+ description
+ "Neighbor list.";
+ leaf ip {
+ type inet:ipv4-address;
+ description
+ "Neighbor IP address.";
+ }
+
+ leaf priority {
+ type uint8;
+ description
+ "Neighbor Priority.";
+ }
+
+ leaf poll-interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Dead Neighbor Polling interval.";
+ }
+ }
+
+ list network {
+ key "prefix";
+ description
+ "Enable routing on list of IP network.";
+ leaf prefix {
+ type inet:ipv4-prefix;
+ description
+ "Prefix on which routing needs to be enabled.";
+ }
+
+ leaf area {
+ type ospf-area-id;
+ description
+ "Area ID for this network.";
+ }
+ }
+
+ list passive-interface {
+ key "interface";
+ description
+ "Suppress routing updates on list interface.";
+ leaf interface {
+ type frr-interface:interface-ref;
+ description
+ "Suppress routing updates on an interface.";
+ }
+
+ leaf address {
+ type inet:ipv4-address;
+ description
+ "Interface address.";
+ }
+ }
+ }
+
+ grouping interface-ospf-attribute-group {
+ leaf area {
+ type ospf-area-id;
+ description
+ "OSPF area ID.";
+ }
+
+ uses authentication-group;
+
+ leaf cost {
+ type uint16 {
+ range "1..65535";
+ }
+ description
+ "Interface cost";
+ }
+
+ container dead-interval {
+ leaf interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ }
+
+ container minimal {
+ leaf hello-multiplier {
+ type uint8 {
+ range "1..10";
+ }
+ }
+ }
+ }
+
+ leaf hello-interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Time between HELLO packets.";
+ }
+
+ leaf retransmit-interval {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Time between retransmitting lost link state advertisements.";
+ }
+
+ leaf transmit-delay {
+ type uint16 {
+ range "1..65535";
+ }
+ units "seconds";
+ description
+ "Link state transmit delay.";
+ }
+
+ leaf mtu-ignore {
+ type boolean;
+ description
+ "Disable MTU mismatch detection on this interface.";
+ }
+
+ leaf priority {
+ type uint8;
+ description
+ "Router priority.";
+ }
+ }
+
+ grouping interface-ospf-group {
+ list instance {
+ key "id";
+ leaf id {
+ type uint16;
+ description
+ "OSPF instance ID.";
+ }
+
+ leaf bfd {
+ type boolean;
+ default "false";
+ description
+ "BFD support.";
+ }
+
+ leaf network {
+ type enumeration {
+ enum "broadcast" {
+ value 1;
+ }
+ enum "non-broadcast" {
+ value 2;
+ }
+ enum "point-to-multipoint" {
+ value 3;
+ }
+ enum "point-to-point" {
+ value 4;
+ }
+ }
+ }
+
+ uses interface-ospf-attribute-group;
+
+ list interface-address {
+ key "address";
+ leaf address {
+ type inet:ipv4-address;
+ description
+ "Address of interface";
+ }
+
+ uses interface-ospf-attribute-group;
+ }
+ }
+ }
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" {
+ container ospf {
+ when "../frr-rt:type = 'frr-ospfd:ospf'" {
+ description
+ "This container is only valid for the 'OSPF' routing
+ protocol.";
+ }
+
+ uses route-ospf-leaf-attributes;
+
+ uses default-info-group;
+
+ uses redistribute-group;
+
+ uses distance-group;
+
+ uses distribute-list-group;
+
+ uses max-metric-group;
+
+ uses mpls-te-group;
+
+ uses ospf-group;
+
+ uses timer-group;
+
+ uses segment-routing-group;
+
+ uses ospf-list-group;
+
+ uses area-groupings;
+ }
+ }
+
+ /*
+ * Per-interface configuration data
+ */
+ augment "/frr-interface:lib/frr-interface:interface" {
+ container ospf {
+ description
+ "OSPF interface parameters.";
+ uses interface-ospf-group;
+ }
+ }
+}
diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang
index f35a2976d1..b895cd12a4 100644
--- a/yang/frr-route-map.yang
+++ b/yang/frr-route-map.yang
@@ -6,14 +6,17 @@ module frr-route-map {
import ietf-inet-types {
prefix inet;
}
+
import frr-filter {
prefix filter;
}
+
import frr-interface {
prefix frr-interface;
}
- organization "FRRouting";
+ organization
+ "FRRouting";
contact
"FRR Users List: <mailto:frog@lists.frrouting.org>
FRR Development List: <mailto:dev@lists.frrouting.org>";
@@ -46,22 +49,34 @@ module frr-route-map {
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
revision 2019-07-01 {
- description "Initial revision";
+ description
+ "Initial revision";
}
/*
* Types.
*/
typedef route-map-sequence {
- description "Route map valid sequence numbers";
type uint16 {
range "1..65535";
}
+ description
+ "Route map valid sequence numbers.";
}
typedef route-map-name {
- description "Route map name format";
type string;
+ description
+ "Route map name format.";
+ }
+
+ typedef route-map-ref {
+ type leafref {
+ path "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:name";
+ require-instance false;
+ }
+ description
+ "Reference to a route-map.";
}
/*
@@ -69,34 +84,33 @@ module frr-route-map {
*/
container lib {
list route-map {
- description "Route map instance";
-
key "name";
-
+ description
+ "Route map instance.";
leaf name {
- description "Route map instance name";
type route-map-name;
+ description
+ "Route map instance name.";
}
list entry {
- description "Route map entry";
-
key "sequence";
-
+ description
+ "Route map entry.";
leaf sequence {
description
- "Route map instance priority (low number means higher priority)";
+ "Route map instance priority (low number means higher priority).";
type route-map-sequence;
}
leaf description {
- description "Route map description";
+ description "Route map description.";
type string;
}
leaf action {
description
- "Route map actions: permit (executes action), deny (quits evaluation)";
+ "Route map actions: permit (executes action), deny (quits evaluation).";
mandatory true;
type enumeration {
enum permit {
@@ -120,25 +134,25 @@ module frr-route-map {
description
"Call another route map before calling `exit-policy`. If the
called route map returns deny then this route map will also
- return deny";
+ return deny.";
type route-map-name;
}
leaf exit-policy {
- description "What do to after route map successful match, set and call";
+ description "What do to after route map successful match, set and call.";
type enumeration {
enum permit-or-deny {
- description "End route map evaluation and return";
+ description "End route map evaluation and return.";
value 0;
}
enum next {
description
- "Proceed evaluating next route map entry per sequence";
+ "Proceed evaluating next route map entry per sequence.";
value 1;
}
enum goto {
description
- "Go to route map entry with the provided sequence number";
+ "Go to route map entry with the provided sequence number.";
value 2;
}
}
@@ -148,82 +162,81 @@ module frr-route-map {
leaf goto-value {
when "../exit-policy = 'goto'";
description
- "Sequence number to jump (when using `goto` exit policy)";
+ "Sequence number to jump (when using `goto` exit policy).";
mandatory true;
type route-map-sequence;
}
list match-condition {
- description "Route map match conditions";
-
key "condition";
-
+ description
+ "Route map match conditions.";
leaf condition {
- description "Match condition";
+ description "Match condition.";
type enumeration {
enum interface {
- description "Match interface";
+ description "Match interface.";
value 0;
}
enum ipv4-address-list {
- description "Match an IPv4 access-list";
+ description "Match an IPv4 access-list.";
value 1;
}
enum ipv4-prefix-list {
- description "Match an IPv4 prefix-list";
+ description "Match an IPv4 prefix-list.";
value 2;
}
enum ipv4-next-hop-list {
- description "Match an IPv4 next-hop";
+ description "Match an IPv4 next-hop.";
value 3;
}
enum ipv4-next-hop-prefix-list {
- description "Match an IPv4 next-hop prefix list";
+ description "Match an IPv4 next-hop prefix list.";
value 4;
}
enum ipv4-next-hop-type {
- description "Match an IPv4 next-hop type";
+ description "Match an IPv4 next-hop type.";
value 5;
}
enum ipv6-address-list {
- description "Match an IPv6 access-list";
+ description "Match an IPv6 access-list.";
value 6;
}
enum ipv6-prefix-list {
- description "Match an IPv6 prefix-list";
+ description "Match an IPv6 prefix-list.";
value 7;
}
enum ipv6-next-hop-type {
- description "Match an IPv6 next-hop type";
+ description "Match an IPv6 next-hop type.";
value 8;
}
enum metric {
- description "Match a route metric";
+ description "Match a route metric.";
value 9;
}
enum tag {
- description "Match a route tag";
+ description "Match a route tag.";
value 10;
}
/* zebra specific conditions. */
enum ipv4-prefix-length {
- description "Match IPv4 prefix length";
+ description "Match IPv4 prefix length.";
value 100;
}
enum ipv6-prefix-length {
- description "Match IPv6 prefix length";
+ description "Match IPv6 prefix length.";
value 101;
}
enum ipv4-next-hop-prefix-length {
- description "Match next-hop prefix length";
+ description "Match next-hop prefix length.";
value 102;
}
enum source-protocol {
- description "Match source protocol";
+ description "Match source protocol.";
value 103;
}
enum source-instance {
- description "Match source protocol instance";
+ description "Match source protocol instance.";
value 104;
}
}
@@ -231,7 +244,7 @@ module frr-route-map {
choice condition-value {
description
- "Value to match (interpretation depends on condition type)";
+ "Value to match (interpretation depends on condition type).";
mandatory true;
case interface {
when "./condition = 'interface'";
@@ -239,20 +252,7 @@ module frr-route-map {
type string;
}
}
- case access-list-num {
- when "./condition = 'ipv4-address-list' or
- ./condition = 'ipv4-next-hop-list'";
- leaf access-list-num {
- type filter:access-list-standard;
- }
- }
- case access-list-num-extended {
- when "./condition = 'ipv4-address-list' or
- ./condition = 'ipv4-next-hop-list'";
- leaf access-list-num-extended {
- type filter:access-list-extended;
- }
- }
+
case list-name {
when "./condition = 'ipv4-address-list' or
./condition = 'ipv4-prefix-list' or
@@ -264,6 +264,7 @@ module frr-route-map {
type filter:access-list-name;
}
}
+
case ipv4-next-hop-type {
when "./condition = 'ipv4-next-hop-type'";
leaf ipv4-next-hop-type {
@@ -274,6 +275,7 @@ module frr-route-map {
}
}
}
+
case ipv6-next-hop-type {
when "./condition = 'ipv6-next-hop-type'";
leaf ipv6-next-hop-type {
@@ -284,6 +286,7 @@ module frr-route-map {
}
}
}
+
case metric {
when "./condition = 'metric'";
leaf metric {
@@ -292,6 +295,7 @@ module frr-route-map {
}
}
}
+
case tag {
when "./condition = 'tag'";
leaf tag {
@@ -304,32 +308,32 @@ module frr-route-map {
}
list set-action {
- description "Route map set actions";
+ description "Route map set actions.";
key "action";
leaf action {
- description "Action to do when the route map matches";
+ description "Action to do when the route map matches.";
type enumeration {
enum ipv4-next-hop {
- description "Set IPv4 address of the next hop";
+ description "Set IPv4 address of the next hop.";
value 0;
}
enum ipv6-next-hop {
- description "Set IPv6 address of the next hop";
+ description "Set IPv6 address of the next hop.";
value 1;
}
enum metric {
- description "Set prefix/route metric";
+ description "Set prefix/route metric.";
value 2;
}
enum tag {
- description "Set tag";
+ description "Set tag.";
value 3;
}
/* zebra specific conditions. */
enum source {
- description "Set source address for route";
+ description "Set source address for route.";
value 100;
}
}
@@ -337,69 +341,77 @@ module frr-route-map {
choice action-value {
description
- "Value to set (interpretation depends on action-type)";
+ "Value to set (interpretation depends on action-type).";
case ipv4-address {
when "./action = 'ipv4-next-hop'";
leaf ipv4-address {
- description "IPv4 address";
+ description "IPv4 address.";
type inet:ipv4-address;
}
}
+
case ipv6-address {
when "./action = 'ipv6-next-hop'";
leaf ipv6-address {
- description "IPv6 address";
+ description "IPv6 address.";
type inet:ipv6-address;
}
}
+
case metric {
when "./action = 'metric'";
choice metric-value {
- description "Metric to set or use";
+ description "Metric to set or use.";
case value {
leaf value {
- description "Use the following metric value";
+ description "Use the following metric value.";
type uint32 {
range "0..4294967295";
}
}
}
+
case add-metric {
leaf add-metric {
- description "Add unit to metric";
+ description "Add unit to metric.";
type boolean;
}
}
+
case subtract-metric {
leaf subtract-metric {
- description "Subtract unit from metric";
+ description "Subtract unit from metric.";
type boolean;
}
}
+
case use-round-trip-time {
leaf use-round-trip-time {
- description "Use the round trip time as metric";
+ description "Use the round trip time as metric.";
type boolean;
}
}
+
case add-round-trip-time {
leaf add-round-trip-time {
- description "Add round trip time to metric";
+ description "Add round trip time to metric.";
type boolean;
}
}
+
case subtract-round-trip-time {
leaf subtract-round-trip-time {
- description "Subtract round trip time to metric";
+ description "Subtract round trip time to metric.";
type boolean;
}
}
}
}
+
case tag {
when "./action = 'tag'";
leaf tag {
- description "Tag value";
+ description "Tag value.";
type uint32 {
range "0..4294967295";
}
diff --git a/yang/frr-routing.yang b/yang/frr-routing.yang
index 5a06e597e5..52607f9ad0 100644
--- a/yang/frr-routing.yang
+++ b/yang/frr-routing.yang
@@ -72,6 +72,105 @@ module frr-routing {
"This identity represents an IPv6 address family.";
}
+ identity afi-safi-type {
+ description
+ "Base identity type (AFI,SAFI) tuples for RIB";
+ }
+
+ identity ipv4-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv4 unicast address family.";
+ }
+
+ identity ipv6-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv6 unicast address family.";
+ }
+
+ identity ipv4-multicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv4 multicast address family.";
+ }
+
+ identity ipv6-multicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv6 multicast address family.";
+ }
+
+ identity ipv4-labeled-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv4 labeled unicast address family.";
+ }
+
+
+ identity ipv6-labeled-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv6 labeled unicast address family.";
+ }
+
+
+ identity l3vpn-ipv4-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the L3vpn IPv4 unicast address family.";
+ }
+
+
+ identity l3vpn-ipv6-unicast {
+ base afi-safi-type;
+ description
+ "This identity represents the L3vpn IPv6 unicast address family.";
+ }
+
+
+ identity l3vpn-ipv4-multicast {
+ base afi-safi-type;
+ description
+ "This identity represents the L3vpn IPv4 multicast address family.";
+ }
+
+
+ identity l3vpn-ipv6-multicast {
+ base afi-safi-type;
+ description
+ "This identity represents the L3vpn IPv6 multicast address family.";
+ }
+
+
+ identity l2vpn-vpls {
+ base afi-safi-type;
+ description
+ "This identity represents the L2vpn VPLS address family.";
+ }
+
+
+ identity l2vpn-evpn {
+ base afi-safi-type;
+ description
+ "This identity represents the L2vpn EVPN address family.";
+ }
+
+
+ identity ipv4-flowspec {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv4 flowspec address family.";
+ }
+
+
+ identity ipv6-flowspec {
+ base afi-safi-type;
+ description
+ "This identity represents the IPv6 flowspec address family.";
+ }
+
+
identity control-plane-protocol {
description
"Base identity from which control-plane protocol identities are
@@ -150,7 +249,7 @@ module frr-routing {
instance.";
}
leaf vrf {
- type frr-vrf:vrf-ref;
+ type string;
description
"vrf for control-plane protocol";
}
diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang
index f59158a0fd..281b4903c0 100644
--- a/yang/frr-staticd.yang
+++ b/yang/frr-staticd.yang
@@ -1,11 +1,10 @@
module frr-staticd {
- yang-version "1.1";
+ yang-version 1.1;
namespace "http://frrouting.org/yang/staticd";
-
prefix frr-staticd;
import frr-routing {
- prefix "frr-rt";
+ prefix frr-rt;
}
import frr-nexthop {
@@ -18,11 +17,9 @@ module frr-staticd {
organization
"FRRouting";
-
contact
"FRR Users List: <mailto:frog@lists.frrouting.org>
FRR Development List: <mailto:dev@lists.frrouting.org>";
-
description
"This module defines a model for managing FRR staticd information.
This YANG module augments the ietf-routing with additional
@@ -58,52 +55,77 @@ module frr-staticd {
"Initial revision.";
}
- identity static {
+ identity staticd {
base frr-rt:routing-protocol;
description
- "'Static' routing pseudo-protocol.";
+ "'Staticd' routing pseudo-protocol.";
+ }
+
+ grouping staticd-prefix-attributes {
+ list path-list {
+ key "distance";
+ leaf distance {
+ type frr-rt:administrative-distance;
+ description
+ "Admin distance associated with this route.";
+ }
+
+ leaf tag {
+ type uint32;
+ default "0";
+ description
+ "Route tag";
+ }
+
+ leaf table-id {
+ type uint32;
+ default "0";
+ description
+ "Table-id";
+ }
+
+ uses frr-nexthop:frr-nexthop;
+ }
}
- augment "/frr-rt:routing/frr-rt:control-plane-protocols/"
- + "frr-rt:control-plane-protocol" {
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" {
container staticd {
- when "../frr-rt:type = 'frr-staticd:static'" {
+ when "../frr-rt:type = 'frr-staticd:staticd'" {
description
- "This container is only valid for the 'static' routing
+ "This container is only valid for the 'staticd' routing
protocol.";
}
description
- "Support for a 'static' pseudo-protocol instance
- consists of a list of routes.";
-
- list prefix-list {
- key "destination-prefix";
+ "Support for a 'staticd' pseudo-protocol instance
+ consists of a list of routes.";
+ list route-list {
+ key "prefix afi-safi";
description
- "List of static IP routes.";
-
- leaf destination-prefix {
- type inet:ip-address;
+ "List of staticd IP routes.";
+ leaf prefix {
+ type inet:ip-prefix;
description
- "IP destination prefix.";
+ "IP prefix.";
}
-
- leaf distance {
- type frr-rt:administrative-distance;
+ leaf afi-safi {
+ type identityref {
+ base frr-rt:afi-safi-type;
+ }
description
- "Admin distance associated with this route.";
+ "AFI-SAFI type.";
}
- leaf tag {
- type uint32 {
- range "1..4294967295";
+ uses staticd-prefix-attributes;
+
+ list src-list {
+ key "src-prefix";
+ leaf src-prefix {
+ type inet:ipv6-prefix;
+ description
+ "IPv6 source prefix";
}
- description
- "Route tag";
- }
- container frr-staticd-next-hop {
- description
- "Augment static route configuration 'nexthop-list'.";
- uses frr-nexthop:frr-nexthop;
+
+ uses staticd-prefix-attributes;
}
}
}
diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang
index 159dd8f791..7762c75d68 100644
--- a/yang/frr-zebra.yang
+++ b/yang/frr-zebra.yang
@@ -77,35 +77,6 @@ module frr-zebra {
"Initial revision.";
}
- identity afi-safi-type {
- description
- "Base identity type (AFI,SAFI) tuples for RIB";
- }
-
- identity ipv4-unicast {
- base afi-safi-type;
- description
- "This identity represents the IPv4 unicast address family.";
- }
-
- identity ipv6-unicast {
- base afi-safi-type;
- description
- "This identity represents the IPv6 unicast address family.";
- }
-
- identity ipv4-multicast {
- base afi-safi-type;
- description
- "This identity represents the IPv4 multicast address family.";
- }
-
- identity ipv6-multicast {
- base afi-safi-type;
- description
- "This identity represents the IPv6 multicast address family.";
- }
-
typedef unix-timestamp {
type uint32;
units "seconds";
@@ -634,7 +605,7 @@ module frr-zebra {
key "afi-safi-name table-id";
leaf afi-safi-name {
type identityref {
- base afi-safi-type;
+ base frr-rt:afi-safi-type;
}
description
"AFI, SAFI name.";
@@ -677,6 +648,23 @@ module frr-zebra {
}
}
+ grouping vrf-vni-mapping {
+ description
+ "EVPN L3-VNI mapping corresponding to a VRF.";
+ leaf l3vni-id {
+ type vni-id-type;
+ description
+ "EVPN L3-VNI id to map to the VRF.";
+ }
+
+ leaf prefix-only {
+ type boolean;
+ default "false";
+ description
+ "EVPN asymmetric mode advertise prefix routes only.";
+ }
+ }
+
// End of zebra container
/*
* RPCs
@@ -2070,7 +2058,11 @@ module frr-zebra {
description
"Extends VRF model with Zebra-related parameters.";
container zebra {
+ description
+ "Zebra's vrf specific configuration and operational model.";
uses ribs;
+
+ uses vrf-vni-mapping;
}
}
@@ -2173,26 +2165,6 @@ module frr-zebra {
description
"Limit on the number of updates queued to the dataplane subsystem.";
}
- list vrf-vni-mapping {
- key "vrf-id";
- description
- "EVPN VNI mapping corresponding to a VRF.";
- leaf vrf-id {
- type uint32;
- description
- "The identifier for a VRF.";
- }
- leaf vni-id {
- type vni-id-type;
- description
- "The VNI id to map to the VRF.";
- }
- leaf prefix-only {
- type empty;
- description
- "Prefix routes only.";
- }
- }
/*
* Debug options
*/
diff --git a/yang/ietf/ietf-bgp-types.yang b/yang/ietf/ietf-bgp-types.yang
new file mode 100644
index 0000000000..9c7a6af76c
--- /dev/null
+++ b/yang/ietf/ietf-bgp-types.yang
@@ -0,0 +1,525 @@
+module ietf-bgp-types {
+ yang-version "1.1";
+ namespace "urn:ietf:params:xml:ns:yang:ietf-bgp-types";
+
+ prefix "bt";
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ // meta
+ organization
+ "IETF IDR Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/idr>
+ WG List: <idr@ietf.org>
+
+ Authors: Mahesh Jethanandani (mjethanandani at gmail.com),
+ Keyur Patel (keyur at arrcus.com),
+ Susan Hares (shares at ndzh.com),
+ Jeffrey Haas (jhaas at pfrc.org).";
+ description
+ "This module contains general data definitions for use in BGP
+ policy. It can be imported by modules that make use of BGP
+ attributes";
+
+ revision 2019-10-03 {
+ description
+ "Initial Version";
+ reference
+ "RFC XXX, BGP Model for Service Provider Network.";
+ }
+
+ identity bgp-capability {
+ description "Base identity for a BGP capability";
+ }
+
+ identity mp-bgp {
+ base bgp-capability;
+ description
+ "Multi-protocol extensions to BGP";
+ reference
+ "RFC 4760";
+ }
+
+ identity route-refresh {
+ base bgp-capability;
+ description
+ "The BGP route-refresh functionality";
+ reference
+ "RFC2918";
+ }
+
+ identity asn32 {
+ base bgp-capability;
+ description
+ "4-byte (32-bit) AS number functionality";
+ reference
+ "RFC6793";
+ }
+
+ identity graceful-restart {
+ base bgp-capability;
+ description
+ "Graceful restart functionality";
+ reference
+ "RFC4724";
+ }
+
+ identity add-paths {
+ base bgp-capability;
+ description
+ "BGP add-paths";
+ reference
+ "RFC 7911.";
+ }
+
+ identity afi-safi-type {
+ description
+ "Base identity type for AFI,SAFI tuples for BGP-4";
+ reference
+ "RFC4760 - multi-protocol extensions for BGP-4";
+ }
+
+ identity ipv4-unicast {
+ base afi-safi-type;
+ description
+ "IPv4 unicast (AFI,SAFI = 1,1)";
+ reference
+ "RFC4760";
+ }
+
+ identity ipv6-unicast {
+ base afi-safi-type;
+ description
+ "IPv6 unicast (AFI,SAFI = 2,1)";
+ reference
+ "RFC4760";
+ }
+
+ identity ipv4-labeled-unicast {
+ base afi-safi-type;
+ description
+ "Labeled IPv4 unicast (AFI,SAFI = 1,4)";
+ reference
+ "RFC3107";
+ }
+
+ identity ipv6-labeled-unicast {
+ base afi-safi-type;
+ description
+ "Labeled IPv6 unicast (AFI,SAFI = 2,4)";
+ reference
+ "RFC3107";
+ }
+
+ identity l3vpn-ipv4-unicast {
+ base afi-safi-type;
+ description
+ "Unicast IPv4 MPLS L3VPN (AFI,SAFI = 1,128)";
+ reference
+ "RFC4364";
+ }
+
+ identity l3vpn-ipv6-unicast {
+ base afi-safi-type;
+ description
+ "Unicast IPv6 MPLS L3VPN (AFI,SAFI = 2,128)";
+ reference
+ "RFC4659";
+ }
+
+ identity l3vpn-ipv4-multicast {
+ base afi-safi-type;
+ description
+ "Multicast IPv4 MPLS L3VPN (AFI,SAFI = 1,129)";
+ reference
+ "RFC6514";
+ }
+
+ identity l3vpn-ipv6-multicast {
+ base afi-safi-type;
+ description
+ "Multicast IPv6 MPLS L3VPN (AFI,SAFI = 2,129)";
+ reference
+ "RFC6514";
+ }
+
+ identity l2vpn-vpls {
+ base afi-safi-type;
+ description
+ "BGP-signalled VPLS (AFI,SAFI = 25,65)";
+ reference
+ "RFC4761";
+ }
+
+ identity l2vpn-evpn {
+ base afi-safi-type;
+ description
+ "BGP MPLS Based Ethernet VPN (AFI,SAFI = 25,70)";
+ }
+
+ identity bgp-well-known-std-community {
+ description
+ "Base identity for reserved communities within the standard
+ community space defined by RFC1997. These communities must
+ fall within the range 0xFFFF0000 to 0xFFFFFFFF";
+ reference
+ "RFC 1997: BGP Communities Attribute.";
+ }
+
+ identity no-export {
+ base bgp-well-known-std-community;
+ description
+ "Do not export NLRI received carrying this community outside
+ the bounds of this autonomous system, or this confederation if
+ the local autonomous system is a confederation member AS. This
+ community has a value of 0xFFFFFF01.";
+ reference
+ "RFC 1997: BGP Communities Attribute.";
+ }
+
+ identity no-advertise {
+ base bgp-well-known-std-community;
+ description
+ "All NLRI received carrying this community must not be
+ advertised to other BGP peers. This community has a value of
+ 0xFFFFFF02.";
+ reference
+ "RFC 1997: BGP Communities Attribute.";
+ }
+
+ identity no-export-subconfed {
+ base bgp-well-known-std-community;
+ description
+ "All NLRI received carrying this community must not be
+ advertised to external BGP peers - including over confederation
+ sub-AS boundaries. This community has a value of 0xFFFFFF03.";
+ reference
+ "RFC 1997: BGP Communities Attribute.";
+ }
+
+ identity no-peer {
+ base bgp-well-known-std-community;
+ description
+ "An autonomous system receiving NLRI tagged with this community
+ is advised not to re-advertise the NLRI to external bi-lateral
+ peer autonomous systems. An AS may also filter received NLRI
+ from bilateral peer sessions when they are tagged with this
+ community value";
+ reference
+ "RFC 3765: NOPEER Community for BGP.";
+ }
+ identity as-path-segment-type {
+ description
+ "Base AS Path Segment Type. In [BGP-4], the path segment type
+ is a 1-octet field with the following values defined.";
+ reference
+ "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3.";
+ }
+
+ identity as-set {
+ base as-path-segment-type;
+ description
+ "Unordered set of autonomous systems that a route in the UPDATE
+ message has traversed.";
+ reference
+ "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3.";
+ }
+
+ identity as-sequence {
+ base as-path-segment-type;
+ description
+ "Ordered set of autonomous systems that a route in the UPDATE
+ message has traversed.";
+ reference
+ "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3.";
+ }
+
+ identity as-confed-sequence {
+ base as-path-segment-type;
+ description
+ "Ordered set of Member Autonomous Systems in the local
+ confederation that the UPDATE message has traversed.";
+ reference
+ "RFC 5065, Autonomous System Configuration for BGP.";
+ }
+
+ identity as-confed-set {
+ base as-path-segment-type;
+ description
+ "Unordered set of Member Autonomous Systems in the local
+ confederation that the UPDATE message has traversed.";
+ reference
+ "RFC 5065, Autonomous System Configuration for BGP.";
+ }
+
+ /*
+ * Features.
+ */
+ feature send-communities {
+ description
+ "Enable the propogation of communities.";
+ }
+
+ feature ttl-security {
+ description
+ "BGP Time To Live (TTL) security check support.";
+ reference
+ "RFC 5082, The Generalized TTL Security Mechanism (GTSM)";
+ }
+
+ feature bfd {
+ description
+ "Support for BFD detection of BGP neighbor reachability.";
+ reference
+ "RFC 5880, Bidirectional Forward Detection (BFD),
+ RFC 5881, Bidirectional Forward Detection for IPv4 and IPv6
+ (Single Hop).
+ RFC 5883, Bidirectional Forwarding Detection (BFD) for Multihop
+ Paths";
+ }
+
+ typedef bgp-session-direction {
+ type enumeration {
+ enum INBOUND {
+ description
+ "Refers to all NLRI received from the BGP peer";
+ }
+ enum OUTBOUND {
+ description
+ "Refers to all NLRI advertised to the BGP peer";
+ }
+ }
+ description
+ "Type to describe the direction of NLRI transmission";
+ }
+
+ typedef bgp-well-known-community-type {
+ type identityref {
+ base bgp-well-known-std-community;
+ }
+ description
+ "Type definition for well-known IETF community attribute
+ values";
+ reference
+ "IANA Border Gateway Protocol (BGP) Well Known Communities";
+ }
+
+ typedef bgp-std-community-type {
+ // TODO: further refine restrictions and allowed patterns
+ // 4-octet value:
+ // <as number> 2 octets
+ // <community value> 2 octets
+ type union {
+ type uint32 {
+ // per RFC 1997, 0x00000000 - 0x0000FFFF and 0xFFFF0000 -
+ // 0xFFFFFFFF are reserved
+ range "65536..4294901759"; // 0x00010000..0xFFFEFFFF
+ }
+ type string {
+ pattern '([0-9]+:[0-9]+)';
+ }
+ }
+ description
+ "Type definition for standard community attributes";
+ reference
+ "RFC 1997 - BGP Communities Attribute";
+ }
+
+ typedef bgp-ext-community-type {
+ // TODO: needs more work to make this more precise given the
+ // variability of extended community attribute specifications
+ // 8-octet value:
+ // <type> 2 octects
+ // <value> 6 octets
+
+ type union {
+ type string {
+ // Type 1: 2-octet global and 4-octet local
+ // (AS number) (Integer)
+ pattern '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' +
+ '[1-9][0-9]{1,4}|[0-9]):' +
+ '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' +
+ '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])';
+ }
+ type string {
+ // Type 2: 4-octet global and 2-octet local
+ // (ipv4-address) (integer)
+ pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' +
+ '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' +
+ '2[0-4][0-9]|25[0-5]):' +
+ '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' +
+ '[1-9][0-9]{1,4}|[0-9])';
+ }
+ type string {
+ // route-target with Type 1
+ // route-target:(ASN):(local-part)
+ pattern 'route\-target:(6[0-5][0-5][0-3][0-5]|' +
+ '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' +
+ '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' +
+ '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])';
+ }
+ type string {
+ // route-target with Type 2
+ // route-target:(IPv4):(local-part)
+ pattern 'route\-target:' +
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' +
+ '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' +
+ '2[0-4][0-9]|25[0-5]):' +
+ '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' +
+ '[1-9][0-9]{1,4}|[0-9])';
+ }
+ type string {
+ // route-origin with Type 1
+ pattern 'route\-origin:(6[0-5][0-5][0-3][0-5]|' +
+ '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' +
+ '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' +
+ '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])';
+ }
+ type string {
+ // route-origin with Type 2
+ pattern 'route\-origin:' +
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' +
+ '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' +
+ '2[0-4][0-9]|25[0-5]):' +
+ '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' +
+ '[1-9][0-9]{1,4}|[0-9])';
+ }
+ }
+ description
+ "Type definition for extended community attributes";
+ reference
+ "RFC 4360 - BGP Extended Communities Attribute";
+ }
+
+ typedef bgp-community-regexp-type {
+ // TODO: needs more work to decide what format these regexps can
+ // take.
+ type string;
+ description
+ "Type definition for communities specified as regular
+ expression patterns";
+ }
+
+ typedef bgp-origin-attr-type {
+ type enumeration {
+ enum igp {
+ description "Origin of the NLRI is internal";
+ }
+ enum egp {
+ description "Origin of the NLRI is EGP";
+ }
+ enum incomplete {
+ description "Origin of the NLRI is neither IGP or EGP";
+ }
+ }
+ description
+ "Type definition for standard BGP origin attribute";
+ reference
+ "RFC 4271 - A Border Gateway Protocol 4 (BGP-4), Sec 4.3";
+ }
+
+ typedef peer-type {
+ type enumeration {
+ enum internal {
+ description
+ "internal (iBGP) peer";
+ }
+ enum external {
+ description
+ "external (eBGP) peer";
+ }
+ enum confederation {
+ description
+ "Confederation as peer";
+ }
+ }
+ description
+ "Labels a peer or peer group as explicitly internal,
+ external or confederation.";
+ }
+
+ identity REMOVE_PRIVATE_AS_OPTION {
+ description
+ "Base identity for options for removing private autonomous
+ system numbers from the AS_PATH attribute";
+ }
+
+ identity PRIVATE_AS_REMOVE_ALL {
+ base REMOVE_PRIVATE_AS_OPTION;
+ description
+ "Strip all private autonomous system numbers from the AS_PATH.
+ This action is performed regardless of the other content of the
+ AS_PATH attribute, and for all instances of private AS numbers
+ within that attribute.";
+ }
+
+ identity PRIVATE_AS_REPLACE_ALL {
+ base REMOVE_PRIVATE_AS_OPTION;
+ description
+ "Replace all instances of private autonomous system numbers in
+ the AS_PATH with the local BGP speaker's autonomous system
+ number. This action is performed regardless of the other
+ content of the AS_PATH attribute, and for all instances of
+ private AS number within that attribute.";
+ }
+
+ typedef remove-private-as-option {
+ type identityref {
+ base REMOVE_PRIVATE_AS_OPTION;
+ }
+ description
+ "Set of options for configuring how private AS path numbers
+ are removed from advertisements";
+ }
+
+ typedef percentage {
+ type uint8 {
+ range "0..100";
+ }
+ description
+ "Integer indicating a percentage value";
+ }
+
+ typedef rr-cluster-id-type {
+ type union {
+ type uint32;
+ type inet:ipv4-address;
+ }
+ description
+ "Union type for route reflector cluster ids:
+ option 1: 4-byte number
+ option 2: IP address";
+ }
+
+ typedef community-type {
+ type bits {
+ bit standard {
+ position 0;
+ description
+ "Send only standard communities.";
+ reference
+ "RFC 1997: BGP Communities Attribute.";
+ }
+ bit extended {
+ description
+ "Send only extended communities.";
+ reference
+ "RFC 4360: BGP Extended Communities Attribute.";
+ }
+ bit large {
+ description
+ "Send only large communities.";
+ reference
+ "RFC 8092: BGP Large Communities Attribute.";
+ }
+ }
+ description
+ "Type describing variations of community attributes.
+ The community types can be combined and a value of 0
+ implies 'none'";
+ }
+}
diff --git a/yang/subdir.am b/yang/subdir.am
index 6caf9fc5f3..6aae0e4701 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -29,10 +29,7 @@ dist_yangmodels_DATA += yang/frr-vrf.yang
dist_yangmodels_DATA += yang/frr-route-types.yang
dist_yangmodels_DATA += yang/frr-routing.yang
dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang
-dist_yangmodels_DATA += yang/frr-igmp.yang
dist_yangmodels_DATA += yang/ietf/ietf-interfaces.yang
-dist_yangmodels_DATA += yang/frr-pim.yang
-dist_yangmodels_DATA += yang/frr-pim-rp.yang
if BFDD
dist_yangmodels_DATA += yang/frr-bfdd.yang
@@ -65,3 +62,9 @@ endif
if ZEBRA
dist_yangmodels_DATA += yang/frr-zebra.yang
endif
+
+if PIMD
+dist_yangmodels_DATA += yang/frr-igmp.yang
+dist_yangmodels_DATA += yang/frr-pim.yang
+dist_yangmodels_DATA += yang/frr-pim-rp.yang
+endif
diff --git a/zebra/connected.c b/zebra/connected.c
index a982ac9b46..3b9ebe14a4 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -312,13 +312,11 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
if (IPV4_ADDR_SAME(addr, dest))
flog_warn(
EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER,
- "warning: interface %s has same local and peer "
- "address %s, routing protocols may malfunction",
+ "warning: interface %s has same local and peer address %s, routing protocols may malfunction",
ifp->name, inet_ntoa(*addr));
} else {
zlog_debug(
- "warning: %s called for interface %s "
- "with peer flag set, but no peer address supplied",
+ "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);
}
@@ -328,8 +326,7 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN)
&& if_is_pointopoint(ifp))
zlog_debug(
- "warning: PtP interface %s with addr %s/%d needs a "
- "peer address",
+ "warning: PtP interface %s with addr %s/%d needs a peer address",
ifp->name, inet_ntoa(*addr), prefixlen);
/* Label of this address. */
diff --git a/zebra/debug.c b/zebra/debug.c
index c920fca5ff..8c53ab73e4 100644
--- a/zebra/debug.c
+++ b/zebra/debug.c
@@ -40,6 +40,7 @@ unsigned long zebra_debug_pw;
unsigned long zebra_debug_dplane;
unsigned long zebra_debug_mlag;
unsigned long zebra_debug_nexthop;
+unsigned long zebra_debug_evpn_mh;
DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty));
@@ -109,6 +110,18 @@ DEFUN_NOSH (show_debugging_zebra,
else if (IS_ZEBRA_DEBUG_NHG)
vty_out(vty, " Zebra nexthop debugging is on\n");
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ vty_out(vty, " Zebra EVPN-MH ethernet segment debugging is on\n");
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
+ vty_out(vty, " Zebra EVPN-MH nexthop debugging is on\n");
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ vty_out(vty, " Zebra EVPN-MH MAC debugging is on\n");
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ vty_out(vty, " Zebra EVPN-MH Neigh debugging is on\n");
+
hook_call(zebra_debug_show_debugging, vty);
return CMD_SUCCESS;
}
@@ -320,6 +333,53 @@ DEFPY (debug_zebra_mlag,
return CMD_SUCCESS;
}
+DEFPY (debug_zebra_evpn_mh,
+ debug_zebra_evpn_mh_cmd,
+ "[no$no] debug zebra evpn mh <es$es|mac$mac|neigh$neigh|nh$nh>",
+ NO_STR
+ DEBUG_STR
+ "Zebra configuration\n"
+ "EVPN\n"
+ "Multihoming\n"
+ "Ethernet Segment Debugging\n"
+ "MAC Debugging\n"
+ "Neigh Debugging\n"
+ "Nexthop Debugging\n")
+{
+ if (es) {
+ if (no)
+ UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES);
+ else
+ SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES);
+ }
+
+ if (mac) {
+ if (no)
+ UNSET_FLAG(zebra_debug_evpn_mh,
+ ZEBRA_DEBUG_EVPN_MH_MAC);
+ else
+ SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_MAC);
+ }
+
+ if (neigh) {
+ if (no)
+ UNSET_FLAG(zebra_debug_evpn_mh,
+ ZEBRA_DEBUG_EVPN_MH_NEIGH);
+ else
+ SET_FLAG(zebra_debug_evpn_mh,
+ ZEBRA_DEBUG_EVPN_MH_NEIGH);
+ }
+
+ if (nh) {
+ if (no)
+ UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH);
+ else
+ SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_zebra_events,
no_debug_zebra_events_cmd,
"no debug zebra events",
@@ -553,6 +613,22 @@ static int config_write_debug(struct vty *vty)
vty_out(vty, "debug zebra mlag\n");
write++;
}
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES) {
+ vty_out(vty, "debug zebra evpn mh es\n");
+ write++;
+ }
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ vty_out(vty, "debug zebra evpn mh nh\n");
+ write++;
+ }
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ vty_out(vty, "debug zebra evpn mh mac\n");
+ write++;
+ }
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) {
+ vty_out(vty, "debug zebra evpn mh neigh\n");
+ write++;
+ }
if (IS_ZEBRA_DEBUG_PW) {
vty_out(vty, "debug zebra pseudowires\n");
write++;
@@ -589,6 +665,7 @@ void zebra_debug_init(void)
zebra_debug_pw = 0;
zebra_debug_dplane = 0;
zebra_debug_mlag = 0;
+ zebra_debug_evpn_mh = 0;
zebra_debug_nht = 0;
zebra_debug_nexthop = 0;
@@ -619,6 +696,7 @@ void zebra_debug_init(void)
install_element(ENABLE_NODE, &no_debug_zebra_rib_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_fpm_cmd);
install_element(ENABLE_NODE, &no_debug_zebra_dplane_cmd);
+ install_element(ENABLE_NODE, &debug_zebra_evpn_mh_cmd);
install_element(CONFIG_NODE, &debug_zebra_events_cmd);
install_element(CONFIG_NODE, &debug_zebra_nht_cmd);
@@ -643,4 +721,5 @@ void zebra_debug_init(void)
install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd);
install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd);
install_element(CONFIG_NODE, &debug_zebra_mlag_cmd);
+ install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd);
}
diff --git a/zebra/debug.h b/zebra/debug.h
index e513f8865d..8402224f19 100644
--- a/zebra/debug.h
+++ b/zebra/debug.h
@@ -62,6 +62,11 @@ extern "C" {
#define ZEBRA_DEBUG_NHG 0x01
#define ZEBRA_DEBUG_NHG_DETAILED 0x02
+#define ZEBRA_DEBUG_EVPN_MH_ES 0x01
+#define ZEBRA_DEBUG_EVPN_MH_NH 0x02
+#define ZEBRA_DEBUG_EVPN_MH_MAC 0x04
+#define ZEBRA_DEBUG_EVPN_MH_NEIGH 0x08
+
/* Debug related macro. */
#define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT)
@@ -100,6 +105,15 @@ extern "C" {
#define IS_ZEBRA_DEBUG_NHG_DETAIL \
(zebra_debug_nexthop & ZEBRA_DEBUG_NHG_DETAILED)
+#define IS_ZEBRA_DEBUG_EVPN_MH_ES \
+ (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_ES)
+#define IS_ZEBRA_DEBUG_EVPN_MH_NH \
+ (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NH)
+#define IS_ZEBRA_DEBUG_EVPN_MH_MAC \
+ (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_MAC)
+#define IS_ZEBRA_DEBUG_EVPN_MH_NEIGH \
+ (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NEIGH)
+
extern unsigned long zebra_debug_event;
extern unsigned long zebra_debug_packet;
extern unsigned long zebra_debug_kernel;
@@ -112,6 +126,7 @@ extern unsigned long zebra_debug_pw;
extern unsigned long zebra_debug_dplane;
extern unsigned long zebra_debug_mlag;
extern unsigned long zebra_debug_nexthop;
+extern unsigned long zebra_debug_evpn_mh;
extern void zebra_debug_init(void);
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index ef208bdc83..5bf47580a8 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -44,6 +44,8 @@
#include "zebra/interface.h"
#include "zebra/zebra_dplane.h"
#include "zebra/zebra_router.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mac.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
@@ -72,6 +74,7 @@ struct fpm_nl_ctx {
int socket;
bool disabled;
bool connecting;
+ bool nhg_complete;
bool rib_complete;
bool rmac_complete;
bool use_nhg;
@@ -149,8 +152,25 @@ enum fpm_nl_events {
FNE_RESET_COUNTERS,
/* Toggle next hop group feature. */
FNE_TOGGLE_NHG,
+ /* Reconnect request by our own code to avoid races. */
+ FNE_INTERNAL_RECONNECT,
+
+ /* Next hop groups walk finished. */
+ FNE_NHG_FINISHED,
+ /* RIB walk finished. */
+ FNE_RIB_FINISHED,
+ /* RMAC walk finished. */
+ FNE_RMAC_FINISHED,
};
+#define FPM_RECONNECT(fnc) \
+ thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \
+ FNE_INTERNAL_RECONNECT, &(fnc)->t_event)
+
+#define WALK_FINISH(fnc, ev) \
+ thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \
+ (ev), NULL)
+
/*
* Prototypes.
*/
@@ -428,7 +448,18 @@ static int fpm_connect(struct thread *t);
static void fpm_reconnect(struct fpm_nl_ctx *fnc)
{
- /* Grab the lock to empty the stream and stop the zebra thread. */
+ /* Cancel all zebra threads first. */
+ thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL);
+ thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL);
+ thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL);
+ thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL);
+ thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL);
+ thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL);
+
+ /*
+ * Grab the lock to empty the streams (data plane might try to
+ * enqueue updates while we are closing).
+ */
frr_mutex_lock_autounlock(&fnc->obuf_mutex);
/* Avoid calling close on `-1`. */
@@ -442,13 +473,6 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc)
THREAD_OFF(fnc->t_read);
THREAD_OFF(fnc->t_write);
- thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL);
- thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL);
- thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL);
- thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL);
- thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL);
- thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL);
-
/* FPM is disabled, don't attempt to connect. */
if (fnc->disabled)
return;
@@ -465,6 +489,13 @@ static int fpm_read(struct thread *t)
/* Let's ignore the input at the moment. */
rv = stream_read_try(fnc->ibuf, fnc->socket,
STREAM_WRITEABLE(fnc->ibuf));
+ /* We've got an interruption. */
+ if (rv == -2) {
+ /* Schedule next read. */
+ thread_add_read(fnc->fthread->master, fpm_read, fnc,
+ fnc->socket, &fnc->t_read);
+ return 0;
+ }
if (rv == 0) {
atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1,
memory_order_relaxed);
@@ -472,19 +503,15 @@ static int fpm_read(struct thread *t)
if (IS_ZEBRA_DEBUG_FPM)
zlog_debug("%s: connection closed", __func__);
- fpm_reconnect(fnc);
+ FPM_RECONNECT(fnc);
return 0;
}
if (rv == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK
- || errno == EINTR)
- return 0;
-
atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1,
memory_order_relaxed);
zlog_warn("%s: connection failure: %s", __func__,
strerror(errno));
- fpm_reconnect(fnc);
+ FPM_RECONNECT(fnc);
return 0;
}
stream_reset(fnc->ibuf);
@@ -525,33 +552,15 @@ static int fpm_write(struct thread *t)
&fnc->counters.connection_errors, 1,
memory_order_relaxed);
- fpm_reconnect(fnc);
+ FPM_RECONNECT(fnc);
return 0;
}
fnc->connecting = false;
- /*
- * Walk the route tables to send old information before starting
- * to send updated information.
- *
- * NOTE 1:
- * RIB table walk is called after the next group table walk
- * ends.
- *
- * NOTE 2:
- * Don't attempt to go through next hop group table if we were
- * explictly told to not use it.
- */
- if (fnc->use_nhg)
- thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0,
- &fnc->t_nhgwalk);
- else
- thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0,
- &fnc->t_ribwalk);
-
- thread_add_timer(zrouter.master, fpm_rmac_send, fnc, 0,
- &fnc->t_rmacwalk);
+ /* Permit receiving messages now. */
+ thread_add_read(fnc->fthread->master, fpm_read, fnc,
+ fnc->socket, &fnc->t_read);
}
frr_mutex_lock_autounlock(&fnc->obuf_mutex);
@@ -589,8 +598,9 @@ static int fpm_write(struct thread *t)
memory_order_relaxed);
zlog_warn("%s: connection failure: %s", __func__,
strerror(errno));
- fpm_reconnect(fnc);
- break;
+
+ FPM_RECONNECT(fnc);
+ return 0;
}
/* Account all bytes sent. */
@@ -661,18 +671,19 @@ static int fpm_connect(struct thread *t)
fnc->connecting = (errno == EINPROGRESS);
fnc->socket = sock;
- thread_add_read(fnc->fthread->master, fpm_read, fnc, sock,
- &fnc->t_read);
+ if (!fnc->connecting)
+ thread_add_read(fnc->fthread->master, fpm_read, fnc, sock,
+ &fnc->t_read);
thread_add_write(fnc->fthread->master, fpm_write, fnc, sock,
&fnc->t_write);
/* Mark all routes as unsent. */
- thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0,
- &fnc->t_nhgreset);
- thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
- &fnc->t_ribreset);
- thread_add_timer(zrouter.master, fpm_rmac_reset, fnc, 0,
- &fnc->t_rmacreset);
+ if (fnc->use_nhg)
+ thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0,
+ &fnc->t_nhgreset);
+ else
+ thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
+ &fnc->t_ribreset);
return 0;
}
@@ -904,10 +915,11 @@ static int fpm_nhg_send(struct thread *t)
dplane_ctx_fini(&fna.ctx);
/* We are done sending next hops, lets install the routes now. */
- if (fna.complete)
- thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0,
- &fnc->t_ribwalk);
- else /* Otherwise reschedule next hop group again. */
+ if (fna.complete) {
+ WALK_FINISH(fnc, FNE_NHG_FINISHED);
+ thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
+ &fnc->t_ribreset);
+ } else /* Otherwise reschedule next hop group again. */
thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0,
&fnc->t_nhgwalk);
@@ -963,7 +975,11 @@ static int fpm_rib_send(struct thread *t)
dplane_ctx_fini(&ctx);
/* All RIB routes sent! */
- fnc->rib_complete = true;
+ WALK_FINISH(fnc, FNE_RIB_FINISHED);
+
+ /* Schedule next event: RMAC reset. */
+ thread_add_event(zrouter.master, fpm_rmac_reset, fnc, 0,
+ &fnc->t_rmacreset);
return 0;
}
@@ -975,6 +991,7 @@ struct fpm_rmac_arg {
struct zebra_dplane_ctx *ctx;
struct fpm_nl_ctx *fnc;
zebra_l3vni_t *zl3vni;
+ bool complete;
};
static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg)
@@ -988,7 +1005,7 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg)
bool sticky;
/* Entry already sent. */
- if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT))
+ if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT) || !fra->complete)
return;
sticky = !!CHECK_FLAG(zrmac->flags,
@@ -1000,10 +1017,12 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg)
dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL);
dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if,
zif->brslave_info.br_if, vid,
- &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky);
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky,
+ 0 /*nhg*/, 0 /*update_flags*/);
if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) {
thread_add_timer(zrouter.master, fpm_rmac_send,
fra->fnc, 1, &fra->fnc->t_rmacwalk);
+ fra->complete = false;
}
}
@@ -1022,9 +1041,14 @@ static int fpm_rmac_send(struct thread *t)
fra.fnc = THREAD_ARG(t);
fra.ctx = dplane_ctx_alloc();
+ fra.complete = true;
hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra);
dplane_ctx_fini(&fra.ctx);
+ /* RMAC walk completed. */
+ if (fra.complete)
+ WALK_FINISH(fra.fnc, FNE_RMAC_FINISHED);
+
return 0;
}
@@ -1041,7 +1065,14 @@ static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg)
static int fpm_nhg_reset(struct thread *t)
{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+
+ fnc->nhg_complete = false;
hash_iterate(zrouter.nhgs_id, fpm_nhg_reset_cb, NULL);
+
+ /* Schedule next step: send next hop groups. */
+ thread_add_event(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk);
+
return 0;
}
@@ -1070,6 +1101,9 @@ static int fpm_rib_reset(struct thread *t)
}
}
+ /* Schedule next step: send RIB routes. */
+ thread_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk);
+
return 0;
}
@@ -1092,8 +1126,15 @@ static void fpm_unset_l3vni_table(struct hash_bucket *backet, void *arg)
static int fpm_rmac_reset(struct thread *t)
{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+
+ fnc->rmac_complete = false;
hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL);
+ /* Schedule next event: send RMAC entries. */
+ thread_add_event(zrouter.master, fpm_rmac_send, fnc, 0,
+ &fnc->t_rmacwalk);
+
return 0;
}
@@ -1174,6 +1215,30 @@ static int fpm_process_event(struct thread *t)
fpm_reconnect(fnc);
break;
+ case FNE_INTERNAL_RECONNECT:
+ fpm_reconnect(fnc);
+ break;
+
+ case FNE_NHG_FINISHED:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: next hop groups walk finished",
+ __func__);
+
+ fnc->nhg_complete = true;
+ break;
+ case FNE_RIB_FINISHED:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: RIB walk finished", __func__);
+
+ fnc->rib_complete = true;
+ break;
+ case FNE_RMAC_FINISHED:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: RMAC walk finished", __func__);
+
+ fnc->rmac_complete = true;
+ break;
+
default:
if (IS_ZEBRA_DEBUG_FPM)
zlog_debug("%s: unhandled event %d", __func__, event);
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index a29a810902..5cd3e69299 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -70,6 +70,7 @@
#include "zebra/if_netlink.h"
#include "zebra/zebra_errors.h"
#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn_mh.h"
extern struct zebra_privs_t zserv_privs;
@@ -245,6 +246,26 @@ static enum zebra_link_type netlink_to_zebra_link_type(unsigned int hwt)
}
}
+static inline void zebra_if_set_ziftype(struct interface *ifp,
+ zebra_iftype_t zif_type,
+ zebra_slave_iftype_t zif_slave_type)
+{
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ zif->zif_slave_type = zif_slave_type;
+
+ if (zif->zif_type != zif_type) {
+ zif->zif_type = zif_type;
+ /* If the if_type has been set to bond initialize ES info
+ * against it. XXX - note that we don't handle the case where
+ * a zif changes from bond to non-bond; it is really
+ * an unexpected/error condition.
+ */
+ zebra_evpn_if_init(zif);
+ }
+}
+
static void netlink_determine_zebra_iftype(const char *kind,
zebra_iftype_t *zif_type)
{
@@ -513,8 +534,7 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data,
if (!attr[IFLA_VXLAN_LINK]) {
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("IFLA_VXLAN_LINK missing "
- "from VXLAN IF message");
+ zlog_debug("IFLA_VXLAN_LINK missing from VXLAN IF message");
} else {
ifindex_link =
*(ifindex_t *)RTA_DATA(attr[IFLA_VXLAN_LINK]);
@@ -558,6 +578,74 @@ static void netlink_interface_update_l2info(struct interface *ifp,
}
}
+static int netlink_bridge_vxlan_update(struct interface *ifp,
+ struct rtattr *af_spec)
+{
+ struct rtattr *aftb[IFLA_BRIDGE_MAX + 1];
+ struct bridge_vlan_info *vinfo;
+ vlanid_t access_vlan;
+
+ /* There is a 1-to-1 mapping of VLAN to VxLAN - hence
+ * only 1 access VLAN is accepted.
+ */
+ memset(aftb, 0, sizeof(aftb));
+ parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, af_spec);
+ if (!aftb[IFLA_BRIDGE_VLAN_INFO])
+ return 0;
+
+ vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]);
+ if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID))
+ return 0;
+
+ access_vlan = (vlanid_t)vinfo->vid;
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan,
+ ifp->name, ifp->ifindex);
+ zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan);
+ return 0;
+}
+
+static void netlink_bridge_vlan_update(struct interface *ifp,
+ struct rtattr *af_spec)
+{
+ struct rtattr *i;
+ int rem;
+ uint16_t vid_range_start = 0;
+ struct zebra_if *zif;
+ bitfield_t old_vlan_bitmap;
+ struct bridge_vlan_info *vinfo;
+
+ zif = (struct zebra_if *)ifp->info;
+
+ /* cache the old bitmap addrs */
+ old_vlan_bitmap = zif->vlan_bitmap;
+ /* create a new bitmap space for re-eval */
+ bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX);
+
+ for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec);
+ RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+
+ if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
+ continue;
+
+ vinfo = RTA_DATA(i);
+
+ if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+ vid_range_start = vinfo->vid;
+ continue;
+ }
+
+ if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
+ vid_range_start = vinfo->vid;
+
+ zebra_vlan_bitmap_compute(ifp, vid_range_start, vinfo->vid);
+ }
+
+ zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap);
+
+ bf_free(old_vlan_bitmap);
+}
+
static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
int startup)
{
@@ -565,12 +653,8 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
struct ifinfomsg *ifi;
struct rtattr *tb[IFLA_MAX + 1];
struct interface *ifp;
- struct rtattr *aftb[IFLA_BRIDGE_MAX + 1];
- struct {
- uint16_t flags;
- uint16_t vid;
- } * vinfo;
- vlanid_t access_vlan;
+ struct zebra_if *zif;
+ struct rtattr *af_spec;
/* Fetch name and ifindex */
ifi = NLMSG_DATA(h);
@@ -588,30 +672,22 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
ifi->ifi_index);
return 0;
}
- if (!IS_ZEBRA_IF_VXLAN(ifp))
- return 0;
/* We are only interested in the access VLAN i.e., AF_SPEC */
- if (!tb[IFLA_AF_SPEC])
- return 0;
+ af_spec = tb[IFLA_AF_SPEC];
+ if (!af_spec)
+ return 0;
- /* There is a 1-to-1 mapping of VLAN to VxLAN - hence
- * only 1 access VLAN is accepted.
- */
- memset(aftb, 0, sizeof(aftb));
- parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]);
- if (!aftb[IFLA_BRIDGE_VLAN_INFO])
- return 0;
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ return netlink_bridge_vxlan_update(ifp, af_spec);
- vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]);
- if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID))
- return 0;
+ /* build vlan bitmap associated with this interface if that
+ * device type is interested in the vlans
+ */
+ zif = (struct zebra_if *)ifp->info;
+ if (bf_is_inited(zif->vlan_bitmap))
+ netlink_bridge_vlan_update(ifp, af_spec);
- access_vlan = (vlanid_t)vinfo->vid;
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan,
- name, ifi->ifi_index);
- zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan);
return 0;
}
@@ -722,10 +798,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (tb[IFLA_LINK])
link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]);
- if (tb[IFLA_LINK_NETNSID]) {
+ if (tb[IFLA_LINK_NETNSID])
link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]);
- link_nsid = ns_id_get_absolute(ns_id, link_nsid);
- }
/* Add interface.
* We add by index first because in some cases such as the master
@@ -733,9 +807,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
* back references on the slave interfaces is painful if not done
* this way, i.e. by creating by ifindex.
*/
- ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id, name);
+ ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id);
set_ifindex(ifp, ifi->ifi_index, zns); /* add it to ns struct */
+ if_set_name(ifp, name);
+
ifp->flags = ifi->ifi_flags & 0x0000fffff;
ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0;
@@ -772,7 +848,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA],
1, link_nsid);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
- zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id);
+ zebra_l2if_update_bridge_slave(ifp, bridge_ifindex);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex);
@@ -911,7 +987,8 @@ int kernel_interface_set_master(struct interface *master,
}
/* Interface address modification. */
-static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
int bytelen;
const struct prefix *p;
@@ -921,64 +998,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
- char buf[NL_PKT_BUF_SIZE];
- } req;
+ char buf[0];
+ } *req = buf;
+
+ if (buflen < sizeof(*req))
+ return 0;
p = dplane_ctx_get_intf_addr(ctx);
- memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
+ memset(req, 0, sizeof(*req));
bytelen = (p->family == AF_INET ? 4 : 16);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL)
cmd = RTM_NEWADDR;
else
cmd = RTM_DELADDR;
- req.n.nlmsg_type = cmd;
- req.ifa.ifa_family = p->family;
+ req->n.nlmsg_type = cmd;
+ req->ifa.ifa_family = p->family;
- req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
+ req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen))
+ return 0;
if (p->family == AF_INET) {
if (dplane_ctx_intf_is_connected(ctx)) {
p = dplane_ctx_get_intf_dest(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_ADDRESS,
- &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS,
+ &p->u.prefix, bytelen))
+ return 0;
} else if (cmd == RTM_NEWADDR) {
struct in_addr broad = {
.s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr,
p->prefixlen)
};
- nl_attr_put(&req.n, sizeof(req), IFA_BROADCAST, &broad,
- bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad,
+ bytelen))
+ return 0;
}
}
/* p is now either address or destination/bcast addr */
- req.ifa.ifa_prefixlen = p->prefixlen;
+ req->ifa.ifa_prefixlen = p->prefixlen;
if (dplane_ctx_intf_is_secondary(ctx))
- SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY);
+ SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY);
if (dplane_ctx_intf_has_label(ctx)) {
label = dplane_ctx_get_intf_label(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LABEL, label,
- strlen(label) + 1);
+ if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label,
+ strlen(label) + 1))
+ return 0;
}
- return netlink_talk_info(netlink_talk_filter, &req.n,
- dplane_ctx_get_ns(ctx), 0);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
-enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return (netlink_address_ctx(ctx) == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder,
+ false);
}
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
@@ -1264,10 +1349,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (tb[IFLA_LINK])
link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]);
- if (tb[IFLA_LINK_NETNSID]) {
+ if (tb[IFLA_LINK_NETNSID])
link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]);
- link_nsid = ns_id_get_absolute(ns_id, link_nsid);
- }
+
if (tb[IFLA_IFALIAS]) {
desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]);
}
@@ -1307,8 +1391,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Add interface notification from kernel */
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d "
- "sl_type %d master %u flags 0x%x",
+ "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u flags 0x%x",
name, ifi->ifi_index, vrf_id, zif_type,
zif_slave_type, bridge_ifindex,
ifi->ifi_flags);
@@ -1356,16 +1439,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
1, link_nsid);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ bridge_ifindex);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex);
} else if (ifp->vrf_id != vrf_id) {
/* VRF change for an interface. */
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "RTM_NEWLINK vrf-change for %s(%u) "
- "vrf_id %u -> %u flags 0x%x",
+ "RTM_NEWLINK vrf-change for %s(%u) vrf_id %u -> %u flags 0x%x",
name, ifp->ifindex, ifp->vrf_id, vrf_id,
ifi->ifi_flags);
@@ -1376,8 +1457,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Interface update. */
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "RTM_NEWLINK update for %s(%u) "
- "sl_type %d master %u flags 0x%x",
+ "RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%x",
name, ifp->ifindex, zif_slave_type,
bridge_ifindex, ifi->ifi_flags);
@@ -1460,8 +1540,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
0, link_nsid);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave)
zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ bridge_ifindex);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave)
zebra_l2if_update_bond_slave(ifp, bond_ifindex);
}
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index 29fd2aca35..0bbba81ca6 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -32,6 +32,10 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
+extern enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Set protodown status of interface.
*
diff --git a/zebra/interface.c b/zebra/interface.c
index 982a63a022..b824e313ec 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -51,6 +51,7 @@
#include "zebra/interface.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
+#include "zebra/zebra_evpn_mh.h"
DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information")
@@ -127,6 +128,7 @@ static int if_zebra_new_hook(struct interface *ifp)
struct zebra_if *zebra_if;
zebra_if = XCALLOC(MTYPE_ZINFO, sizeof(struct zebra_if));
+ zebra_if->ifp = ifp;
zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC;
zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF;
@@ -238,6 +240,8 @@ static int if_zebra_delete_hook(struct interface *ifp)
list_delete(&rtadv->AdvDNSSLList);
#endif /* HAVE_RTADV */
+ zebra_evpn_if_cleanup(zebra_if);
+
if_nhg_dependents_release(ifp);
zebra_if_nhg_dependents_free(zebra_if);
@@ -385,8 +389,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc)
rn = route_node_lookup(zebra_if->ipv4_subnets, &cp);
if (!(rn && rn->info)) {
flog_warn(EC_ZEBRA_REMOVE_ADDR_UNKNOWN_SUBNET,
- "Trying to remove an address from an unknown subnet."
- " (please report this bug)");
+ "Trying to remove an address from an unknown subnet. (please report this bug)");
return -1;
}
route_unlock_node(rn);
@@ -400,8 +403,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc)
if (!listnode_lookup(addr_list, ifc)) {
flog_warn(
EC_ZEBRA_REMOVE_UNREGISTERED_ADDR,
- "Trying to remove an address from a subnet where it is not"
- " currently registered. (please report this bug)");
+ "Trying to remove an address from a subnet where it is not currently registered. (please report this bug)");
return -1;
}
@@ -618,8 +620,7 @@ void if_add_update(struct interface *ifp)
if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) {
if (IS_ZEBRA_DEBUG_KERNEL) {
zlog_debug(
- "interface %s vrf %s(%u) index %d is shutdown. "
- "Won't wake it up.",
+ "interface %s vrf %s(%u) index %d is shutdown. Won't wake it up.",
ifp->name, VRF_LOGNAME(zvrf->vrf),
ifp->vrf_id, ifp->ifindex);
}
@@ -834,6 +835,7 @@ void if_delete_update(struct interface *ifp)
memset(&zif->l2info, 0, sizeof(union zebra_l2if_info));
memset(&zif->brslave_info, 0,
sizeof(struct zebra_l2info_brslave));
+ zebra_evpn_if_cleanup(zif);
}
if (!ifp->configured) {
@@ -1075,6 +1077,8 @@ void if_up(struct interface *ifp)
} else if (IS_ZEBRA_IF_MACVLAN(ifp))
zebra_vxlan_macvlan_up(ifp);
+ if (zif->es_info.es)
+ zebra_evpn_es_if_oper_state_change(zif, true /*up*/);
}
/* Interface goes down. We have to manage different behavior of based
@@ -1109,6 +1113,8 @@ void if_down(struct interface *ifp)
} else if (IS_ZEBRA_IF_MACVLAN(ifp))
zebra_vxlan_macvlan_down(ifp);
+ if (zif->es_info.es)
+ zebra_evpn_es_if_oper_state_change(zif, false /*up*/);
/* Notify to the protocol daemons. */
zebra_interface_down_update(ifp);
@@ -1236,23 +1242,6 @@ static void nbr_connected_dump_vty(struct vty *vty,
vty_out(vty, "\n");
}
-static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type)
-{
- switch (zif_slave_type) {
- case ZEBRA_IF_SLAVE_BRIDGE:
- return "Bridge";
- case ZEBRA_IF_SLAVE_VRF:
- return "Vrf";
- case ZEBRA_IF_SLAVE_BOND:
- return "Bond";
- case ZEBRA_IF_SLAVE_OTHER:
- return "Other";
- case ZEBRA_IF_SLAVE_NONE:
- return "None";
- }
- return "None";
-}
-
static const char *zebra_ziftype_2str(zebra_iftype_t zif_type)
{
switch (zif_type) {
@@ -1480,9 +1469,6 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
vty_out(vty, " Interface Type %s\n",
zebra_ziftype_2str(zebra_if->zif_type));
- vty_out(vty, " Interface Slave Type %s\n",
- zebra_zifslavetype_2str(zebra_if->zif_slave_type));
-
if (IS_ZEBRA_IF_BRIDGE(ifp)) {
struct zebra_l2info_bridge *bridge_info;
@@ -1550,6 +1536,8 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
}
}
+ zebra_evpn_if_es_print(vty, zebra_if);
+
if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
if (zebra_if->link)
vty_out(vty, " Parent interface: %s\n", zebra_if->link->name);
@@ -1625,14 +1613,12 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
#ifdef HAVE_PROC_NET_DEV
/* Statistics print out using proc file system. */
vty_out(vty,
- " %lu input packets (%lu multicast), %lu bytes, "
- "%lu dropped\n",
+ " %lu input packets (%lu multicast), %lu bytes, %lu dropped\n",
ifp->stats.rx_packets, ifp->stats.rx_multicast,
ifp->stats.rx_bytes, ifp->stats.rx_dropped);
vty_out(vty,
- " %lu input errors, %lu length, %lu overrun,"
- " %lu CRC, %lu frame\n",
+ " %lu input errors, %lu length, %lu overrun, %lu CRC, %lu frame\n",
ifp->stats.rx_errors, ifp->stats.rx_length_errors,
ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors,
ifp->stats.rx_frame_errors);
@@ -1645,8 +1631,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
ifp->stats.tx_dropped);
vty_out(vty,
- " %lu output errors, %lu aborted, %lu carrier,"
- " %lu fifo, %lu heartbeat\n",
+ " %lu output errors, %lu aborted, %lu carrier, %lu fifo, %lu heartbeat\n",
ifp->stats.tx_errors, ifp->stats.tx_aborted_errors,
ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors,
ifp->stats.tx_heartbeat_errors);
@@ -1658,8 +1643,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
#ifdef HAVE_NET_RT_IFLIST
/* Statistics print out using sysctl (). */
vty_out(vty,
- " input packets %llu, bytes %llu, dropped %llu,"
- " multicast packets %llu\n",
+ " input packets %llu, bytes %llu, dropped %llu, multicast packets %llu\n",
(unsigned long long)ifp->stats.ifi_ipackets,
(unsigned long long)ifp->stats.ifi_ibytes,
(unsigned long long)ifp->stats.ifi_iqdrops,
@@ -1669,8 +1653,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
(unsigned long long)ifp->stats.ifi_ierrors);
vty_out(vty,
- " output packets %llu, bytes %llu,"
- " multicast packets %llu\n",
+ " output packets %llu, bytes %llu, multicast packets %llu\n",
(unsigned long long)ifp->stats.ifi_opackets,
(unsigned long long)ifp->stats.ifi_obytes,
(unsigned long long)ifp->stats.ifi_omcasts);
@@ -3596,7 +3579,7 @@ static int if_config_write(struct vty *vty)
}
hook_call(zebra_if_config_wr, vty, ifp);
-
+ zebra_evpn_mh_if_write(vty, ifp);
link_params_config_write(vty, ifp);
vty_endframe(vty, "!\n");
@@ -3672,4 +3655,7 @@ void zebra_if_init(void)
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);
+
+ /* setup EVPN MH elements */
+ zebra_evpn_interface_init();
}
diff --git a/zebra/interface.h b/zebra/interface.h
index 2dad0c3bb2..1a8e3caed5 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -25,6 +25,7 @@
#include "redistribute.h"
#include "vrf.h"
#include "hook.h"
+#include "bitfield.h"
#include "zebra/zebra_l2.h"
#include "zebra/zebra_nhg_private.h"
@@ -42,6 +43,8 @@ extern "C" {
#define IF_ZEBRA_SHUTDOWN_OFF 0
#define IF_ZEBRA_SHUTDOWN_ON 1
+#define IF_VLAN_BITMAP_MAX 4096
+
#if defined(HAVE_RTADV)
/* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */
struct rtadvconf {
@@ -272,8 +275,19 @@ typedef enum {
struct irdp_interface;
+/* Ethernet segment info used for setting up EVPN multihoming */
+struct zebra_evpn_es;
+struct zebra_es_if_info {
+ struct ethaddr sysmac;
+ uint32_t lid; /* local-id; has to be unique per-ES-sysmac */
+ struct zebra_evpn_es *es; /* local ES */
+};
+
/* `zebra' daemon local interface structure. */
struct zebra_if {
+ /* back pointer to the interface */
+ struct interface *ifp;
+
/* Shutdown configuration. */
uint8_t shutdown;
@@ -347,6 +361,12 @@ struct zebra_if {
struct zebra_l2info_bondslave bondslave_info;
+ /* ethernet segment */
+ struct zebra_es_if_info es_info;
+
+ /* bitmap of vlans associated with this interface */
+ bitfield_t vlan_bitmap;
+
/* Link fields - for sub-interfaces. */
ifindex_t link_ifindex;
struct interface *link;
@@ -370,17 +390,6 @@ DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
DECLARE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp),
(vty, ifp))
-static inline void zebra_if_set_ziftype(struct interface *ifp,
- zebra_iftype_t zif_type,
- zebra_slave_iftype_t zif_slave_type)
-{
- struct zebra_if *zif;
-
- zif = (struct zebra_if *)ifp->info;
- zif->zif_type = zif_type;
- zif->zif_slave_type = zif_slave_type;
-}
-
#define IS_ZEBRA_IF_VRF(ifp) \
(((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF)
diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c
index 1a45328248..3d6a29bec6 100644
--- a/zebra/ipforward_solaris.c
+++ b/zebra/ipforward_solaris.c
@@ -72,8 +72,7 @@ static int solaris_nd(const int cmd, const char *parameter, const int value)
snprintf(nd_buf, ND_BUFFER_SIZE, "%s", parameter);
else {
flog_err_sys(EC_LIB_SYSTEM_CALL,
- "internal error - inappropriate command given to "
- "solaris_nd()%s:%d",
+ "internal error - inappropriate command given to solaris_nd()%s:%d",
__FILE__, __LINE__);
return -1;
}
diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c
index 87a1f5fdc7..2ab5fd3a4c 100644
--- a/zebra/irdp_interface.c
+++ b/zebra/irdp_interface.c
@@ -224,8 +224,7 @@ static void irdp_if_start(struct interface *ifp, int multicast,
}
if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) {
flog_warn(EC_ZEBRA_IRDP_CANNOT_ACTIVATE_IFACE,
- "IRDP: Cannot activate interface %s (cannot create "
- "IRDP socket)",
+ "IRDP: Cannot activate interface %s (cannot create IRDP socket)",
ifp->name);
return;
}
@@ -503,8 +502,7 @@ DEFUN (ip_irdp_minadvertinterval,
return CMD_SUCCESS;
} else {
vty_out(vty,
- "%% MinAdvertInterval must be less than or equal to "
- "MaxAdvertInterval\n");
+ "%% MinAdvertInterval must be less than or equal to MaxAdvertInterval\n");
return CMD_WARNING_CONFIG_FAILED;
}
}
@@ -528,8 +526,7 @@ DEFUN (ip_irdp_maxadvertinterval,
return CMD_SUCCESS;
} else {
vty_out(vty,
- "%% MaxAdvertInterval must be greater than or equal to "
- "MinAdvertInterval\n");
+ "%% MaxAdvertInterval must be greater than or equal to MinAdvertInterval\n");
return CMD_WARNING_CONFIG_FAILED;
}
}
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index a4d22c12a4..d0c1bc812d 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -84,6 +84,27 @@
#define RTPROT_MROUTED 17
#endif
+#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE)
+
+/*
+ * We limit the batch's size to a number smaller than the length of the
+ * underlying buffer since the last message that wouldn't fit the batch would go
+ * over the upper boundary and then it would have to be encoded again into a new
+ * buffer. If the difference between the limit and the length of the buffer is
+ * big enough (bigger than the biggest Netlink message) then this situation
+ * won't occur.
+ */
+#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE)
+
+/*
+ * For every request sent to the kernel that has failed we get an error message,
+ * which contains a standard netlink message header and the payload consisting
+ * of an error code and the original netlink mesage. So the receiving buffer
+ * must be at least as big as the transmitting buffer increased by some space
+ * for headers.
+ */
+#define NL_BATCH_RX_BUFSIZE (NL_DEFAULT_BATCH_BUFSIZE + NL_PKT_BUF_SIZE)
+
static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_DELROUTE, "RTM_DELROUTE"},
{RTM_GETROUTE, "RTM_GETROUTE"},
@@ -151,6 +172,62 @@ extern uint32_t nl_rcvbufsize;
extern struct zebra_privs_t zserv_privs;
+DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers")
+
+size_t nl_batch_tx_bufsize;
+char *nl_batch_tx_buf;
+
+char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE];
+
+_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE;
+_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+
+struct nl_batch {
+ void *buf;
+ size_t bufsiz;
+ size_t limit;
+
+ void *buf_head;
+ size_t curlen;
+ size_t msgcnt;
+
+ const struct zebra_dplane_info *zns;
+
+ struct dplane_ctx_q ctx_list;
+
+ /*
+ * Pointer to the queue of completed contexts outbound back
+ * towards the dataplane module.
+ */
+ struct dplane_ctx_q *ctx_out_q;
+};
+
+int netlink_config_write_helper(struct vty *vty)
+{
+ uint32_t size =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ if (size != NL_DEFAULT_BATCH_BUFSIZE
+ || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD)
+ vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size,
+ threshold);
+
+ return 0;
+}
+
+void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set)
+{
+ if (!set) {
+ size = NL_DEFAULT_BATCH_BUFSIZE;
+ threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+ }
+
+ atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed);
+ atomic_store_explicit(&nl_batch_send_threshold, threshold,
+ memory_order_relaxed);
+}
int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
@@ -1008,9 +1085,10 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
* startup -> Are we reading in under startup conditions
* This is passed through eventually to filter.
*/
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup)
+static int
+netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
+ struct nlmsghdr *n, const struct zebra_dplane_info *dp_info,
+ int startup)
{
const struct nlsock *nl;
@@ -1080,6 +1158,328 @@ int netlink_request(struct nlsock *nl, void *req)
return 0;
}
+static int nl_batch_read_resp(struct nl_batch *bth)
+{
+ struct nlmsghdr *h;
+ struct sockaddr_nl snl;
+ struct msghdr msg;
+ int status, seq;
+ const struct nlsock *nl;
+ struct zebra_dplane_ctx *ctx;
+ bool ignore_msg;
+
+ nl = &(bth->zns->nls);
+
+ msg.msg_name = (void *)&snl;
+ msg.msg_namelen = sizeof(snl);
+
+ status = netlink_recv_msg(nl, msg, nl_batch_rx_buf,
+ sizeof(nl_batch_rx_buf));
+ if (status == -1 || status == 0)
+ return status;
+
+ for (h = (struct nlmsghdr *)nl_batch_rx_buf;
+ (status >= 0 && NLMSG_OK(h, (unsigned int)status));
+ h = NLMSG_NEXT(h, status)) {
+ ignore_msg = false;
+ seq = h->nlmsg_seq;
+ /*
+ * Find the corresponding context object. Received responses are
+ * in the same order as requests we sent, so we can simply
+ * iterate over the context list and match responses with
+ * requests at same time.
+ */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+
+ /* We have found corresponding context object. */
+ if (dplane_ctx_get_ns(ctx)->nls.seq == seq)
+ break;
+
+ /*
+ * 'update' context objects take two consecutive
+ * sequence numbers.
+ */
+ if (dplane_ctx_is_update(ctx)
+ && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) {
+ /*
+ * This is the situation where we get a response
+ * to a message that should be ignored.
+ */
+ ignore_msg = true;
+ break;
+ }
+ }
+
+ if (ignore_msg)
+ continue;
+
+ /*
+ * We received a message with the sequence number that isn't
+ * associated with any dplane context object.
+ */
+ if (ctx == NULL) {
+ zlog_debug(
+ "%s: skipping unassociated response, seq number %d NS %u",
+ __func__, h->nlmsg_seq, bth->zns->ns_id);
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ int err = netlink_parse_error(nl, h, bth->zns, 0);
+
+ if (err == -1)
+ dplane_ctx_set_status(
+ ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ zlog_debug("%s: netlink error message seq=%d ",
+ __func__, h->nlmsg_seq);
+ continue;
+ }
+
+ /*
+ * If we get here then we did not receive neither the ack nor
+ * the error and instead received some other message in an
+ * unexpected way.
+ */
+ zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u",
+ __func__, h->nlmsg_type,
+ nl_msg_type_to_str(h->nlmsg_type), bth->zns->ns_id);
+ }
+
+ return 0;
+}
+
+static void nl_batch_reset(struct nl_batch *bth)
+{
+ bth->buf_head = bth->buf;
+ bth->curlen = 0;
+ bth->msgcnt = 0;
+ bth->zns = NULL;
+
+ TAILQ_INIT(&(bth->ctx_list));
+}
+
+static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q)
+{
+ /*
+ * If the size of the buffer has changed, free and then allocate a new
+ * one.
+ */
+ size_t bufsize =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ if (bufsize != nl_batch_tx_bufsize) {
+ if (nl_batch_tx_buf)
+ XFREE(MTYPE_NL_BUF, nl_batch_tx_buf);
+
+ nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize);
+ nl_batch_tx_bufsize = bufsize;
+ }
+
+ bth->buf = nl_batch_tx_buf;
+ bth->bufsiz = bufsize;
+ bth->limit = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ bth->ctx_out_q = ctx_out_q;
+
+ nl_batch_reset(bth);
+}
+
+static void nl_batch_send(struct nl_batch *bth)
+{
+ struct zebra_dplane_ctx *ctx;
+ bool err = false;
+
+ if (bth->curlen != 0 && bth->zns != NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu",
+ __func__, bth->zns->nls.name, bth->curlen,
+ bth->msgcnt);
+
+ if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen)
+ == -1)
+ err = true;
+
+ if (!err) {
+ if (nl_batch_read_resp(bth) == -1)
+ err = true;
+ }
+ }
+
+ /* Move remaining contexts to the outbound queue. */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ if (err)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+ }
+
+ nl_batch_reset(bth);
+}
+
+enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res)
+{
+ int seq;
+ ssize_t size;
+ struct nlmsghdr *msgh;
+
+ size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen);
+
+ /*
+ * If there was an error while encoding the message (other than buffer
+ * overflow) then return an error.
+ */
+ if (size < 0)
+ return FRR_NETLINK_ERROR;
+
+ /*
+ * If the message doesn't fit entirely in the buffer then send the batch
+ * and retry.
+ */
+ if (size == 0) {
+ nl_batch_send(bth);
+ size = (*msg_encoder)(ctx, bth->buf_head,
+ bth->bufsiz - bth->curlen);
+ /*
+ * If the message doesn't fit in the empty buffer then just
+ * return an error.
+ */
+ if (size <= 0)
+ return FRR_NETLINK_ERROR;
+ }
+
+ seq = dplane_ctx_get_ns(ctx)->nls.seq;
+ if (ignore_res)
+ seq++;
+
+ msgh = (struct nlmsghdr *)bth->buf_head;
+ msgh->nlmsg_seq = seq;
+ msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
+
+ bth->zns = dplane_ctx_get_ns(ctx);
+ bth->buf_head = ((char *)bth->buf_head) + size;
+ bth->curlen += size;
+ bth->msgcnt++;
+
+ return FRR_NETLINK_QUEUED;
+}
+
+static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ if (dplane_ctx_is_skip_kernel(ctx))
+ return FRR_NETLINK_SUCCESS;
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ return netlink_put_route_update_msg(bth, ctx);
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ return netlink_put_nexthop_update_msg(bth, ctx);
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ return netlink_put_lsp_update_msg(bth, ctx);
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ return netlink_put_pw_update_msg(bth, ctx);
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ return netlink_put_address_update_msg(bth, ctx);
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ return netlink_put_mac_update_msg(bth, ctx);
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NEIGH_DISCOVER:
+ return netlink_put_neigh_update_msg(bth, ctx);
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ return netlink_put_rule_update_msg(bth, ctx);
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ return FRR_NETLINK_SUCCESS;
+
+ case DPLANE_OP_NONE:
+ return FRR_NETLINK_ERROR;
+ }
+
+ return FRR_NETLINK_ERROR;
+}
+
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct nl_batch batch;
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum netlink_msg_status res;
+
+ TAILQ_INIT(&handled_list);
+ nl_batch_init(&batch, &handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ if (batch.zns != NULL
+ && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id)
+ nl_batch_send(&batch);
+
+ /*
+ * Assume all messages will succeed and then mark only the ones
+ * that failed.
+ */
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+
+ res = nl_put_msg(&batch, ctx);
+
+ dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx);
+ if (res == FRR_NETLINK_ERROR)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ if (batch.curlen > batch.limit)
+ nl_batch_send(&batch);
+ }
+
+ nl_batch_send(&batch);
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
/* Exported interface function. This function simply calls
netlink_socket (). */
void kernel_init(struct zebra_ns *zns)
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index bd8159faf3..c02e16480b 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -98,13 +98,49 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup);
extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl,
struct zebra_ns *zns, int startup);
-/* Version with 'info' struct only */
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup);
-
extern int netlink_request(struct nlsock *nl, void *req);
+enum netlink_msg_status {
+ FRR_NETLINK_SUCCESS,
+ FRR_NETLINK_ERROR,
+ FRR_NETLINK_QUEUED,
+};
+
+struct nl_batch;
+
+/*
+ * netlink_batch_add_msg - add message to the netlink batch using dplane
+ * context object.
+ *
+ * @ctx: Dataplane context
+ * @msg_encoder: A function that encodes dplane context object into
+ * netlink message. Should take dplane context object,
+ * pointer to a buffer and buffer's length as parameters
+ * and should return -1 on error, 0 on buffer overflow or
+ * size of the encoded message.
+ * @ignore_res: Whether the result of this message should be ignored.
+ * This should be used in some 'update' cases where we
+ * need to send two messages for one context object.
+ *
+ * Return: Status of the message.
+ */
+extern enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res);
+
+/*
+ * Vty/cli apis
+ */
+extern int netlink_config_write_helper(struct vty *vty);
+
+/*
+ * Configure size of the batch buffer and sending threshold. If 'unset', reset
+ * to default value.
+ */
+extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold,
+ bool set);
+
#endif /* HAVE_NETLINK */
#ifdef __cplusplus
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 950690b943..4c29b999f0 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -712,8 +712,7 @@ int ifm_read(struct if_msghdr *ifm)
{
if (ifp->ifindex != ifm->ifm_index) {
zlog_debug(
- "%s: index mismatch, ifname %s, ifp index %d, "
- "ifm index %d",
+ "%s: index mismatch, ifname %s, ifp index %d, ifm index %d",
__func__, ifp->name, ifp->ifindex,
ifm->ifm_index);
return -1;
@@ -833,9 +832,7 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr,
? ip_masklen(mask->sin.sin_addr)
: ip6_masklen(mask->sin6.sin6_addr);
zlog_debug(
- "%s: ifindex %d, ifname %s, ifam_addrs {%s}, "
- "ifam_flags 0x%x, addr %s/%d broad %s dst %s "
- "gateway %s",
+ "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s",
__func__, ifm->ifam_index,
(ifnlen ? ifname : "(nil)"),
rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)),
@@ -978,8 +975,7 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest,
/* rt_msghdr version check. */
if (rtm->rtm_version != RTM_VERSION)
flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH,
- "Routing message version different %d should be %d."
- "This may cause problem\n",
+ "Routing message version different %d should be %d.This may cause problem\n",
rtm->rtm_version, RTM_VERSION);
/* Be sure structure is cleared */
@@ -1468,4 +1464,99 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
return;
}
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum zebra_dplane_result res;
+
+ TAILQ_INIT(&handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ /*
+ * A previous provider plugin may have asked to skip the
+ * kernel update.
+ */
+ if (dplane_ctx_is_skip_kernel(ctx)) {
+ res = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ goto skip_one;
+ }
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ res = kernel_route_update(ctx);
+ break;
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ res = kernel_nexthop_update(ctx);
+ break;
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ res = kernel_lsp_update(ctx);
+ break;
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ res = kernel_pw_update(ctx);
+ break;
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ res = kernel_address_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ res = kernel_mac_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NEIGH_DISCOVER:
+ res = kernel_neigh_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ res = kernel_pbr_rule_update(ctx);
+ break;
+
+ /* Ignore 'notifications' - no-op */
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ res = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ break;
+
+ default:
+ res = ZEBRA_DPLANE_REQUEST_FAILURE;
+ break;
+ }
+
+ skip_one:
+ dplane_ctx_set_status(ctx, res);
+
+ dplane_ctx_enqueue_tail(&handled_list, ctx);
+ }
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
#endif /* !HAVE_NETLINK */
diff --git a/zebra/label_manager.c b/zebra/label_manager.c
index 93736e672a..d312a661f3 100644
--- a/zebra/label_manager.c
+++ b/zebra/label_manager.c
@@ -240,7 +240,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
if (lmc->proto != NO_PROTO)
return NULL;
- if (end < lmc->end) {
+ if (end <= lmc->end) {
last_node = node;
break;
}
diff --git a/zebra/main.c b/zebra/main.c
index 71c7ebb62f..64746f7166 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -35,6 +35,7 @@
#include "vrf.h"
#include "libfrr.h"
#include "routemap.h"
+#include "routing_nb.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_errors.h"
@@ -56,6 +57,7 @@
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_nb.h"
#include "zebra/zebra_opaque.h"
+#include "zebra/zebra_srte.h"
#if defined(HANDLE_NETLINK_FUZZING)
#include "zebra/kernel_netlink.h"
@@ -185,7 +187,7 @@ static void sigint(void)
vrf_terminate();
rtadv_terminate();
- ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
+ ns_walk_func(zebra_ns_early_shutdown);
zebra_ns_notify_close();
access_list_reset();
@@ -216,7 +218,7 @@ int zebra_finalize(struct thread *dummy)
zlog_info("Zebra final shutdown");
/* Final shutdown of ns resources */
- ns_walk_func(zebra_ns_final_shutdown, NULL, NULL);
+ ns_walk_func(zebra_ns_final_shutdown);
/* Stop dplane thread and finish any cleanup */
zebra_dplane_shutdown();
@@ -258,14 +260,14 @@ static const struct frr_yang_module_info *const zebra_yang_modules[] = {
&frr_route_map_info,
&frr_zebra_info,
&frr_vrf_info,
+ &frr_routing_info,
};
FRR_DAEMON_INFO(
zebra, ZEBRA, .vty_port = ZEBRA_VTY_PORT, .flags = FRR_NO_ZCLIENT,
.proghelp =
- "Daemon which manages kernel routing table management "
- "and\nredistribution between different routing protocols.",
+ "Daemon which manages kernel routing table management and\nredistribution between different routing protocols.",
.signals = zebra_signals, .n_signals = array_size(zebra_signals),
@@ -415,12 +417,12 @@ int main(int argc, char **argv)
rib_init();
zebra_if_init();
zebra_debug_init();
- router_id_cmd_init();
/*
* Initialize NS( and implicitly the VRF module), and make kernel
* routing socket. */
zebra_ns_init((const char *)vrf_default_name_configured);
+ router_id_cmd_init();
zebra_vty_init();
access_list_init();
prefix_list_init();
@@ -437,9 +439,10 @@ int main(int argc, char **argv)
zebra_pw_vty_init();
zebra_pbr_init();
zebra_opaque_init();
+ zebra_srte_init();
-/* For debug purpose. */
-/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
+ /* For debug purpose. */
+ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
/* Process the configuration file. Among other configuration
* directives we can meet those installing static routes. Such
diff --git a/zebra/rib.h b/zebra/rib.h
index ec992974fa..b9f4e56905 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -144,6 +144,10 @@ struct route_entry {
#define ROUTE_ENTRY_INSTALLED 0x10
/* Route has Failed installation into the Data Plane in some manner */
#define ROUTE_ENTRY_FAILED 0x20
+/* Route has a 'fib' set of nexthops, probably because the installed set
+ * differs from the rib/normal set of nexthops.
+ */
+#define ROUTE_ENTRY_USE_FIB_NHG 0x40
/* Sequence value incremented for each dataplane operation */
uint32_t dplane_sequence;
@@ -296,6 +300,7 @@ struct rib_table_info {
struct zebra_vrf *zvrf;
afi_t afi;
safi_t safi;
+ uint32_t table_id;
};
enum rib_tables_iter_state {
@@ -526,26 +531,28 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
/*
- * Access active nexthop-group, either RIB or FIB version
+ * Access installed/fib nexthops, which may be a subset of the
+ * rib nexthops.
*/
static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re)
{
- if (re->fib_ng.nexthop)
+ /* If the fib set is a subset of the active rib set,
+ * use the dedicated fib list.
+ */
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG))
return &(re->fib_ng);
else
return &(re->nhe->nhg);
}
/*
- * Access active nexthop-group, either RIB or FIB version
+ * Access backup nexthop-group that represents the installed backup nexthops;
+ * any installed backup will be on the fib list.
*/
static inline struct nexthop_group *rib_get_fib_backup_nhg(
struct route_entry *re)
{
- if (re->fib_backup_ng.nexthop)
- return &(re->fib_backup_ng);
- else
- return zebra_nhg_get_backup_nhg(re->nhe);
+ return &(re->fib_backup_ng);
}
extern void zebra_vty_init(void);
diff --git a/zebra/router-id.c b/zebra/router-id.c
index 710f2f6c27..7e81f29827 100644
--- a/zebra/router-id.c
+++ b/zebra/router-id.c
@@ -59,9 +59,6 @@ static struct connected *router_id_find_node(struct list *l,
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;
@@ -69,50 +66,82 @@ static int router_id_bad_address(struct connected *ifc)
return 0;
}
-void router_id_get(struct prefix *p, vrf_id_t vrf_id)
+static bool router_id_v6_is_any(struct prefix *p)
+{
+ return memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr))
+ == 0;
+}
+
+int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf)
{
struct listnode *node;
struct connected *c;
- struct zebra_vrf *zvrf = vrf_info_get(vrf_id);
-
- p->u.prefix4.s_addr = INADDR_ANY;
- p->family = AF_INET;
- p->prefixlen = 32;
-
- if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY)
- 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;
+ struct in6_addr *addr = NULL;
+
+ switch (afi) {
+ case AFI_IP:
+ p->u.prefix4.s_addr = INADDR_ANY;
+ p->family = AF_INET;
+ p->prefixlen = 32;
+ if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY)
+ 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;
+ }
+ return 0;
+ case AFI_IP6:
+ p->u.prefix6 = in6addr_any;
+ p->family = AF_INET6;
+ p->prefixlen = 128;
+ if (!router_id_v6_is_any(&zvrf->rid6_user_assigned))
+ addr = &zvrf->rid6_user_assigned.u.prefix6;
+ else if (!list_isempty(zvrf->rid6_lo_sorted_list)) {
+ node = listtail(zvrf->rid6_lo_sorted_list);
+ c = listgetdata(node);
+ addr = &c->address->u.prefix6;
+ } else if (!list_isempty(zvrf->rid6_all_sorted_list)) {
+ node = listtail(zvrf->rid6_all_sorted_list);
+ c = listgetdata(node);
+ addr = &c->address->u.prefix6;
+ }
+ if (addr)
+ memcpy(&p->u.prefix6, addr, sizeof(struct in6_addr));
+ return 0;
+ default:
+ return -1;
}
}
-static void router_id_set(struct prefix *p, vrf_id_t vrf_id)
+static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf)
{
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;
+ switch (afi) {
+ case AFI_IP:
+ zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr;
+ break;
+ case AFI_IP6:
+ zvrf->rid6_user_assigned.u.prefix6 = p->u.prefix6;
+ break;
+ default:
+ return -1;
+ }
- router_id_get(&p2, vrf_id);
+ router_id_get(afi, &p2, zvrf);
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
- zsend_router_id_update(client, &p2, vrf_id);
+ zsend_router_id_update(client, afi, &p2, zvrf->vrf->vrf_id);
+
+ return 0;
}
void router_id_add_address(struct connected *ifc)
@@ -123,27 +152,42 @@ void router_id_add_address(struct connected *ifc)
struct prefix after;
struct zserv *client;
struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id);
+ afi_t afi;
+ struct list *rid_lo;
+ struct list *rid_all;
if (router_id_bad_address(ifc))
return;
- router_id_get(&before, zvrf_id(zvrf));
+ switch (ifc->address->family) {
+ case AF_INET:
+ afi = AFI_IP;
+ rid_lo = zvrf->rid_lo_sorted_list;
+ rid_all = zvrf->rid_all_sorted_list;
+ break;
+ case AF_INET6:
+ afi = AFI_IP6;
+ rid_lo = zvrf->rid6_lo_sorted_list;
+ rid_all = zvrf->rid6_all_sorted_list;
+ break;
+ default:
+ return;
+ }
+
+ router_id_get(afi, &before, zvrf);
- if (if_is_loopback(ifc->ifp))
- l = zvrf->rid_lo_sorted_list;
- else
- l = zvrf->rid_all_sorted_list;
+ l = if_is_loopback(ifc->ifp) ? rid_lo : rid_all;
if (!router_id_find_node(l, ifc))
listnode_add_sort(l, ifc);
- router_id_get(&after, zvrf_id(zvrf));
+ router_id_get(afi, &after, zvrf);
if (prefix_same(&before, &after))
return;
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
- zsend_router_id_update(client, &after, zvrf_id(zvrf));
+ zsend_router_id_update(client, afi, &after, zvrf_id(zvrf));
}
void router_id_del_address(struct connected *ifc)
@@ -155,134 +199,340 @@ void router_id_del_address(struct connected *ifc)
struct listnode *node;
struct zserv *client;
struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id);
+ afi_t afi;
+ struct list *rid_lo;
+ struct list *rid_all;
if (router_id_bad_address(ifc))
return;
- router_id_get(&before, zvrf_id(zvrf));
+ switch (ifc->address->family) {
+ case AF_INET:
+ afi = AFI_IP;
+ rid_lo = zvrf->rid_lo_sorted_list;
+ rid_all = zvrf->rid_all_sorted_list;
+ break;
+ case AF_INET6:
+ afi = AFI_IP6;
+ rid_lo = zvrf->rid6_lo_sorted_list;
+ rid_all = zvrf->rid6_all_sorted_list;
+ break;
+ default:
+ return;
+ }
+
+ router_id_get(afi, &before, zvrf);
if (if_is_loopback(ifc->ifp))
- l = zvrf->rid_lo_sorted_list;
+ l = rid_lo;
else
- l = zvrf->rid_all_sorted_list;
+ l = rid_all;
if ((c = router_id_find_node(l, ifc)))
listnode_delete(l, c);
- router_id_get(&after, zvrf_id(zvrf));
+ router_id_get(afi, &after, zvrf);
if (prefix_same(&before, &after))
return;
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
- zsend_router_id_update(client, &after, zvrf_id(zvrf));
+ zsend_router_id_update(client, afi, &after, zvrf_id(zvrf));
+}
+
+void router_id_write(struct vty *vty, struct zebra_vrf *zvrf)
+{
+ char space[2];
+
+ memset(space, 0, sizeof(space));
+
+ if (zvrf_id(zvrf) != VRF_DEFAULT)
+ snprintf(space, sizeof(space), "%s", " ");
+
+ if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) {
+ vty_out(vty, "%sip router-id %pI4\n", space,
+ &zvrf->rid_user_assigned.u.prefix4);
+ }
+ if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) {
+ vty_out(vty, "%sipv6 router-id %pI6\n", space,
+ &zvrf->rid_user_assigned.u.prefix6);
+ }
}
-void router_id_write(struct vty *vty)
+DEFUN (ip_router_id,
+ ip_router_id_cmd,
+ "ip router-id A.B.C.D vrf NAME",
+ IP_STR
+ "Manually set the router-id\n"
+ "IP address to use for router-id\n"
+ VRF_CMD_HELP_STR)
{
- struct vrf *vrf;
+ int idx = 0;
+ struct prefix rid;
+ vrf_id_t vrf_id;
struct zebra_vrf *zvrf;
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
- if ((zvrf = vrf->info) != NULL)
- if (zvrf->rid_user_assigned.u.prefix4.s_addr
- != INADDR_ANY) {
- if (zvrf_id(zvrf) == VRF_DEFAULT)
- vty_out(vty, "router-id %s\n",
- inet_ntoa(
- zvrf->rid_user_assigned
- .u.prefix4));
- else
- vty_out(vty, "router-id %s vrf %s\n",
- inet_ntoa(
- zvrf->rid_user_assigned
- .u.prefix4),
- zvrf_name(zvrf));
- }
+ argv_find(argv, argc, "A.B.C.D", &idx);
+
+ if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ rid.prefixlen = 32;
+ rid.family = AF_INET;
+
+ argv_find(argv, argc, "NAME", &idx);
+ VRF_GET_ID(vrf_id, argv[idx]->arg, false);
+
+ zvrf = vrf_info_lookup(vrf_id);
+ router_id_set(AFI_IP, &rid, zvrf);
+
+ return CMD_SUCCESS;
}
-DEFUN (router_id,
+ALIAS (ip_router_id,
router_id_cmd,
- "router-id A.B.C.D [vrf NAME]",
+ "router-id A.B.C.D vrf NAME",
"Manually set the router-id\n"
"IP address to use for router-id\n"
+ VRF_CMD_HELP_STR);
+
+DEFUN (ipv6_router_id,
+ ipv6_router_id_cmd,
+ "ipv6 router-id X:X::X:X vrf NAME",
+ IPV6_STR
+ "Manually set the router-id\n"
+ "IPv6 address to use for router-id\n"
VRF_CMD_HELP_STR)
{
- int idx_ipv4 = 1;
- int idx_name = 3;
+ int idx = 0;
+ struct prefix rid;
+ vrf_id_t vrf_id;
+ struct zebra_vrf *zvrf;
+
+ argv_find(argv, argc, "X:X::X:X", &idx);
+
+ if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ rid.prefixlen = 128;
+ rid.family = AF_INET6;
+
+ argv_find(argv, argc, "NAME", &idx);
+ VRF_GET_ID(vrf_id, argv[idx]->arg, false);
+ zvrf = vrf_info_lookup(vrf_id);
+ router_id_set(AFI_IP6, &rid, zvrf);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (ip_router_id_in_vrf,
+ ip_router_id_in_vrf_cmd,
+ "ip router-id A.B.C.D",
+ IP_STR
+ "Manuall set the router-id\n"
+ "IP address to use for router-id\n")
+{
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ int idx = 0;
struct prefix rid;
- vrf_id_t vrf_id = VRF_DEFAULT;
- rid.u.prefix4.s_addr = inet_addr(argv[idx_ipv4]->arg);
- if (!rid.u.prefix4.s_addr)
+ argv_find(argv, argc, "A.B.C.D", &idx);
+
+ if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4))
return CMD_WARNING_CONFIG_FAILED;
rid.prefixlen = 32;
rid.family = AF_INET;
- if (argc > 2)
- VRF_GET_ID(vrf_id, argv[idx_name]->arg, false);
+ router_id_set(AFI_IP, &rid, zvrf);
- router_id_set(&rid, vrf_id);
+ return CMD_SUCCESS;
+}
+
+ALIAS (ip_router_id_in_vrf,
+ router_id_in_vrf_cmd,
+ "router-id A.B.C.D",
+ "Manually set the router-id\n"
+ "IP address to use for router-id\n");
+
+DEFUN (ipv6_router_id_in_vrf,
+ ipv6_router_id_in_vrf_cmd,
+ "ipv6 router-id X:X::X:X",
+ IP6_STR
+ "Manuall set the IPv6 router-id\n"
+ "IPV6 address to use for router-id\n")
+{
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ int idx = 0;
+ struct prefix rid;
+
+ argv_find(argv, argc, "X:X::X:X", &idx);
+
+ if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ rid.prefixlen = 128;
+ rid.family = AF_INET6;
+
+ router_id_set(AFI_IP6, &rid, zvrf);
return CMD_SUCCESS;
}
-DEFUN (no_router_id,
- no_router_id_cmd,
- "no router-id [A.B.C.D [vrf NAME]]",
+DEFUN (no_ip_router_id,
+ no_ip_router_id_cmd,
+ "no ip router-id [A.B.C.D vrf NAME]",
NO_STR
+ IP_STR
"Remove the manually configured router-id\n"
"IP address to use for router-id\n"
VRF_CMD_HELP_STR)
{
- int idx_name = 4;
+ int idx = 0;
+ struct prefix rid;
+ vrf_id_t vrf_id = VRF_DEFAULT;
+ struct zebra_vrf *zvrf;
+
+ rid.u.prefix4.s_addr = 0;
+ rid.prefixlen = 0;
+ rid.family = AF_INET;
+
+ if (argv_find(argv, argc, "NAME", &idx))
+ VRF_GET_ID(vrf_id, argv[idx]->arg, false);
+
+ zvrf = vrf_info_lookup(vrf_id);
+ router_id_set(AFI_IP, &rid, zvrf);
+
+ return CMD_SUCCESS;
+}
+ALIAS (no_ip_router_id,
+ no_router_id_cmd,
+ "no router-id [A.B.C.D vrf NAME]",
+ NO_STR
+ "Remove the manually configured router-id\n"
+ "IP address to use for router-id\n"
+ VRF_CMD_HELP_STR);
+
+DEFUN (no_ipv6_router_id,
+ no_ipv6_router_id_cmd,
+ "no ipv6 router-id [X:X::X:X vrf NAME]",
+ NO_STR
+ IPV6_STR
+ "Remove the manually configured IPv6 router-id\n"
+ "IPv6 address to use for router-id\n"
+ VRF_CMD_HELP_STR)
+{
+ int idx = 0;
struct prefix rid;
vrf_id_t vrf_id = VRF_DEFAULT;
+ struct zebra_vrf *zvrf;
+
+ memset(&rid, 0, sizeof(rid));
+ rid.family = AF_INET;
+
+ if (argv_find(argv, argc, "NAME", &idx))
+ VRF_GET_ID(vrf_id, argv[idx]->arg, false);
+
+ zvrf = vrf_info_lookup(vrf_id);
+ router_id_set(AFI_IP6, &rid, zvrf);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_router_id_in_vrf,
+ no_ip_router_id_in_vrf_cmd,
+ "no ip router-id [A.B.C.D]",
+ NO_STR
+ IP_STR
+ "Remove the manually configured router-id\n"
+ "IP address to use for router-id\n")
+{
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+
+ struct prefix rid;
rid.u.prefix4.s_addr = 0;
rid.prefixlen = 0;
rid.family = AF_INET;
- if (argc > 3)
- VRF_GET_ID(vrf_id, argv[idx_name]->arg, false);
+ router_id_set(AFI_IP, &rid, zvrf);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (no_ip_router_id_in_vrf,
+ no_router_id_in_vrf_cmd,
+ "no router-id [A.B.C.D]",
+ NO_STR
+ "Remove the manually configured router-id\n"
+ "IP address to use for router-id\n");
+
+DEFUN (no_ipv6_router_id_in_vrf,
+ no_ipv6_router_id_in_vrf_cmd,
+ "no ipv6 router-id [X:X::X:X]",
+ NO_STR
+ IP6_STR
+ "Remove the manually configured IPv6 router-id\n"
+ "IPv6 address to use for router-id\n")
+{
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
- router_id_set(&rid, vrf_id);
+ struct prefix rid;
+
+ memset(&rid, 0, sizeof(rid));
+ rid.family = AF_INET;
+
+ router_id_set(AFI_IP6, &rid, zvrf);
return CMD_SUCCESS;
}
-DEFUN (show_router_id,
- show_router_id_cmd,
- "show router-id [vrf NAME]",
+DEFUN (show_ip_router_id,
+ show_ip_router_id_cmd,
+ "show [ip|ipv6] router-id [vrf NAME]",
SHOW_STR
+ IP_STR
+ IPV6_STR
"Show the configured router-id\n"
VRF_CMD_HELP_STR)
{
- int idx_name = 3;
-
- vrf_id_t vrf_id = VRF_DEFAULT;
- struct zebra_vrf *zvrf;
+ int idx = 0;
+ vrf_id_t vrf_id = VRF_DEFAULT;
+ struct zebra_vrf *zvrf;
+ const char *vrf_name = "default";
+ char addr_name[INET6_ADDRSTRLEN];
+ int is_ipv6 = 0;
- if (argc > 2)
- VRF_GET_ID(vrf_id, argv[idx_name]->arg, false);
+ is_ipv6 = argv_find(argv, argc, "ipv6", &idx);
- zvrf = vrf_info_get(vrf_id);
+ if (argv_find(argv, argc, "NAME", &idx)) {
+ VRF_GET_ID(vrf_id, argv[idx]->arg, false);
+ vrf_name = argv[idx]->arg;
+ }
- if ((zvrf != NULL) && (zvrf->rid_user_assigned.u.prefix4.s_addr)) {
- vty_out(vty, "zebra:\n");
- if (vrf_id == VRF_DEFAULT)
- vty_out(vty, " router-id %s vrf default\n",
- inet_ntoa(zvrf->rid_user_assigned.u.prefix4));
- else
- vty_out(vty, " router-id %s vrf %s\n",
- inet_ntoa(zvrf->rid_user_assigned.u.prefix4),
- argv[idx_name]->arg);
- }
+ zvrf = vrf_info_get(vrf_id);
+
+ if (zvrf != NULL) {
+ if (is_ipv6) {
+ if (router_id_v6_is_any(&zvrf->rid6_user_assigned))
+ return CMD_SUCCESS;
+ inet_ntop(AF_INET6, &zvrf->rid6_user_assigned.u.prefix6,
+ addr_name, sizeof(addr_name));
+ } else {
+ if (zvrf->rid_user_assigned.u.prefix4.s_addr == 0)
+ return CMD_SUCCESS;
+ inet_ntop(AF_INET, &zvrf->rid_user_assigned.u.prefix4,
+ addr_name, sizeof(addr_name));
+ }
+
+ vty_out(vty, "zebra:\n");
+ vty_out(vty, " router-id %s vrf %s\n", addr_name, vrf_name);
+ }
- return CMD_SUCCESS;
+ return CMD_SUCCESS;
}
static int router_id_cmp(void *a, void *b)
@@ -294,26 +544,62 @@ static int router_id_cmp(void *a, void *b)
&ifb->address->u.prefix4.s_addr);
}
+static int router_id_v6_cmp(void *a, void *b)
+{
+ const struct connected *ifa = (const struct connected *)a;
+ const struct connected *ifb = (const struct connected *)b;
+
+ return IPV6_ADDR_CMP(&ifa->address->u.prefix6,
+ &ifb->address->u.prefix6);
+}
+
void router_id_cmd_init(void)
{
+ install_element(CONFIG_NODE, &ip_router_id_cmd);
install_element(CONFIG_NODE, &router_id_cmd);
+ install_element(CONFIG_NODE, &ipv6_router_id_cmd);
+ install_element(CONFIG_NODE, &no_ip_router_id_cmd);
install_element(CONFIG_NODE, &no_router_id_cmd);
- install_element(VIEW_NODE, &show_router_id_cmd);
+ install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &ip_router_id_in_vrf_cmd);
+ install_element(CONFIG_NODE, &router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &router_id_in_vrf_cmd);
+ install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_router_id_cmd);
+ install_element(CONFIG_NODE, &no_ip_router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &no_ip_router_id_in_vrf_cmd);
+ install_element(CONFIG_NODE, &no_router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &no_router_id_in_vrf_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_router_id_in_vrf_cmd);
+ install_element(VRF_NODE, &no_ipv6_router_id_in_vrf_cmd);
+ install_element(VIEW_NODE, &show_ip_router_id_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;
+ zvrf->rid6_all_sorted_list = &zvrf->_rid6_all_sorted_list;
+ zvrf->rid6_lo_sorted_list = &zvrf->_rid6_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));
+ memset(zvrf->rid6_all_sorted_list, 0,
+ sizeof(zvrf->_rid6_all_sorted_list));
+ memset(zvrf->rid6_lo_sorted_list, 0,
+ sizeof(zvrf->_rid6_lo_sorted_list));
+ memset(&zvrf->rid6_user_assigned, 0, sizeof(zvrf->rid6_user_assigned));
zvrf->rid_all_sorted_list->cmp = router_id_cmp;
zvrf->rid_lo_sorted_list->cmp = router_id_cmp;
+ zvrf->rid6_all_sorted_list->cmp = router_id_v6_cmp;
+ zvrf->rid6_lo_sorted_list->cmp = router_id_v6_cmp;
zvrf->rid_user_assigned.family = AF_INET;
zvrf->rid_user_assigned.prefixlen = 32;
+ zvrf->rid6_user_assigned.family = AF_INET6;
+ zvrf->rid6_user_assigned.prefixlen = 128;
}
diff --git a/zebra/router-id.h b/zebra/router-id.h
index f7d16853f1..4a35f6605b 100644
--- a/zebra/router-id.h
+++ b/zebra/router-id.h
@@ -34,12 +34,12 @@
extern "C" {
#endif
-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_add_address(struct connected *c);
+extern void router_id_del_address(struct connected *c);
+extern void router_id_init(struct zebra_vrf *zvrf);
extern void router_id_cmd_init(void);
-extern void router_id_write(struct vty *);
-extern void router_id_get(struct prefix *, vrf_id_t);
+extern void router_id_write(struct vty *vty, struct zebra_vrf *zvrf);
+extern int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf);
#ifdef __cplusplus
}
diff --git a/zebra/rt.h b/zebra/rt.h
index 4b9a3f83fe..48f1df2868 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -40,7 +40,7 @@ extern "C" {
#define RSYSTEM_ROUTE(type) \
((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
-
+#ifndef HAVE_NETLINK
/*
* Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the
* kernel, using info from a dataplane context.
@@ -63,6 +63,11 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx);
enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx);
+extern enum zebra_dplane_result
+kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
+
+#endif /* !HAVE_NETLINK */
+
extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id);
extern int kernel_interface_set_master(struct interface *master,
@@ -91,6 +96,16 @@ extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp);
extern void neigh_read_specific_ip(struct ipaddr *ip,
struct interface *vlan_if);
extern void route_read(struct zebra_ns *zns);
+extern int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip);
+extern int kernel_del_mac_nh(uint32_t nh_id);
+extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
+ struct nh_grp *nh_ids);
+extern int kernel_del_mac_nhg(uint32_t nhg_id);
+
+/*
+ * Message batching interface.
+ */
+extern void kernel_update_multi(struct dplane_ctx_q *ctx_list);
#ifdef __cplusplus
}
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 40a7eeba8e..4a6839d3b1 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -68,11 +68,27 @@
#include "zebra/zebra_mroute.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
+#include "zebra/zebra_evpn_mh.h"
#ifndef AF_MPLS
#define AF_MPLS 28
#endif
+/* Re-defining as I am unable to include <linux/if_bridge.h> which has the
+ * UAPI for MAC sync. */
+#ifndef _UAPI_LINUX_IF_BRIDGE_H
+/* FDB notification bits for NDA_NOTIFY:
+ * - BR_FDB_NFY_STATIC - notify on activity/expire even for a static entry
+ * - BR_FDB_NFY_INACTIVE - mark as inactive to avoid double notification,
+ * used with BR_FDB_NFY_STATIC (kernel controlled)
+ */
+enum {
+ BR_FDB_NFY_STATIC,
+ BR_FDB_NFY_INACTIVE,
+ BR_FDB_NFY_MAX
+};
+#endif
+
static vlanid_t filter_vlan = 0;
/* We capture whether the current kernel supports nexthop ids; by
@@ -131,6 +147,8 @@ static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags)
flags |= NTF_EXT_LEARNED;
if (dplane_flags & DPLANE_NTF_ROUTER)
flags |= NTF_ROUTER;
+ if (dplane_flags & DPLANE_NTF_USE)
+ flags |= NTF_USE;
return flags;
}
@@ -150,6 +168,8 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state)
state |= NUD_NOARP;
if (dplane_state & DPLANE_NUD_PROBE)
state |= NUD_PROBE;
+ if (dplane_state & DPLANE_NUD_INCOMPLETE)
+ state |= NUD_INCOMPLETE;
return state;
}
@@ -163,7 +183,8 @@ static inline bool is_selfroute(int proto)
|| (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
|| (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
|| (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
- || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) {
+ || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)
+ || (proto == RTPROT_SRTE)) {
return true;
}
@@ -213,6 +234,9 @@ static inline int zebra2proto(int proto)
case ZEBRA_ROUTE_OPENFABRIC:
proto = RTPROT_OPENFABRIC;
break;
+ case ZEBRA_ROUTE_SRTE:
+ proto = RTPROT_SRTE;
+ break;
case ZEBRA_ROUTE_TABLE:
case ZEBRA_ROUTE_NHG:
proto = RTPROT_ZEBRA;
@@ -278,6 +302,9 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop)
case RTPROT_OPENFABRIC:
proto = ZEBRA_ROUTE_OPENFABRIC;
break;
+ case RTPROT_SRTE:
+ proto = ZEBRA_ROUTE_SRTE;
+ break;
case RTPROT_ZEBRA:
if (is_nexthop) {
proto = ZEBRA_ROUTE_NHG;
@@ -1051,14 +1078,17 @@ static bool _netlink_route_add_gateway_info(uint8_t route_family,
bytelen + 2))
return false;
} else {
- if (gw_family == AF_INET) {
- if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
- &nexthop->gate.ipv4, bytelen))
- return false;
- } else {
- if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
- &nexthop->gate.ipv6, bytelen))
- return false;
+ if (!(nexthop->rparent
+ && IS_MAPPED_IPV6(&nexthop->rparent->gate.ipv6))) {
+ if (gw_family == AF_INET) {
+ if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
+ &nexthop->gate.ipv4, bytelen))
+ return false;
+ } else {
+ if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
+ &nexthop->gate.ipv6, bytelen))
+ return false;
+ }
}
}
@@ -2204,19 +2234,11 @@ nexthop_done:
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/**
- * kernel_nexthop_update() - Update/delete a nexthop from the kernel
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
enum dplane_op_e op;
int cmd = 0;
- int ret = 0;
- char buf[NL_PKT_BUF_SIZE];
op = dplane_ctx_get_op(ctx);
if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE)
@@ -2227,33 +2249,43 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
flog_err(EC_ZEBRA_NHG_FIB_UPDATE,
"Context received for kernel nexthop update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return -1;
}
+ return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen);
+}
+
+enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
/* Nothing to do if the kernel doesn't support nexthop objects */
if (!kernel_nexthops_supported())
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
- if (netlink_nexthop_msg_encode(cmd, ctx, buf, sizeof(buf)) > 0)
- ret = netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = 0;
+ return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder,
+ false);
+}
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf,
+ buflen, false, false);
}
-/*
- * Update or delete a prefix from the kernel,
- * using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf,
+ buflen, false, false);
+}
+
+enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
{
- int cmd, ret;
+ int cmd;
const struct prefix *p = dplane_ctx_get_dest(ctx);
- struct nexthop *nexthop;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
cmd = RTM_DELROUTE;
@@ -2263,7 +2295,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
if (p->family == AF_INET || v6_rr_semantics) {
/* Single 'replace' operation */
- cmd = RTM_NEWROUTE;
/*
* With route replace semantics in place
@@ -2273,17 +2304,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* route should cause us to withdraw from
* the kernel the old non-system route
*/
- if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) &&
- !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))
+ && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
} else {
/*
* So v6 route replace semantics are not in
@@ -2297,51 +2322,24 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* of the route delete. If that happens yeah we're
* screwed.
*/
- if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
- cmd = RTM_NEWROUTE;
+ if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
}
- } else {
- return ZEBRA_DPLANE_REQUEST_FAILURE;
- }
-
- if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false)
- > 0)
- ret = netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = -1;
-
+ cmd = RTM_NEWROUTE;
} else
- ret = 0;
- if ((cmd == RTM_NEWROUTE) && (ret == 0)) {
- /* Update installed nexthops to signal which have been
- * installed.
- */
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
+ return FRR_NETLINK_ERROR;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))
+ return FRR_NETLINK_SUCCESS;
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx,
+ cmd == RTM_NEWROUTE
+ ? netlink_newroute_msg_encoder
+ : netlink_delroute_msg_encoder,
+ false);
}
/**
@@ -2518,6 +2516,15 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* We use the ID key'd nhg table for kernel updates */
id = *((uint32_t *)RTA_DATA(tb[NHA_ID]));
+ if (zebra_evpn_mh_is_fdb_nh(id)) {
+ /* If this is a L2 NH just ignore it */
+ if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ zlog_debug("Ignore kernel update (%u) for fdb-nh 0x%x",
+ h->nlmsg_type, id);
+ }
+ return 0;
+ }
+
family = nhm->nh_family;
afi = family2afi(family);
@@ -2673,7 +2680,9 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
static ssize_t netlink_neigh_update_msg_encode(
const struct zebra_dplane_ctx *ctx, int cmd, const struct ethaddr *mac,
const struct ipaddr *ip, bool replace_obj, uint8_t family, uint8_t type,
- uint8_t flags, uint16_t state, void *data, size_t datalen)
+ uint8_t flags, uint16_t state, uint32_t nhg_id,
+ bool nfy, uint8_t nfy_flags,
+ void *data, size_t datalen)
{
uint8_t protocol = RTPROT_ZEBRA;
struct {
@@ -2686,7 +2695,7 @@ static ssize_t netlink_neigh_update_msg_encode(
if (datalen < sizeof(*req))
return 0;
- memset(req, 0, datalen);
+ memset(req, 0, sizeof(*req));
op = dplane_ctx_get_op(ctx);
@@ -2703,7 +2712,7 @@ static ssize_t netlink_neigh_update_msg_encode(
req->ndm.ndm_flags = flags;
req->ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx);
- if (!nl_attr_put(&req->n, sizeof(req), NDA_PROTOCOL, &protocol,
+ if (!nl_attr_put(&req->n, datalen, NDA_PROTOCOL, &protocol,
sizeof(protocol)))
return 0;
@@ -2712,6 +2721,16 @@ static ssize_t netlink_neigh_update_msg_encode(
return 0;
}
+ if (nhg_id) {
+ if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id))
+ return 0;
+ }
+ if (nfy) {
+ if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY,
+ &nfy_flags, sizeof(nfy_flags)))
+ return 0;
+ }
+
ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len))
return 0;
@@ -2736,22 +2755,16 @@ static ssize_t netlink_neigh_update_msg_encode(
* Add remote VTEP to the flood list for this VxLAN interface (VNI). This
* is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.
*/
-static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t
+netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd,
+ void *buf, size_t buflen)
{
struct ethaddr dst_mac = {.octet = {0}};
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- if (netlink_neigh_update_msg_encode(
- ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
- PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), nl_pkt,
- sizeof(nl_pkt))
- <= 0)
- return -1;
-
- return netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
+ PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/,
+ false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
#ifndef NDA_RTA
@@ -2774,6 +2787,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
char vid_buf[20];
char dst_buf[30];
bool sticky;
+ bool local_inactive = false;
+ bool dp_static = false;
+ uint32_t nhg_id = 0;
ndm = NLMSG_DATA(h);
@@ -2821,13 +2837,29 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
inet_ntoa(vtep_ip));
}
+ if (tb[NDA_NH_ID])
+ nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]);
+
+ if (ndm->ndm_state & NUD_STALE)
+ local_inactive = true;
+
+ if (tb[NDA_NOTIFY]) {
+ uint8_t nfy_flags;
+
+ dp_static = true;
+ nfy_flags = *(uint8_t *)RTA_DATA(tb[NDA_NOTIFY]);
+ /* local activity has not been detected on the entry */
+ if (nfy_flags & (1 << BR_FDB_NFY_INACTIVE))
+ local_inactive = true;
+ }
+
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s",
+ zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s nhg %d",
nl_msg_type_to_str(h->nlmsg_type),
ndm->ndm_ifindex, vid_present ? vid_buf : "",
ndm->ndm_state, ndm->ndm_flags,
prefix_mac2str(&mac, buf, sizeof(buf)),
- dst_present ? dst_buf : "");
+ dst_present ? dst_buf : "", nhg_id);
/* The interface should exist. */
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
@@ -2850,7 +2882,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
return 0;
}
- sticky = !!(ndm->ndm_state & NUD_NOARP);
+ sticky = !!(ndm->ndm_flags & NTF_STICKY);
if (filter_vlan && vid != filter_vlan) {
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -2878,7 +2910,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
vid);
return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid,
- sticky);
+ sticky, local_inactive, dp_static);
}
/* This is a delete notification.
@@ -2891,6 +2923,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
* Note: We will get notifications from both bridge driver and VxLAN
* driver.
*/
+ if (nhg_id)
+ return 0;
+
if (dst_present) {
u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
@@ -3078,9 +3113,8 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
/*
* Netlink-specific handler for MAC updates using dataplane context object.
*/
-ssize_t
-netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
- size_t datalen)
+ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
+ size_t datalen)
{
struct ipaddr vtep_ip;
vlanid_t vid;
@@ -3088,18 +3122,43 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
int cmd;
uint8_t flags;
uint16_t state;
+ uint32_t nhg_id;
+ uint32_t update_flags;
+ bool nfy = false;
+ uint8_t nfy_flags = 0;
cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL
? RTM_NEWNEIGH : RTM_DELNEIGH;
- flags = (NTF_SELF | NTF_MASTER);
+ flags = NTF_MASTER;
state = NUD_REACHABLE;
- if (dplane_ctx_mac_is_sticky(ctx))
- state |= NUD_NOARP;
- else
- flags |= NTF_EXT_LEARNED;
+ update_flags = dplane_ctx_mac_get_update_flags(ctx);
+ if (update_flags & DPLANE_MAC_REMOTE) {
+ flags |= NTF_SELF;
+ if (dplane_ctx_mac_is_sticky(ctx))
+ flags |= NTF_STICKY;
+ else
+ flags |= NTF_EXT_LEARNED;
+ /* if it was static-local previously we need to clear the
+ * notify flags on replace with remote
+ */
+ if (update_flags & DPLANE_MAC_WAS_STATIC)
+ nfy = true;
+ } else {
+ /* local mac */
+ if (update_flags & DPLANE_MAC_SET_STATIC) {
+ nfy_flags |= (1 << BR_FDB_NFY_STATIC);
+ state |= NUD_NOARP;
+ }
+ if (update_flags & DPLANE_MAC_SET_INACTIVE)
+ nfy_flags |= (1 << BR_FDB_NFY_INACTIVE);
+
+ nfy = true;
+ }
+
+ nhg_id = dplane_ctx_mac_get_nhg_id(ctx);
vtep_ip.ipaddr_v4 = *(dplane_ctx_mac_get_vtep_ip(ctx));
SET_IPADDR_V4(&vtep_ip);
@@ -3107,6 +3166,7 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
char ipbuf[PREFIX_STRLEN];
char buf[ETHER_ADDR_STRLEN];
char vid_buf[20];
+ const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx);
vid = dplane_ctx_mac_get_vlan(ctx);
if (vid > 0)
@@ -3114,20 +3174,30 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
else
vid_buf[0] = '\0';
- const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx);
-
- zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s dst %s",
+ zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s dst %s nhg %u%s%s%s%s%s",
nl_msg_type_to_str(cmd), nl_family_to_str(AF_BRIDGE),
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_ifindex(ctx), vid_buf,
dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "",
prefix_mac2str(mac, buf, sizeof(buf)),
- ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)));
+ ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)),
+ nhg_id,
+ (update_flags &
+ DPLANE_MAC_REMOTE) ? " rem" : "",
+ (update_flags &
+ DPLANE_MAC_WAS_STATIC) ? " clr_sync" : "",
+ (update_flags &
+ DPLANE_MAC_SET_STATIC) ? " static" : "",
+ (update_flags &
+ DPLANE_MAC_SET_INACTIVE) ? " inactive" : "",
+ (nfy &
+ DPLANE_MAC_SET_INACTIVE) ? " nfy" : "");
}
total = netlink_neigh_update_msg_encode(
ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, true,
- AF_BRIDGE, 0, flags, state, data, datalen);
+ AF_BRIDGE, 0, flags, state, nhg_id, nfy, nfy_flags,
+ data, datalen);
return total;
}
@@ -3161,6 +3231,8 @@ static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif,
#define NUD_VALID \
(NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE \
| NUD_DELAY)
+#define NUD_LOCAL_ACTIVE \
+ (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)
static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
{
@@ -3177,6 +3249,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
int mac_present = 0;
bool is_ext;
bool is_router;
+ bool local_inactive;
ndm = NLMSG_DATA(h);
@@ -3286,10 +3359,17 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
* result
* in re-adding the neighbor if it is a valid "remote" neighbor.
*/
- if (ndm->ndm_state & NUD_VALID)
+ if (ndm->ndm_state & NUD_VALID) {
+ local_inactive = !(ndm->ndm_state & NUD_LOCAL_ACTIVE);
+
+ /* XXX - populate dp-static based on the sync flags
+ * in the kernel
+ */
return zebra_vxlan_handle_kernel_neigh_update(
ifp, link_if, &ip, &mac, ndm->ndm_state,
- is_ext, is_router);
+ is_ext, is_router, local_inactive,
+ false /* dp_static */);
+ }
return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip);
}
@@ -3510,15 +3590,14 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)
/*
* Utility neighbor-update function, using info from dplane context.
*/
-static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
+ int cmd, void *buf, size_t buflen)
{
const struct ipaddr *ip;
const struct ethaddr *mac;
uint8_t flags;
uint16_t state;
uint8_t family;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
ip = dplane_ctx_neigh_get_ipaddr(ctx);
mac = dplane_ctx_neigh_get_mac(ctx);
@@ -3543,59 +3622,56 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
flags, state);
}
- if (netlink_neigh_update_msg_encode(ctx, cmd, mac, ip, true, family,
- RTN_UNICAST, flags, state, nl_pkt,
- sizeof(nl_pkt))
- <= 0)
- return -1;
-
- return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state,
+ 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
-/*
- * Update MAC, using dataplane context object.
- */
-enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t rv;
-
- rv = netlink_macfdb_update_ctx(ctx, nl_pkt, sizeof(nl_pkt));
- if (rv <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- rv = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
-
- return rv == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE;
-}
-
-enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)
-{
- int ret = -1;
+ ssize_t ret;
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_NEIGH_INSTALL:
case DPLANE_OP_NEIGH_UPDATE:
- ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH);
+ case DPLANE_OP_NEIGH_DISCOVER:
+ ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen);
break;
case DPLANE_OP_NEIGH_DELETE:
- ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen);
break;
case DPLANE_OP_VTEP_ADD:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf,
+ buflen);
break;
case DPLANE_OP_VTEP_DELETE:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf,
+ buflen);
break;
default:
- break;
+ ret = -1;
}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return ret;
+}
+
+/*
+ * Update MAC, using dataplane context object.
+ */
+
+enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx,
+ false);
+}
+
+enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder,
+ false);
}
/*
@@ -3754,4 +3830,172 @@ ssize_t netlink_mpls_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
return NLMSG_ALIGN(req->n.nlmsg_len);
}
+
+/****************************************************************************
+* This code was developed in a branch that didn't have dplane APIs for
+* MAC updates. Hence the use of the legacy style. It will be moved to
+* the new dplane style pre-merge to master. XXX
+*/
+static int netlink_fdb_nh_update(uint32_t nh_id, struct in_addr vtep_ip)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ char buf[256];
+ } req;
+ int cmd = RTM_NEWNEXTHOP;
+ struct zebra_vrf *zvrf;
+ struct zebra_ns *zns;
+
+ zvrf = zebra_vrf_get_evpn();
+ if (!zvrf)
+ return -1;
+ zns = zvrf->zns;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+ req.n.nlmsg_type = cmd;
+ req.nhm.nh_family = AF_INET;
+
+ if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id))
+ return -1;
+ if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0))
+ return -1;
+ if (!nl_attr_put(&req.n, sizeof(req), NHA_GATEWAY,
+ &vtep_ip, IPV4_MAX_BYTELEN))
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ zlog_debug("Tx %s fdb-nh 0x%x %s",
+ nl_msg_type_to_str(cmd), nh_id, inet_ntoa(vtep_ip));
+ }
+
+ return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
+ 0);
+}
+
+static int netlink_fdb_nh_del(uint32_t nh_id)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ char buf[256];
+ } req;
+ int cmd = RTM_DELNEXTHOP;
+ struct zebra_vrf *zvrf;
+ struct zebra_ns *zns;
+
+ zvrf = zebra_vrf_get_evpn();
+ if (!zvrf)
+ return -1;
+ zns = zvrf->zns;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = cmd;
+ req.nhm.nh_family = AF_UNSPEC;
+
+ if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id))
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ zlog_debug("Tx %s fdb-nh 0x%x",
+ nl_msg_type_to_str(cmd), nh_id);
+ }
+
+ return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
+ 0);
+}
+
+static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt,
+ struct nh_grp *nh_ids)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ char buf[256];
+ } req;
+ int cmd = RTM_NEWNEXTHOP;
+ struct zebra_vrf *zvrf;
+ struct zebra_ns *zns;
+ struct nexthop_grp grp[nh_cnt];
+ uint32_t i;
+
+ zvrf = zebra_vrf_get_evpn();
+ if (!zvrf)
+ return -1;
+ zns = zvrf->zns;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+ req.n.nlmsg_type = cmd;
+ req.nhm.nh_family = AF_UNSPEC;
+
+ if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nhg_id))
+ return -1;
+ if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0))
+ return -1;
+ memset(&grp, 0, sizeof(grp));
+ for (i = 0; i < nh_cnt; ++i) {
+ grp[i].id = nh_ids[i].id;
+ grp[i].weight = nh_ids[i].weight;
+ }
+ if (!nl_attr_put(&req.n, sizeof(req), NHA_GROUP,
+ grp, nh_cnt * sizeof(struct nexthop_grp)))
+ return -1;
+
+
+ if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ char vtep_str[ES_VTEP_LIST_STR_SZ];
+ char nh_buf[16];
+
+ vtep_str[0] = '\0';
+ for (i = 0; i < nh_cnt; ++i) {
+ snprintf(nh_buf, sizeof(nh_buf), "%u ",
+ grp[i].id);
+ strlcat(vtep_str, nh_buf, sizeof(vtep_str));
+ }
+
+ zlog_debug("Tx %s fdb-nhg 0x%x %s",
+ nl_msg_type_to_str(cmd), nhg_id, vtep_str);
+ }
+
+ return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
+ 0);
+}
+
+static int netlink_fdb_nhg_del(uint32_t nhg_id)
+{
+ return netlink_fdb_nh_del(nhg_id);
+}
+
+int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip)
+{
+ return netlink_fdb_nh_update(nh_id, vtep_ip);
+}
+
+int kernel_del_mac_nh(uint32_t nh_id)
+{
+ return netlink_fdb_nh_del(nh_id);
+}
+
+int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
+ struct nh_grp *nh_ids)
+{
+ return netlink_fdb_nhg_update(nhg_id, nh_cnt, nh_ids);
+}
+
+int kernel_del_mac_nhg(uint32_t nhg_id)
+{
+ return netlink_fdb_nhg_del(nhg_id);
+}
+
#endif /* HAVE_NETLINK */
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 429ff0bf85..e1bb844785 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -60,6 +60,7 @@ extern "C" {
#define RTPROT_PBR 195
#define RTPROT_ZSTATIC 196
#define RTPROT_OPENFABRIC 197
+#define RTPROT_SRTE 198
void rt_netlink_init(void);
@@ -73,7 +74,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd,
uint8_t *data, size_t datalen,
bool fpm, bool force_nhg);
extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx,
- uint8_t *data, size_t datalen);
+ void *data, size_t datalen);
extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int netlink_route_read(struct zebra_ns *zns);
@@ -100,6 +101,23 @@ extern int netlink_neigh_read_specific_ip(struct ipaddr *ip,
struct interface *vlan_if);
extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id);
+struct nl_batch;
+extern enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 64fd7fa491..a2e15cbd2b 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -50,8 +50,7 @@ static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label,
{
if (nh_label->num_labels > 1) {
flog_warn(EC_ZEBRA_MAX_LABELS_PUSH,
- "%s: can't push %u labels at "
- "once (maximum is 1)",
+ "%s: can't push %u labels at once (maximum is 1)",
__func__, nh_label->num_labels);
return -1;
}
@@ -359,20 +358,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
}
} /* Elevated privs */
- if (RSYSTEM_ROUTE(type)
- && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) {
- struct nexthop *nexthop;
-
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
-
return res;
}
@@ -418,4 +403,25 @@ uint32_t kernel_get_speed(struct interface *ifp, int *error)
return ifp->speed;
}
+int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip)
+{
+ return 0;
+}
+
+int kernel_del_mac_nh(uint32_t nh_id)
+{
+ return 0;
+}
+
+int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
+ struct nh_grp *nh_ids)
+{
+ return 0;
+}
+
+int kernel_del_mac_nhg(uint32_t nhg_id)
+{
+ return 0;
+}
+
#endif /* !HAVE_NETLINK */
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index fa0a682d07..13b7c150a3 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -2282,13 +2282,11 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp)
interval = rtadv->MaxRtrAdvInterval;
if (interval % 1000)
vty_out(vty,
- " ND router advertisements are sent every "
- "%d milliseconds\n",
+ " ND router advertisements are sent every %d milliseconds\n",
interval);
else
vty_out(vty,
- " ND router advertisements are sent every "
- "%d seconds\n",
+ " ND router advertisements are sent every %d seconds\n",
interval / 1000);
if (!rtadv->UseFastRexmit)
vty_out(vty,
@@ -2302,8 +2300,7 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp)
vty_out(vty,
" ND router advertisements lifetime tracks ra-interval\n");
vty_out(vty,
- " ND router advertisement default router preference is "
- "%s\n",
+ " ND router advertisement default router preference is %s\n",
rtadv_pref_strs[rtadv->DefaultPreference]);
if (rtadv->AdvManagedFlag)
vty_out(vty,
diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c
index 28529cc138..0a1fb82d95 100644
--- a/zebra/rtread_getmsg.c
+++ b/zebra/rtread_getmsg.c
@@ -230,8 +230,7 @@ void route_read(struct zebra_ns *zns)
if (msgdata.len % sizeof(mib2_ipRouteEntry_t)
!= 0) {
zlog_debug(
- "getmsg(data) returned "
- "msgdata.len = %d (%% sizeof(mib2_ipRouteEntry_t) != 0)",
+ "getmsg(data) returned msgdata.len = %d (%% sizeof(mib2_ipRouteEntry_t) != 0)",
msgdata.len);
goto exit;
}
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index b7be398506..3a3baab4ca 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -58,10 +58,12 @@
* Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
* or the number of bytes written to buf.
*/
-static ssize_t netlink_rule_msg_encode(
- int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
- uint32_t priority, uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen)
+static ssize_t
+netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
+ uint32_t filter_bm, uint32_t priority, uint32_t table,
+ const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark,
+ uint8_t dsfield, void *buf, size_t buflen)
{
uint8_t protocol = RTPROT_ZEBRA;
int family;
@@ -76,6 +78,8 @@ static ssize_t netlink_rule_msg_encode(
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
+ if (buflen < sizeof(*req))
+ return 0;
memset(req, 0, sizeof(*req));
family = PREFIX_FAMILY(src_ip);
bytelen = (family == AF_INET ? 4 : 16);
@@ -122,6 +126,10 @@ static ssize_t netlink_rule_msg_encode(
return 0;
}
+ /* dsfield, if specified */
+ if (filter_bm & PBR_FILTER_DSFIELD)
+ req->frh.tos = dsfield;
+
/* Route table to use to forward, if filter criteria matches. */
if (table < 256)
req->frh.table = table;
@@ -142,53 +150,55 @@ static ssize_t netlink_rule_msg_encode(
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/* Install or uninstall specified rule for a specific interface.
- * Form netlink message and ship it.
- */
-static int
-netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx,
- uint32_t filter_bm, uint32_t priority,
- uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark)
+static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- char buf[NL_PKT_BUF_SIZE];
+ int cmd = RTM_NEWRULE;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE)
+ cmd = RTM_DELRULE;
+
+ return netlink_rule_msg_encode(
+ cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
+ dplane_ctx_rule_get_priority(ctx),
+ dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
+ dplane_ctx_rule_get_dst_ip(ctx),
+ dplane_ctx_rule_get_fwmark(ctx),
+ dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
+}
- netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
- dst_ip, fwmark, buf, sizeof(buf));
- return netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
+static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_rule_msg_encode(
+ RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx),
+ dplane_ctx_rule_get_old_priority(ctx),
+ dplane_ctx_rule_get_old_table(ctx),
+ dplane_ctx_rule_get_old_src_ip(ctx),
+ dplane_ctx_rule_get_old_dst_ip(ctx),
+ dplane_ctx_rule_get_old_fwmark(ctx),
+ dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
}
+
/* Public functions */
-/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
{
enum dplane_op_e op;
- int cmd;
- int ret;
+ enum netlink_msg_status ret;
op = dplane_ctx_get_op(ctx);
- if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
- cmd = RTM_NEWRULE;
- else if (op == DPLANE_OP_RULE_DELETE)
- cmd = RTM_DELRULE;
- else {
+ if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE
+ || op == DPLANE_OP_RULE_DELETE)) {
flog_err(
EC_ZEBRA_PBR_RULE_UPDATE,
"Context received for kernel rule update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return FRR_NETLINK_ERROR;
}
- ret = netlink_rule_update_internal(
- cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
- dplane_ctx_rule_get_priority(ctx),
- dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
- dplane_ctx_rule_get_dst_ip(ctx),
- dplane_ctx_rule_get_fwmark(ctx));
+ ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false);
/**
* Delete the old one.
@@ -196,18 +206,10 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
* Don't care about this result right?
*/
if (op == DPLANE_OP_RULE_UPDATE)
- netlink_rule_update_internal(
- RTM_DELRULE, ctx,
- dplane_ctx_rule_get_old_filter_bm(ctx),
- dplane_ctx_rule_get_old_priority(ctx),
- dplane_ctx_rule_get_old_table(ctx),
- dplane_ctx_rule_get_old_src_ip(ctx),
- dplane_ctx_rule_get_old_dst_ip(ctx),
- dplane_ctx_rule_get_old_fwmark(ctx));
-
-
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+ netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder,
+ true);
+
+ return ret;
}
/*
@@ -247,7 +249,16 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
}
frh = NLMSG_DATA(h);
+
if (frh->family != AF_INET && frh->family != AF_INET6) {
+ if (frh->family == RTNL_FAMILY_IPMR
+ || frh->family == RTNL_FAMILY_IP6MR) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Received rule netlink that we are ignoring for family %u, rule change: %u",
+ frh->family, h->nlmsg_type);
+ return 0;
+ }
flog_warn(
EC_ZEBRA_NETLINK_INVALID_AF,
"Invalid address family: %u received from kernel rule change: %u",
diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h
index 8c4741dc06..cf4d978e78 100644
--- a/zebra/rule_netlink.h
+++ b/zebra/rule_netlink.h
@@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
*/
extern int netlink_rules_read(struct zebra_ns *zns);
+extern enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 49e60820bc..a6ef1537c0 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -12,6 +12,7 @@ vtysh_scan += \
zebra/rtadv.c \
zebra/zebra_gr.c \
zebra/zebra_mlag_vty.c \
+ zebra/zebra_evpn_mh.c \
zebra/zebra_mpls_vty.c \
zebra/zebra_ptm.c \
zebra/zebra_pw.c \
@@ -79,6 +80,9 @@ zebra_zebra_SOURCES = \
zebra/zebra_errors.c \
zebra/zebra_gr.c \
zebra/zebra_l2.c \
+ zebra/zebra_evpn.c \
+ zebra/zebra_evpn_mac.c \
+ zebra/zebra_evpn_neigh.c \
zebra/zebra_mlag.c \
zebra/zebra_mlag_vty.c \
zebra/zebra_memory.c \
@@ -105,9 +109,11 @@ zebra_zebra_SOURCES = \
zebra/zebra_router.c \
zebra/zebra_rnh.c \
zebra/zebra_routemap.c \
+ zebra/zebra_srte.c \
zebra/zebra_vrf.c \
zebra/zebra_vty.c \
zebra/zebra_vxlan.c \
+ zebra/zebra_evpn_mh.c \
zebra/zserv.c \
# end
@@ -115,6 +121,7 @@ clippy_scan += \
zebra/debug.c \
zebra/interface.c \
zebra/rtadv.c \
+ zebra/zebra_evpn_mh.c \
zebra/zebra_mlag_vty.c \
zebra/zebra_routemap.c \
zebra/zebra_vty.c \
@@ -143,6 +150,10 @@ noinst_HEADERS += \
zebra/zapi_msg.h \
zebra/zebra_dplane.h \
zebra/zebra_errors.h \
+ zebra/zebra_evpn.h \
+ zebra/zebra_evpn_mac.h \
+ zebra/zebra_evpn_neigh.h \
+ zebra/zebra_evpn_vxlan.h \
zebra/zebra_fpm_private.h \
zebra/zebra_l2.h \
zebra/zebra_memory.h \
@@ -164,9 +175,11 @@ noinst_HEADERS += \
zebra/zebra_rnh.h \
zebra/zebra_routemap.h \
zebra/zebra_router.h \
+ zebra/zebra_srte.h \
zebra/zebra_vrf.h \
zebra/zebra_vxlan.h \
zebra/zebra_vxlan_private.h \
+ zebra/zebra_evpn_mh.h \
zebra/zserv.h \
# end
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index dc7c595d26..285f28ccec 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -51,6 +51,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_mroute.h"
#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn_mh.h"
#include "zebra/rt.h"
#include "zebra/zebra_pbr.h"
#include "zebra/table_manager.h"
@@ -59,6 +60,7 @@
#include "zebra/zebra_mlag.h"
#include "zebra/connected.h"
#include "zebra/zebra_opaque.h"
+#include "zebra/zebra_srte.h"
/* Encoding helpers -------------------------------------------------------- */
@@ -729,9 +731,10 @@ static int route_notify_internal(const struct prefix *p, int type,
char buff[PREFIX_STRLEN];
zlog_debug(
- "Not Notifying Owner: %u about prefix %s(%u) %d vrf: %u",
- type, prefix2str(p, buff, sizeof(buff)),
- table_id, note, vrf_id);
+ "Not Notifying Owner: %s about prefix %s(%u) %d vrf: %u",
+ zebra_route_string(type),
+ prefix2str(p, buff, sizeof(buff)), table_id,
+ note, vrf_id);
}
return 0;
}
@@ -739,9 +742,10 @@ static int route_notify_internal(const struct prefix *p, int type,
if (IS_ZEBRA_DEBUG_PACKET) {
char buff[PREFIX_STRLEN];
- zlog_debug("Notifying Owner: %u about prefix %s(%u) %d vrf: %u",
- type, prefix2str(p, buff, sizeof(buff)),
- table_id, note, vrf_id);
+ zlog_debug("Notifying Owner: %s about prefix %s(%u) %d vrf: %u",
+ zebra_route_string(type),
+ prefix2str(p, buff, sizeof(buff)), table_id, note,
+ vrf_id);
}
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
@@ -904,17 +908,18 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
zserv_send_message(client, s);
}
-/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */
-int zsend_router_id_update(struct zserv *client, struct prefix *p,
+/* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */
+int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p,
vrf_id_t vrf_id)
{
int blen;
+ struct stream *s;
/* Check this client need interface information. */
- if (!vrf_bitmap_check(client->ridinfo, vrf_id))
+ if (!vrf_bitmap_check(client->ridinfo[afi], vrf_id))
return 0;
- struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
/* Message type. */
zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id);
@@ -1073,7 +1078,8 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS)
s = msg;
- client->nh_reg_time = monotime(NULL);
+ if (!client->nh_reg_time)
+ client->nh_reg_time = monotime(NULL);
while (l < hdr->length) {
STREAM_GETC(s, flags);
@@ -1416,6 +1422,7 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re,
struct nexthop *nexthop = NULL;
struct ipaddr vtep_ip;
struct interface *ifp;
+ int i;
char nhbuf[INET6_ADDRSTRLEN] = "";
switch (api_nh->type) {
@@ -1521,17 +1528,36 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re,
nexthop->weight = api_nh->weight;
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- if (api_nh->backup_idx < api->backup_nexthop_num) {
- /* Capture backup info */
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = api_nh->backup_idx;
- } else {
- /* Warn about invalid backup index */
+ /* Validate count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s: invalid backup nh idx %d",
- __func__, api_nh->backup_idx);
+ zlog_debug("%s: invalid backup nh count %d",
+ __func__, api_nh->backup_num);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
+
+ /* Copy backup info */
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nexthop->backup_num = api_nh->backup_num;
+
+ for (i = 0; i < api_nh->backup_num; i++) {
+ /* Validate backup index */
+ if (api_nh->backup_idx[i] < api->backup_nexthop_num) {
+ nexthop->backup_idx[i] = api_nh->backup_idx[i];
+ } else {
+ if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: invalid backup nh idx %d",
+ __func__,
+ api_nh->backup_idx[i]);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
}
}
+
done:
return nexthop;
}
@@ -1627,6 +1653,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
return;
}
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) {
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE);
+ nexthop->srte_color = api_nh->srte_color;
+ }
+
/* MPLS labels for BGP-LU or Segment Routing */
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& api_nh->type != NEXTHOP_TYPE_IFINDEX
@@ -1703,7 +1734,12 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
__func__, nhbuf);
}
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = 0;
+ nexthop->backup_num = 0;
+ }
+
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) {
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE);
+ nexthop->srte_color = api_nh->srte_color;
}
/* MPLS labels for BGP-LU or Segment Routing */
@@ -1874,20 +1910,48 @@ stream_failure:
/* Register zebra server router-id information. Send current router-id */
static void zread_router_id_add(ZAPI_HANDLER_ARGS)
{
+ afi_t afi;
+
struct prefix p;
+ STREAM_GETW(msg, afi);
+
+ if (afi <= AFI_UNSPEC || afi >= AFI_MAX) {
+ zlog_warn(
+ "Invalid AFI %u while registering for router ID notifications",
+ afi);
+ goto stream_failure;
+ }
+
/* Router-id information is needed. */
- vrf_bitmap_set(client->ridinfo, zvrf_id(zvrf));
+ vrf_bitmap_set(client->ridinfo[afi], zvrf_id(zvrf));
+
+ router_id_get(afi, &p, zvrf);
- router_id_get(&p, zvrf_id(zvrf));
+ zsend_router_id_update(client, afi, &p, zvrf_id(zvrf));
- zsend_router_id_update(client, &p, zvrf_id(zvrf));
+stream_failure:
+ return;
}
/* Unregister zebra server router-id information. */
static void zread_router_id_delete(ZAPI_HANDLER_ARGS)
{
- vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf));
+ afi_t afi;
+
+ STREAM_GETW(msg, afi);
+
+ if (afi <= AFI_UNSPEC || afi >= AFI_MAX) {
+ zlog_warn(
+ "Invalid AFI %u while unregistering from router ID notifications",
+ afi);
+ goto stream_failure;
+ }
+
+ vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf));
+
+stream_failure:
+ return;
}
static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf)
@@ -1976,8 +2040,58 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS)
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
vrf_bitmap_unset(client->redist[afi][i], zvrf_id(zvrf));
vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf));
+ vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf));
+ }
+}
+
+/*
+ * Validate incoming zapi mpls lsp / labels message
+ */
+static int zapi_labels_validate(const struct zapi_labels *zl)
+{
+ int ret = -1;
+ int i, j, idx;
+ uint32_t bits[8];
+ uint32_t ival;
+ const struct zapi_nexthop *znh;
+
+ /* Validate backup info: no duplicates for a single primary */
+ if (zl->backup_nexthop_num == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ for (j = 0; j < zl->nexthop_num; j++) {
+ znh = &zl->nexthops[j];
+
+ memset(bits, 0, sizeof(bits));
+
+ for (i = 0; i < znh->backup_num; i++) {
+ idx = znh->backup_idx[i] / 32;
+
+ ival = 1 << znh->backup_idx[i] % 32;
+
+ /* Check whether value is already used */
+ if (ival & bits[idx]) {
+ /* Fail */
+
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: invalid zapi mpls message: duplicate backup nexthop index %d",
+ __func__,
+ znh->backup_idx[i]);
+ goto done;
+ }
+
+ /* Mark index value */
+ bits[idx] |= ival;
+ }
}
- vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf));
+
+ ret = 0;
+
+done:
+
+ return ret;
}
/*
@@ -2006,6 +2120,10 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS)
if (!mpls_enabled)
return;
+ /* Validate; will debug on failure */
+ if (zapi_labels_validate(&zl) < 0)
+ return;
+
ret = mpls_zapi_labels_process(true, zvrf, &zl);
if (ret < 0) {
if (IS_ZEBRA_DEBUG_RECV)
@@ -2087,6 +2205,10 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS)
if (!mpls_enabled)
return;
+ /* Validate; will debug on failure */
+ if (zapi_labels_validate(&zl) < 0)
+ return;
+
/* This removes everything, then re-adds from the client's
* zapi message. Since the LSP will be processed later, on this
* this same pthread, all of the changes will 'appear' at once.
@@ -2099,6 +2221,107 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS)
mpls_zapi_labels_process(true, zvrf, &zl);
}
+static void zread_sr_policy_set(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ struct zapi_sr_policy zp;
+ struct zapi_srte_tunnel *zt;
+ struct zebra_sr_policy *policy;
+
+ /* Get input stream. */
+ s = msg;
+ if (zapi_sr_policy_decode(s, &zp) < 0) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to decode zapi_sr_policy sent",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+ zt = &zp.segment_list;
+ if (zt->label_num < 1) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug(
+ "%s: SR-TE tunnel must contain at least one label",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ if (!mpls_enabled)
+ return;
+
+ policy = zebra_sr_policy_find(zp.color, &zp.endpoint);
+ if (!policy)
+ policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name);
+ /* TODO: per-VRF list of SR-TE policies. */
+ policy->zvrf = zvrf;
+
+ zebra_sr_policy_validate(policy, &zp.segment_list);
+}
+
+static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ struct zapi_sr_policy zp;
+ struct zebra_sr_policy *policy;
+
+ /* Get input stream. */
+ s = msg;
+ if (zapi_sr_policy_decode(s, &zp) < 0) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to decode zapi_sr_policy sent",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ if (!mpls_enabled)
+ return;
+
+ policy = zebra_sr_policy_find(zp.color, &zp.endpoint);
+ if (!policy) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to find SR-TE policy",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ zebra_sr_policy_del(policy);
+}
+
+int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint,
+ char *name, int status)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0);
+ if (!client) {
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug(
+ "Not notifying pathd about policy %s"
+ " status change to %d",
+ name, status);
+ return 0;
+ }
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug(
+ "Notifying pathd about policy %s status change"
+ " to %d",
+ name, status);
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT);
+ stream_putl(s, color);
+ stream_put_ipaddr(s, endpoint);
+ stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH);
+ stream_putl(s, status);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zserv_send_message(client, s);
+}
+
/* Send response to a table manager connect request to client */
static void zread_table_manager_connect(struct zserv *client,
struct stream *msg, vrf_id_t vrf_id)
@@ -2526,6 +2749,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s,
prefix_blen(&zpr.rule.filter.dst_ip));
STREAM_GETW(s, zpr.rule.filter.dst_port);
+ STREAM_GETC(s, zpr.rule.filter.dsfield);
STREAM_GETL(s, zpr.rule.filter.fwmark);
STREAM_GETL(s, zpr.rule.action.table);
STREAM_GETL(s, zpr.rule.ifindex);
@@ -2556,14 +2780,16 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
if (zpr.rule.filter.dst_port)
zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT;
+ if (zpr.rule.filter.dsfield)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD;
+
if (zpr.rule.filter.fwmark)
zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
if (!(zpr.rule.filter.src_ip.family == AF_INET
|| zpr.rule.filter.src_ip.family == AF_INET6)) {
zlog_warn(
- "Unsupported PBR source IP family: %s (%" PRIu8
- ")",
+ "Unsupported PBR source IP family: %s (%hhu)",
family2str(zpr.rule.filter.src_ip.family),
zpr.rule.filter.src_ip.family);
return;
@@ -2571,8 +2797,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
if (!(zpr.rule.filter.dst_ip.family == AF_INET
|| zpr.rule.filter.dst_ip.family == AF_INET6)) {
zlog_warn(
- "Unsupported PBR destination IP family: %s (%" PRIu8
- ")",
+ "Unsupported PBR destination IP family: %s (%hhu)",
family2str(zpr.rule.filter.dst_ip.family),
zpr.rule.filter.dst_ip.family);
return;
@@ -2667,16 +2892,14 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS)
if (!(zpi.dst.family == AF_INET
|| zpi.dst.family == AF_INET6)) {
zlog_warn(
- "Unsupported PBR destination IP family: %s (%" PRIu8
- ")",
+ "Unsupported PBR destination IP family: %s (%hhu)",
family2str(zpi.dst.family), zpi.dst.family);
goto stream_failure;
}
if (!(zpi.src.family == AF_INET
|| zpi.src.family == AF_INET6)) {
zlog_warn(
- "Unsupported PBR source IP family: %s (%" PRIu8
- ")",
+ "Unsupported PBR source IP family: %s (%hhu)",
family2str(zpi.src.family), zpi.src.family);
goto stream_failure;
}
@@ -2739,6 +2962,41 @@ stream_failure:
return;
}
+static inline void zread_neigh_discover(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ ifindex_t ifindex;
+ struct interface *ifp;
+ struct prefix p;
+ struct ipaddr ip;
+
+ s = msg;
+
+ STREAM_GETL(s, ifindex);
+
+ ifp = if_lookup_by_index_per_ns(zvrf->zns, ifindex);
+ if (!ifp) {
+ zlog_debug("Failed to lookup ifindex: %u", ifindex);
+ return;
+ }
+
+ STREAM_GETC(s, p.family);
+ STREAM_GETC(s, p.prefixlen);
+ STREAM_GET(&p.u.prefix, s, prefix_blen(&p));
+
+ if (p.family == AF_INET)
+ SET_IPADDR_V4(&ip);
+ else
+ SET_IPADDR_V6(&ip);
+
+ memcpy(&ip.ip.addr, &p.u.prefix, prefix_blen(&p));
+
+ dplane_neigh_discover(ifp, &ip);
+
+stream_failure:
+ return;
+}
+
static void zsend_error_msg(struct zserv *client, enum zebra_error_types error,
struct zmsghdr *bad_hdr)
{
@@ -2798,6 +3056,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register,
[ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable,
[ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable,
+ [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set,
+ [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete,
[ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add,
[ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete,
[ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace,
@@ -2812,6 +3072,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ADVERTISE_SVI_MACIP] = zebra_vxlan_advertise_svi_macip,
[ZEBRA_ADVERTISE_SUBNET] = zebra_vxlan_advertise_subnet,
[ZEBRA_ADVERTISE_ALL_VNI] = zebra_vxlan_advertise_all_vni,
+ [ZEBRA_REMOTE_ES_VTEP_ADD] = zebra_evpn_proc_remote_es,
+ [ZEBRA_REMOTE_ES_VTEP_DEL] = zebra_evpn_proc_remote_es,
[ZEBRA_REMOTE_VTEP_ADD] = zebra_vxlan_remote_vtep_add,
[ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del,
[ZEBRA_REMOTE_MACIP_ADD] = zebra_vxlan_remote_macip_add,
@@ -2838,8 +3100,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register,
[ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister,
[ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg,
- [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities
-};
+ [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities,
+ [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover};
#if defined(HANDLE_ZAPI_FUZZING)
extern struct zebra_privs_t zserv_privs;
@@ -2890,7 +3152,8 @@ void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo)
zapi_parse_header(msg, &hdr);
- if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+ if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV
+ && IS_ZEBRA_DEBUG_DETAIL)
zserv_log_message(NULL, msg, &hdr);
#if defined(HANDLE_ZAPI_FUZZING)
diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h
index eb50e3c410..e7c755c2bf 100644
--- a/zebra/zapi_msg.h
+++ b/zebra/zapi_msg.h
@@ -68,8 +68,8 @@ extern int zsend_redistribute_route(int cmd, struct zserv *zclient,
const struct prefix *src_p,
const struct route_entry *re);
-extern int zsend_router_id_update(struct zserv *zclient, struct prefix *p,
- vrf_id_t vrf_id);
+extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,
+ struct prefix *p, vrf_id_t vrf_id);
extern int zsend_interface_vrf_update(struct zserv *zclient,
struct interface *ifp, vrf_id_t vrf_id);
extern int zsend_interface_link_params(struct zserv *zclient,
@@ -100,7 +100,9 @@ extern int zsend_assign_label_chunk_response(struct zserv *client,
extern int zsend_label_manager_connect_response(struct zserv *client,
vrf_id_t vrf_id,
unsigned short result);
-
+extern int zsend_sr_policy_notify_status(uint32_t color,
+ struct ipaddr *endpoint, char *name,
+ int status);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index e34b6f23ff..6e8d35aa77 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -180,16 +180,19 @@ struct dplane_mac_info {
struct ethaddr mac;
struct in_addr vtep_ip;
bool is_sticky;
+ uint32_t nhg_id;
+ uint32_t update_flags;
};
/*
- * EVPN neighbor info for the dataplane
+ * Neighbor info for the dataplane
*/
struct dplane_neigh_info {
struct ipaddr ip_addr;
struct ethaddr mac;
uint32_t flags;
uint16_t state;
+ uint32_t update_flags;
};
/*
@@ -204,6 +207,7 @@ struct dplane_ctx_rule {
/* Filter criteria */
uint32_t filter_bm;
uint32_t fwmark;
+ uint8_t dsfield;
struct prefix src_ip;
struct prefix dst_ip;
};
@@ -440,13 +444,14 @@ static enum zebra_dplane_result mac_update_common(
enum dplane_op_e op, const struct interface *ifp,
const struct interface *br_ifp,
vlanid_t vid, const struct ethaddr *mac,
- struct in_addr vtep_ip, bool sticky);
+ struct in_addr vtep_ip, bool sticky, uint32_t nhg_id,
+ uint32_t update_flags);
static enum zebra_dplane_result neigh_update_internal(
enum dplane_op_e op,
const struct interface *ifp,
const struct ethaddr *mac,
const struct ipaddr *ip,
- uint32_t flags, uint16_t state);
+ uint32_t flags, uint16_t state, uint32_t update_flags);
/*
* Public APIs
@@ -602,6 +607,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_RULE_ADD:
case DPLANE_OP_RULE_DELETE:
case DPLANE_OP_RULE_UPDATE:
+ case DPLANE_OP_NEIGH_DISCOVER:
case DPLANE_OP_NONE:
break;
}
@@ -834,6 +840,10 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_RULE_UPDATE:
ret = "RULE_UPDATE";
break;
+
+ case DPLANE_OP_NEIGH_DISCOVER:
+ ret = "NEIGH_DISCOVER";
+ break;
}
return ret;
@@ -853,8 +863,6 @@ const char *dplane_res2str(enum zebra_dplane_result res)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
ret = "SUCCESS";
break;
- case ZEBRA_DPLANE_REQUEST_PENDING:
- ret = "PENDING";
}
return ret;
@@ -1551,6 +1559,18 @@ bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx)
return ctx->u.macinfo.is_sticky;
}
+uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.macinfo.nhg_id;
+}
+
+uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.macinfo.update_flags;
+}
+
const struct ethaddr *dplane_ctx_mac_get_addr(
const struct zebra_dplane_ctx *ctx)
{
@@ -1598,6 +1618,12 @@ uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx)
return ctx->u.neigh.state;
}
+uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.neigh.update_flags;
+}
+
/* Accessors for PBR rule information */
int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx)
{
@@ -1676,6 +1702,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
return ctx->u.rule.old.fwmark;
}
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.dsfield;
+}
+
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.dsfield;
+}
+
const struct prefix *
dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx)
{
@@ -1942,6 +1982,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
* it probably won't require two messages
*/
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE);
ret = AOK;
@@ -1964,6 +2005,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* Capture namespace info */
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
(op == DPLANE_OP_LSP_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE);
memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
@@ -2001,10 +2043,19 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
break;
}
- /* Need to copy flags too */
+ /* Need to copy flags and backup info too */
new_nhlfe->flags = nhlfe->flags;
new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
+ if (CHECK_FLAG(new_nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP)) {
+ new_nhlfe->nexthop->backup_num =
+ nhlfe->nexthop->backup_num;
+ memcpy(new_nhlfe->nexthop->backup_idx,
+ nhlfe->nexthop->backup_idx,
+ new_nhlfe->nexthop->backup_num);
+ }
+
if (nhlfe == lsp->best_nhlfe)
ctx->u.lsp.best_nhlfe = new_nhlfe;
}
@@ -2104,8 +2155,15 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
if (re) {
nhg = rib_get_fib_nhg(re);
- copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- nhg->nexthop, NULL);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(ctx->u.pw.nhg.nexthop),
+ nhg->nexthop, NULL);
+
+ /* Include any installed backup nexthops */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(ctx->u.pw.nhg.nexthop),
+ nhg->nexthop, NULL);
}
route_unlock_node(rn);
}
@@ -2129,6 +2187,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
dplane_rule->filter_bm = rule->rule.filter.filter_bm;
dplane_rule->fwmark = rule->rule.filter.fwmark;
+ dplane_rule->dsfield = rule->rule.filter.dsfield;
prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
}
@@ -2168,6 +2227,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
op == DPLANE_OP_RULE_UPDATE);
+ ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE);
ctx->zd_vrf_id = new_rule->vrf_id;
memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));
@@ -2458,9 +2518,11 @@ dplane_route_notif_update(struct route_node *rn,
enum dplane_op_e op,
struct zebra_dplane_ctx *ctx)
{
- enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
struct zebra_dplane_ctx *new_ctx = NULL;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
if (rn == NULL || re == NULL)
goto done;
@@ -2482,8 +2544,17 @@ dplane_route_notif_update(struct route_node *rn,
nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop);
new_ctx->u.rinfo.zd_ng.nexthop = NULL;
- copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
- (rib_get_fib_nhg(re))->nexthop, NULL);
+ nhg = rib_get_fib_nhg(re);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ nhg->nexthop, NULL);
+
+ /* Check for installed backup nexthops also */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop) {
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ nhg->nexthop, NULL);
+ }
for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -2494,12 +2565,15 @@ dplane_route_notif_update(struct route_node *rn,
dplane_ctx_set_notif_provider(new_ctx,
dplane_ctx_get_notif_provider(ctx));
- dplane_update_enqueue(new_ctx);
-
- ret = ZEBRA_DPLANE_REQUEST_QUEUED;
+ ret = dplane_update_enqueue(new_ctx);
done:
- return ret;
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else if (new_ctx)
+ dplane_ctx_free(&new_ctx);
+
+ return result;
}
/*
@@ -2583,6 +2657,8 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp,
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret = EINVAL;
struct zebra_dplane_ctx *ctx = NULL;
+ struct nhlfe_list_head *head;
+ zebra_nhlfe_t *nhlfe, *new_nhlfe;
/* Obtain context block */
ctx = dplane_ctx_alloc();
@@ -2591,10 +2667,27 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp,
goto done;
}
+ /* Copy info from zebra LSP */
ret = dplane_ctx_lsp_init(ctx, op, lsp);
if (ret != AOK)
goto done;
+ /* Add any installed backup nhlfes */
+ head = &(ctx->u.lsp.backup_nhlfe_list);
+ frr_each(nhlfe_list, head, nhlfe) {
+
+ if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) &&
+ CHECK_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp),
+ nhlfe->type,
+ nhlfe->nexthop);
+
+ /* Need to copy flags too */
+ new_nhlfe->flags = nhlfe->flags;
+ new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
+ }
+ }
+
/* Capture info about the source of the notification */
dplane_ctx_set_notif_provider(
ctx,
@@ -2830,35 +2923,75 @@ static enum zebra_dplane_result intf_addr_update_internal(
/*
* Enqueue vxlan/evpn mac add (or update).
*/
-enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp,
const struct interface *bridge_ifp,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip,
- bool sticky)
+ bool sticky,
+ uint32_t nhg_id,
+ bool was_static)
{
enum zebra_dplane_result result;
+ uint32_t update_flags = 0;
+
+ update_flags |= DPLANE_MAC_REMOTE;
+ if (was_static)
+ update_flags |= DPLANE_MAC_WAS_STATIC;
/* Use common helper api */
result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp,
- vid, mac, vtep_ip, sticky);
+ vid, mac, vtep_ip, sticky, nhg_id, update_flags);
return result;
}
/*
* Enqueue vxlan/evpn mac delete.
*/
-enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp,
const struct interface *bridge_ifp,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip)
{
enum zebra_dplane_result result;
+ uint32_t update_flags = 0;
+
+ update_flags |= DPLANE_MAC_REMOTE;
/* Use common helper api */
result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp,
- vid, mac, vtep_ip, false);
+ vid, mac, vtep_ip, false, 0, update_flags);
+ return result;
+}
+
+/*
+ * Enqueue local mac add (or update).
+ */
+enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp,
+ const struct interface *bridge_ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ bool sticky,
+ uint32_t set_static,
+ uint32_t set_inactive)
+{
+ enum zebra_dplane_result result;
+ uint32_t update_flags = 0;
+ struct in_addr vtep_ip;
+
+ if (set_static)
+ update_flags |= DPLANE_MAC_SET_STATIC;
+
+ if (set_inactive)
+ update_flags |= DPLANE_MAC_SET_INACTIVE;
+
+ vtep_ip.s_addr = 0;
+
+ /* Use common helper api */
+ result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp,
+ vid, mac, vtep_ip, sticky, 0,
+ update_flags);
return result;
}
@@ -2872,7 +3005,9 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip,
- bool sticky)
+ bool sticky,
+ uint32_t nhg_id,
+ uint32_t update_flags)
{
struct zebra_ns *zns;
@@ -2893,6 +3028,8 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx,
ctx->u.macinfo.mac = *mac;
ctx->u.macinfo.vid = vid;
ctx->u.macinfo.is_sticky = sticky;
+ ctx->u.macinfo.nhg_id = nhg_id;
+ ctx->u.macinfo.update_flags = update_flags;
}
/*
@@ -2905,7 +3042,9 @@ mac_update_common(enum dplane_op_e op,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip,
- bool sticky)
+ bool sticky,
+ uint32_t nhg_id,
+ uint32_t update_flags)
{
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret;
@@ -2925,7 +3064,8 @@ mac_update_common(enum dplane_op_e op,
ctx->zd_op = op;
/* Common init for the ctx */
- dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky);
+ dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky,
+ nhg_id, update_flags);
/* Enqueue for processing on the dplane pthread */
ret = dplane_update_enqueue(ctx);
@@ -2949,30 +3089,56 @@ mac_update_common(enum dplane_op_e op,
/*
* Enqueue evpn neighbor add for the dataplane.
*/
-enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp,
const struct ipaddr *ip,
const struct ethaddr *mac,
- uint32_t flags)
+ uint32_t flags, bool was_static)
{
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ uint32_t update_flags = 0;
+
+ update_flags |= DPLANE_NEIGH_REMOTE;
+
+ if (was_static)
+ update_flags |= DPLANE_NEIGH_WAS_STATIC;
result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL,
- ifp, mac, ip, flags, DPLANE_NUD_NOARP);
+ ifp, mac, ip, flags, DPLANE_NUD_NOARP,
+ update_flags);
return result;
}
/*
- * Enqueue evpn neighbor update for the dataplane.
+ * Enqueue local neighbor add for the dataplane.
*/
-enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp,
- const struct ipaddr *ip,
- const struct ethaddr *mac)
+enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp,
+ const struct ipaddr *ip,
+ const struct ethaddr *mac,
+ bool set_router, bool set_static,
+ bool set_inactive)
{
- enum zebra_dplane_result result;
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ uint32_t update_flags = 0;
+ uint32_t ntf = 0;
+ uint16_t state;
+
+ if (set_static)
+ update_flags |= DPLANE_NEIGH_SET_STATIC;
+
+ if (set_inactive) {
+ update_flags |= DPLANE_NEIGH_SET_INACTIVE;
+ state = DPLANE_NUD_STALE;
+ } else {
+ state = DPLANE_NUD_REACHABLE;
+ }
- result = neigh_update_internal(DPLANE_OP_NEIGH_UPDATE,
- ifp, mac, ip, 0, DPLANE_NUD_PROBE);
+ if (set_router)
+ ntf |= DPLANE_NTF_ROUTER;
+
+ result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL,
+ ifp, mac, ip, ntf,
+ state, update_flags);
return result;
}
@@ -2980,13 +3146,16 @@ enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp,
/*
* Enqueue evpn neighbor delete for the dataplane.
*/
-enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp,
const struct ipaddr *ip)
{
enum zebra_dplane_result result;
+ uint32_t update_flags = 0;
+
+ update_flags |= DPLANE_NEIGH_REMOTE;
result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE,
- ifp, NULL, ip, 0, 0);
+ ifp, NULL, ip, 0, 0, update_flags);
return result;
}
@@ -3010,7 +3179,7 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp,
addr.ipaddr_v4 = *ip;
result = neigh_update_internal(DPLANE_OP_VTEP_ADD,
- ifp, &mac, &addr, 0, 0);
+ ifp, &mac, &addr, 0, 0, 0);
return result;
}
@@ -3035,20 +3204,32 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
addr.ipaddr_v4 = *ip;
result = neigh_update_internal(DPLANE_OP_VTEP_DELETE,
- ifp, &mac, &addr, 0, 0);
+ ifp, &mac, &addr, 0, 0, 0);
+
+ return result;
+}
+
+enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp,
+ const struct ipaddr *ip)
+{
+ enum zebra_dplane_result result;
+
+ result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip,
+ DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0);
return result;
}
/*
- * Common helper api for evpn neighbor updates
+ * Common helper api for neighbor updates
*/
static enum zebra_dplane_result
neigh_update_internal(enum dplane_op_e op,
const struct interface *ifp,
const struct ethaddr *mac,
const struct ipaddr *ip,
- uint32_t flags, uint16_t state)
+ uint32_t flags, uint16_t state,
+ uint32_t update_flags)
{
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret;
@@ -3059,9 +3240,8 @@ neigh_update_internal(enum dplane_op_e op,
char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s",
- dplane_op2str(op),
+ dplane_op2str(op), ifp->name,
prefix_mac2str(mac, buf1, sizeof(buf1)),
- ifp->name,
ipaddr2str(ip, buf2, sizeof(buf2)));
}
@@ -3085,6 +3265,7 @@ neigh_update_internal(enum dplane_op_e op,
ctx->u.neigh.mac = *mac;
ctx->u.neigh.flags = flags;
ctx->u.neigh.state = state;
+ ctx->u.neigh.update_flags = update_flags;
/* Enqueue for processing on the dplane pthread */
ret = dplane_update_enqueue(ctx);
@@ -3261,8 +3442,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed)
out_max = atomic_load_explicit(&prov->dp_out_max,
memory_order_relaxed);
- vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", "
- "out: %"PRIu64", q_max: %"PRIu64"\n",
+ vty_out(vty,
+ "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64
+ ", out: %" PRIu64 ", q_max: %" PRIu64 "\n",
prov->dp_name, prov->dp_id, in, in_max, out, out_max);
DPLANE_LOCK();
@@ -3534,149 +3716,100 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
* Kernel dataplane provider
*/
-/*
- * Handler for kernel LSP updates
- */
-static enum zebra_dplane_result
-kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
+static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
{
- return kernel_lsp_update(ctx);
-}
+ char buf[PREFIX_STRLEN];
-/*
- * Handler for kernel pseudowire updates
- */
-static enum zebra_dplane_result
-kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
- zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
- dplane_ctx_get_ifname(ctx),
- dplane_op2str(ctx->zd_op),
- dplane_ctx_get_pw_af(ctx),
- dplane_ctx_get_pw_local_label(ctx),
- dplane_ctx_get_pw_remote_label(ctx));
-
- return kernel_pw_update(ctx);
-}
-
-/*
- * Handler for kernel route updates
- */
-static enum zebra_dplane_result
-kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
+ switch (dplane_ctx_get_op(ctx)) {
- prefix2str(dplane_ctx_get_dest(ctx),
- dest_str, sizeof(dest_str));
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf));
zlog_debug("%u:%s Dplane route update ctx %p op %s",
- dplane_ctx_get_vrf(ctx), dest_str,
- ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
- }
-
- return kernel_route_update(ctx);
-}
-
-/*
- * Handler for kernel-facing interface address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
-
- prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
- sizeof(dest_str));
-
- zlog_debug("Dplane intf %s, idx %u, addr %s",
- dplane_op2str(dplane_ctx_get_op(ctx)),
- dplane_ctx_get_ifindex(ctx), dest_str);
- }
-
- return kernel_address_update_ctx(ctx);
-}
+ dplane_ctx_get_vrf(ctx), buf, ctx,
+ dplane_op2str(dplane_ctx_get_op(ctx)));
+ break;
-/**
- * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-static enum zebra_dplane_result
-kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s",
dplane_ctx_get_nhe_id(ctx), ctx,
dplane_op2str(dplane_ctx_get_op(ctx)));
- }
+ break;
- return kernel_nexthop_update(ctx);
-}
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ break;
-/*
- * Handler for kernel-facing EVPN MAC address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[ETHER_ADDR_STRLEN];
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
+ dplane_ctx_get_ifname(ctx),
+ dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx),
+ dplane_ctx_get_pw_local_label(ctx),
+ dplane_ctx_get_pw_remote_label(ctx));
+ break;
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf));
+
+ zlog_debug("Dplane intf %s, idx %u, addr %s",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx), buf);
+ break;
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, mac %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_mac_update_ctx(ctx);
-}
-
-/*
- * Handler for kernel-facing EVPN neighbor updates
- */
-static enum zebra_dplane_result
-kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[PREFIX_STRLEN];
+ break;
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NEIGH_DISCOVER:
ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, ip %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_neigh_update_ctx(ctx);
-}
+ break;
-/*
- * Handler for kernel PBR rule updates
- */
-static enum zebra_dplane_result
-kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p",
dplane_op2str(dplane_ctx_get_op(ctx)),
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_ifindex(ctx), ctx);
+ break;
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
- return kernel_pbr_rule_update(ctx);
+ case DPLANE_OP_NONE:
+ break;
+ }
}
-static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
- enum zebra_dplane_result res)
+static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
{
+ enum zebra_dplane_result res = dplane_ctx_get_status(ctx);
+
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_ROUTE_INSTALL:
@@ -3685,6 +3818,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
1, memory_order_relaxed);
+
+ if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE)
+ && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) {
+ struct nexthop *nexthop;
+
+ /* Update installed nexthops to signal which have been
+ * installed.
+ */
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+ nexthop)) {
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+ }
+ }
break;
case DPLANE_OP_NH_INSTALL:
@@ -3731,6 +3885,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
case DPLANE_OP_NEIGH_DELETE:
case DPLANE_OP_VTEP_ADD:
case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NEIGH_DISCOVER:
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors,
1, memory_order_relaxed);
@@ -3749,11 +3904,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
case DPLANE_OP_SYS_ROUTE_DELETE:
case DPLANE_OP_ROUTE_NOTIFY:
case DPLANE_OP_LSP_NOTIFY:
+ break;
+
case DPLANE_OP_NONE:
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
+ 1, memory_order_relaxed);
break;
}
-
- dplane_ctx_set_status(ctx, res);
}
/*
@@ -3761,7 +3919,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
{
- enum zebra_dplane_result res;
struct zebra_dplane_ctx *ctx, *tctx;
struct dplane_ctx_q work_list;
int counter, limit;
@@ -3775,97 +3932,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
dplane_provider_get_name(prov));
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
- /* A previous provider plugin may have asked to skip the
- * kernel update.
- */
- if (dplane_ctx_is_skip_kernel(ctx)) {
- res = ZEBRA_DPLANE_REQUEST_SUCCESS;
- goto skip_one;
- }
-
- /* Dispatch to appropriate kernel-facing apis */
- switch (dplane_ctx_get_op(ctx)) {
-
- case DPLANE_OP_ROUTE_INSTALL:
- case DPLANE_OP_ROUTE_UPDATE:
- case DPLANE_OP_ROUTE_DELETE:
- res = kernel_dplane_route_update(ctx);
- break;
-
- case DPLANE_OP_NH_INSTALL:
- case DPLANE_OP_NH_UPDATE:
- case DPLANE_OP_NH_DELETE:
- res = kernel_dplane_nexthop_update(ctx);
- break;
-
- case DPLANE_OP_LSP_INSTALL:
- case DPLANE_OP_LSP_UPDATE:
- case DPLANE_OP_LSP_DELETE:
- res = kernel_dplane_lsp_update(ctx);
- break;
-
- case DPLANE_OP_PW_INSTALL:
- case DPLANE_OP_PW_UNINSTALL:
- res = kernel_dplane_pw_update(ctx);
- break;
-
- case DPLANE_OP_ADDR_INSTALL:
- case DPLANE_OP_ADDR_UNINSTALL:
- res = kernel_dplane_address_update(ctx);
- break;
-
- case DPLANE_OP_MAC_INSTALL:
- case DPLANE_OP_MAC_DELETE:
- res = kernel_dplane_mac_update(ctx);
- break;
-
- case DPLANE_OP_NEIGH_INSTALL:
- case DPLANE_OP_NEIGH_UPDATE:
- case DPLANE_OP_NEIGH_DELETE:
- case DPLANE_OP_VTEP_ADD:
- case DPLANE_OP_VTEP_DELETE:
- res = kernel_dplane_neigh_update(ctx);
- break;
-
- case DPLANE_OP_RULE_ADD:
- case DPLANE_OP_RULE_DELETE:
- case DPLANE_OP_RULE_UPDATE:
- res = kernel_dplane_rule_update(ctx);
- break;
-
- /* Ignore 'notifications' - no-op */
- case DPLANE_OP_SYS_ROUTE_ADD:
- case DPLANE_OP_SYS_ROUTE_DELETE:
- case DPLANE_OP_ROUTE_NOTIFY:
- case DPLANE_OP_LSP_NOTIFY:
- res = ZEBRA_DPLANE_REQUEST_SUCCESS;
- break;
-
- default:
- atomic_fetch_add_explicit(
- &zdplane_info.dg_other_errors, 1,
- memory_order_relaxed);
-
- res = ZEBRA_DPLANE_REQUEST_FAILURE;
- break;
- }
-
- skip_one:
- /* If the request isn't pending, we can handle the result right
- * away.
- */
- if (res != ZEBRA_DPLANE_REQUEST_PENDING)
- kernel_dplane_handle_result(ctx, res);
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ kernel_dplane_log_detail(ctx);
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
}
+ kernel_update_multi(&work_list);
+
TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) {
+ kernel_dplane_handle_result(ctx);
+
TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
dplane_provider_enqueue_out_ctx(prov, ctx);
}
@@ -3910,7 +3991,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov)
limit = dplane_provider_get_work_limit(prov);
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
@@ -4003,19 +4083,19 @@ bool dplane_is_in_shutdown(void)
*/
void zebra_dplane_pre_finish(void)
{
- struct zebra_dplane_provider *dp;
+ struct zebra_dplane_provider *prov;
if (IS_ZEBRA_DEBUG_DPLANE)
- zlog_debug("Zebra dataplane pre-fini called");
+ zlog_debug("Zebra dataplane pre-finish called");
zdplane_info.dg_is_shutdown = true;
/* Notify provider(s) of pending shutdown. */
- TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) {
- if (dp->dp_fini == NULL)
+ TAILQ_FOREACH(prov, &zdplane_info.dg_providers_q, dp_prov_link) {
+ if (prov->dp_fini == NULL)
continue;
- dp->dp_fini(dp, true);
+ prov->dp_fini(prov, true /* early */);
}
}
@@ -4337,7 +4417,10 @@ void zebra_dplane_shutdown(void)
zdplane_info.dg_pthread = NULL;
zdplane_info.dg_master = NULL;
- /* Notify provider(s) of final shutdown. */
+ /* Notify provider(s) of final shutdown.
+ * Note that this call is in the main pthread, so providers must
+ * be prepared for that.
+ */
TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) {
if (dp->dp_fini == NULL)
continue;
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 8e873886df..5dd789a851 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -90,7 +90,6 @@ enum zebra_dplane_result {
ZEBRA_DPLANE_REQUEST_QUEUED,
ZEBRA_DPLANE_REQUEST_SUCCESS,
ZEBRA_DPLANE_REQUEST_FAILURE,
- ZEBRA_DPLANE_REQUEST_PENDING,
};
/*
@@ -150,6 +149,9 @@ enum dplane_op_e {
DPLANE_OP_RULE_ADD,
DPLANE_OP_RULE_DELETE,
DPLANE_OP_RULE_UPDATE,
+
+ /* Link layer address discovery */
+ DPLANE_OP_NEIGH_DISCOVER,
};
/*
@@ -161,12 +163,26 @@ enum dplane_op_e {
/* Neighbor cache flags */
#define DPLANE_NTF_EXT_LEARNED 0x01
#define DPLANE_NTF_ROUTER 0x02
+#define DPLANE_NTF_USE 0x04
/* Neighbor cache states */
#define DPLANE_NUD_REACHABLE 0x01
#define DPLANE_NUD_STALE 0x02
#define DPLANE_NUD_NOARP 0x04
#define DPLANE_NUD_PROBE 0x08
+#define DPLANE_NUD_INCOMPLETE 0x10
+
+/* MAC update flags - dplane_mac_info.update_flags */
+#define DPLANE_MAC_REMOTE (1 << 0)
+#define DPLANE_MAC_WAS_STATIC (1 << 1)
+#define DPLANE_MAC_SET_STATIC (1 << 2)
+#define DPLANE_MAC_SET_INACTIVE (1 << 3)
+
+/* Neigh update flags - dplane_neigh_info.update_flags */
+#define DPLANE_NEIGH_REMOTE (1 << 0)
+#define DPLANE_NEIGH_WAS_STATIC (1 << 1)
+#define DPLANE_NEIGH_SET_STATIC (1 << 2)
+#define DPLANE_NEIGH_SET_INACTIVE (1 << 3)
/* Enable system route notifications */
void dplane_enable_sys_route_notifs(void);
@@ -386,6 +402,8 @@ const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx);
/* Accessors for MAC information */
vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx);
bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx);
const struct ethaddr *dplane_ctx_mac_get_addr(
const struct zebra_dplane_ctx *ctx);
const struct in_addr *dplane_ctx_mac_get_vtep_ip(
@@ -399,6 +417,7 @@ const struct ethaddr *dplane_ctx_neigh_get_mac(
const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx);
uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx);
/* Accessors for policy based routing rule information */
int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx);
@@ -412,6 +431,8 @@ uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx);
const struct prefix *
dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
const struct prefix *
@@ -495,20 +516,24 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
/*
* Enqueue evpn mac operations for the dataplane.
*/
-extern struct zebra_dplane_ctx *mac_update_internal(
- enum dplane_op_e op, const struct interface *ifp,
- const struct interface *br_ifp,
- vlanid_t vid, const struct ethaddr *mac,
- struct in_addr vtep_ip, bool sticky);
-
-enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp,
const struct interface *bridge_ifp,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip,
- bool sticky);
+ bool sticky,
+ uint32_t nhg_id,
+ bool was_static);
-enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
+enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp,
+ const struct interface *bridge_ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ bool sticky,
+ uint32_t set_static,
+ uint32_t set_inactive);
+
+enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp,
const struct interface *bridge_ifp,
vlanid_t vid,
const struct ethaddr *mac,
@@ -521,19 +546,22 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx,
vlanid_t vid,
const struct ethaddr *mac,
struct in_addr vtep_ip,
- bool sticky);
+ bool sticky,
+ uint32_t nhg_id, uint32_t update_flags);
/*
* Enqueue evpn neighbor updates for the dataplane.
*/
-enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp,
+enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp,
+ const struct ipaddr *ip,
+ const struct ethaddr *mac,
+ uint32_t flags, bool was_static);
+enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp,
const struct ipaddr *ip,
const struct ethaddr *mac,
- uint32_t flags);
-enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp,
- const struct ipaddr *ip,
- const struct ethaddr *mac);
-enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp,
+ bool set_router, bool set_static,
+ bool set_inactive);
+enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp,
const struct ipaddr *ip);
/*
@@ -546,6 +574,12 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
const struct in_addr *ip,
vni_t vni);
+/*
+ * Enqueue a neighbour discovery request for the dataplane.
+ */
+enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp,
+ const struct ipaddr *ip);
+
/* Forward ref of zebra_pbr_rule */
struct zebra_pbr_rule;
diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h
index 5f2a7a12c6..03953ed17f 100644
--- a/zebra/zebra_errors.h
+++ b/zebra/zebra_errors.h
@@ -134,6 +134,7 @@ enum zebra_log_refs {
EC_ZEBRA_BAD_NHG_MESSAGE,
EC_ZEBRA_DUPLICATE_NHG_MESSAGE,
EC_ZEBRA_VRF_MISCONFIGURED,
+ EC_ZEBRA_ES_CREATE,
};
void zebra_error_init(void);
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
new file mode 100644
index 0000000000..73df93258e
--- /dev/null
+++ b/zebra/zebra_evpn.c
@@ -0,0 +1,1448 @@
+/*
+ * Zebra EVPN for VxLAN code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "hash.h"
+#include "if.h"
+#include "jhash.h"
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "stream.h"
+#include "table.h"
+#include "vlan.h"
+#include "vxlan.h"
+#ifdef GNU_LINUX
+#include <linux/neighbour.h>
+#endif
+
+#include "zebra/zebra_router.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_l2.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mac.h"
+#include "zebra/zebra_evpn_neigh.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_evpn_mh.h"
+#include "zebra/zebra_evpn_vxlan.h"
+#include "zebra/zebra_router.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash");
+DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP");
+
+/* PMSI strings. */
+#define VXLAN_FLOOD_STR_NO_INFO "-"
+#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO
+static const struct message zvtep_flood_str[] = {
+ {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO},
+ {VXLAN_FLOOD_PIM_SM, "PIM-SM"},
+ {VXLAN_FLOOD_HEAD_END_REPL, "HER"},
+ {0}
+};
+
+int advertise_gw_macip_enabled(zebra_evpn_t *zevpn)
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = zebra_vrf_get_evpn();
+ if (zvrf && zvrf->advertise_gw_macip)
+ return 1;
+
+ if (zevpn && zevpn->advertise_gw_macip)
+ return 1;
+
+ return 0;
+}
+
+int advertise_svi_macip_enabled(zebra_evpn_t *zevpn)
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = zebra_vrf_get_evpn();
+ if (zvrf && zvrf->advertise_svi_macip)
+ return 1;
+
+ if (zevpn && zevpn->advertise_svi_macip)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Print a specific EVPN entry.
+ */
+void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt)
+{
+ struct vty *vty;
+ zebra_vtep_t *zvtep;
+ uint32_t num_macs;
+ uint32_t num_neigh;
+ json_object *json = NULL;
+ json_object *json_vtep_list = NULL;
+ json_object *json_ip_str = NULL;
+
+ vty = ctxt[0];
+ json = ctxt[1];
+
+ if (json == NULL) {
+ vty_out(vty, "VNI: %u\n", zevpn->vni);
+ vty_out(vty, " Type: %s\n", "L2");
+ vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id));
+ } else {
+ json_object_int_add(json, "vni", zevpn->vni);
+ json_object_string_add(json, "type", "L2");
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zevpn->vrf_id));
+ }
+
+ if (!zevpn->vxlan_if) { // unexpected
+ if (json == NULL)
+ vty_out(vty, " VxLAN interface: unknown\n");
+ return;
+ }
+ num_macs = num_valid_macs(zevpn);
+ num_neigh = hashcount(zevpn->neigh_table);
+ if (json == NULL) {
+ vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name);
+ vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex);
+ vty_out(vty, " Local VTEP IP: %s\n",
+ inet_ntoa(zevpn->local_vtep_ip));
+ vty_out(vty, " Mcast group: %s\n",
+ inet_ntoa(zevpn->mcast_grp));
+ } else {
+ json_object_string_add(json, "vxlanInterface",
+ zevpn->vxlan_if->name);
+ json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex);
+ json_object_string_add(json, "vtepIp",
+ inet_ntoa(zevpn->local_vtep_ip));
+ json_object_string_add(json, "mcastGroup",
+ inet_ntoa(zevpn->mcast_grp));
+ json_object_string_add(json, "advertiseGatewayMacip",
+ zevpn->advertise_gw_macip ? "Yes" : "No");
+ json_object_int_add(json, "numMacs", num_macs);
+ json_object_int_add(json, "numArpNd", num_neigh);
+ }
+ if (!zevpn->vteps) {
+ if (json == NULL)
+ vty_out(vty, " No remote VTEPs known for this VNI\n");
+ } else {
+ if (json == NULL)
+ vty_out(vty, " Remote VTEPs for this VNI:\n");
+ else
+ json_vtep_list = json_object_new_array();
+ for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
+ const char *flood_str = lookup_msg(zvtep_flood_str,
+ zvtep->flood_control,
+ VXLAN_FLOOD_STR_DEFAULT);
+
+ if (json == NULL) {
+ vty_out(vty, " %s flood: %s\n",
+ inet_ntoa(zvtep->vtep_ip),
+ flood_str);
+ } else {
+ json_ip_str = json_object_new_string(
+ inet_ntoa(zvtep->vtep_ip));
+ json_object_array_add(json_vtep_list,
+ json_ip_str);
+ }
+ }
+ if (json)
+ json_object_object_add(json, "numRemoteVteps",
+ json_vtep_list);
+ }
+ if (json == NULL) {
+ vty_out(vty,
+ " Number of MACs (local and remote) known for this VNI: %u\n",
+ num_macs);
+ vty_out(vty,
+ " Number of ARPs (IPv4 and IPv6, local and remote) "
+ "known for this VNI: %u\n",
+ num_neigh);
+ vty_out(vty, " Advertise-gw-macip: %s\n",
+ zevpn->advertise_gw_macip ? "Yes" : "No");
+ }
+}
+
+/*
+ * Print an EVPN hash entry - called for display of all VNIs.
+ */
+void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[])
+{
+ struct vty *vty;
+ zebra_evpn_t *zevpn;
+ zebra_vtep_t *zvtep;
+ uint32_t num_vteps = 0;
+ uint32_t num_macs = 0;
+ uint32_t num_neigh = 0;
+ json_object *json = NULL;
+ json_object *json_evpn = NULL;
+ json_object *json_ip_str = NULL;
+ json_object *json_vtep_list = NULL;
+
+ vty = ctxt[0];
+ json = ctxt[1];
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+
+ zvtep = zevpn->vteps;
+ while (zvtep) {
+ num_vteps++;
+ zvtep = zvtep->next;
+ }
+
+ num_macs = num_valid_macs(zevpn);
+ num_neigh = hashcount(zevpn->neigh_table);
+ if (json == NULL)
+ vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n",
+ zevpn->vni, "L2",
+ zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown",
+ num_macs, num_neigh, num_vteps,
+ vrf_id_to_name(zevpn->vrf_id));
+ else {
+ char vni_str[VNI_STR_LEN];
+ snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
+ json_evpn = json_object_new_object();
+ json_object_int_add(json_evpn, "vni", zevpn->vni);
+ json_object_string_add(json_evpn, "type", "L2");
+ json_object_string_add(json_evpn, "vxlanIf",
+ zevpn->vxlan_if ? zevpn->vxlan_if->name
+ : "unknown");
+ json_object_int_add(json_evpn, "numMacs", num_macs);
+ json_object_int_add(json_evpn, "numArpNd", num_neigh);
+ json_object_int_add(json_evpn, "numRemoteVteps", num_vteps);
+ json_object_string_add(json_evpn, "tenantVrf",
+ vrf_id_to_name(zevpn->vrf_id));
+ if (num_vteps) {
+ json_vtep_list = json_object_new_array();
+ for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
+ json_ip_str = json_object_new_string(
+ inet_ntoa(zvtep->vtep_ip));
+ json_object_array_add(json_vtep_list,
+ json_ip_str);
+ }
+ json_object_object_add(json_evpn, "remoteVteps",
+ json_vtep_list);
+ }
+ json_object_object_add(json, vni_str, json_evpn);
+ }
+}
+
+/*
+ * Print an EVPN hash entry in detail - called for display of all EVPNs.
+ */
+void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data)
+{
+ struct vty *vty;
+ zebra_evpn_t *zevpn;
+ json_object *json_array = NULL;
+ bool use_json = false;
+ struct zebra_evpn_show *zes = data;
+
+ vty = zes->vty;
+ json_array = zes->json;
+ use_json = zes->use_json;
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+
+ zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array);
+
+ if (!use_json)
+ vty_out(vty, "\n");
+}
+
+int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn)
+{
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
+ continue;
+
+ if (c->address->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (c->address->family == AF_INET6) {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
+ sizeof(struct in6_addr));
+ } else {
+ continue;
+ }
+
+ zebra_evpn_gw_macip_del(ifp, zevpn, &ip);
+ }
+
+ return 0;
+}
+
+int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn)
+{
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
+ continue;
+
+ if (c->address->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (c->address->family == AF_INET6) {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
+ sizeof(struct in6_addr));
+ } else {
+ continue;
+ }
+
+ zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip);
+ }
+ return 0;
+}
+
+static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
+ uint16_t cmd)
+{
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
+ char buf[PREFIX_STRLEN];
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, cmd, vrf_id);
+ stream_put(s, p, sizeof(struct prefix));
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send ip prefix %s %s on vrf %s",
+ prefix2str(p, buf, sizeof(buf)),
+ (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
+ vrf_id_to_name(vrf_id));
+
+ if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD)
+ client->prefixadd_cnt++;
+ else
+ client->prefixdel_cnt++;
+
+ return zserv_send_message(client, s);
+}
+
+int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp,
+ int advertise)
+{
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct prefix p;
+
+ memcpy(&p, c->address, sizeof(struct prefix));
+
+ /* skip link local address */
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
+ continue;
+
+ apply_mask(&p);
+ if (advertise)
+ ip_prefix_send_to_client(ifp->vrf_id, &p,
+ ZEBRA_IP_PREFIX_ROUTE_ADD);
+ else
+ ip_prefix_send_to_client(ifp->vrf_id, &p,
+ ZEBRA_IP_PREFIX_ROUTE_DEL);
+ }
+ return 0;
+}
+
+/*
+ * zebra_evpn_gw_macip_add_to_client
+ */
+int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ethaddr *macaddr, struct ipaddr *ip)
+{
+ zebra_mac_t *mac = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ zif = zevpn->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr,
+ vxl->access_vlan)
+ != 0)
+ return -1;
+
+ return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac);
+}
+
+/*
+ * zebra_evpn_gw_macip_del_from_client
+ */
+int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+
+ /* If the neigh entry is not present nothing to do*/
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
+ if (!n)
+ return 0;
+
+ /* mac entry should be present */
+ mac = zebra_evpn_mac_lookup(zevpn, &n->emac);
+ if (!mac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u",
+ prefix_mac2str(&n->emac,
+ buf1, sizeof(buf1)),
+ ipaddr2str(ip, buf2, sizeof(buf2)),
+ zevpn->vni);
+ return -1;
+ }
+
+ /* If the entry is not local nothing to do*/
+ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
+ return -1;
+
+ /* only need to delete the entry from bgp if we sent it before */
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ ifp->vrf_id, ifp->name, ifp->ifindex, zevpn->vni,
+ prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
+ ipaddr2str(ip, buf2, sizeof(buf2)));
+
+ /* Remove neighbor from BGP. */
+ zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac,
+ n->flags, ZEBRA_NEIGH_ACTIVE,
+ false /*force*/);
+
+ /* Delete this neighbor entry. */
+ zebra_evpn_neigh_del(zevpn, n);
+
+ /* see if the mac needs to be deleted as well*/
+ if (mac)
+ zebra_evpn_deref_ip2mac(zevpn, mac);
+
+ return 0;
+}
+
+void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_evpn_t *zevpn = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *vrr_if = NULL;
+ struct interface *ifp;
+
+ /* Add primary SVI MAC*/
+ zevpn = (zebra_evpn_t *)bucket->data;
+
+ /* Global (Zvrf) advertise-default-gw is disabled,
+ * but zevpn advertise-default-gw is enabled
+ */
+ if (zevpn->advertise_gw_macip) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip",
+ zevpn->vni);
+ return;
+ }
+
+ ifp = zevpn->vxlan_if;
+ if (!ifp)
+ return;
+ zif = ifp->info;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return;
+
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if =
+ zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
+ if (!vlan_if)
+ return;
+
+ /* Del primary MAC-IP */
+ zebra_evpn_del_macip_for_intf(vlan_if, zevpn);
+
+ /* Del VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zebra_evpn_del_macip_for_intf(vrr_if, zevpn);
+
+ return;
+}
+
+void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_evpn_t *zevpn = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *vrr_if = NULL;
+ struct interface *ifp = NULL;
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+
+ ifp = zevpn->vxlan_if;
+ if (!ifp)
+ return;
+ zif = ifp->info;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return;
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if =
+ zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
+ if (!vlan_if)
+ return;
+
+ /* Add primary SVI MAC-IP */
+ zebra_evpn_add_macip_for_intf(vlan_if, zevpn);
+
+ if (advertise_gw_macip_enabled(zevpn)) {
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zebra_evpn_add_macip_for_intf(vrr_if, zevpn);
+ }
+
+ return;
+}
+
+void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_evpn_t *zevpn = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *ifp;
+
+ /* Add primary SVI MAC*/
+ zevpn = (zebra_evpn_t *)bucket->data;
+ if (!zevpn)
+ return;
+
+ /* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip
+ * enabled
+ */
+ if (zevpn->advertise_svi_macip) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip",
+ zevpn->vni);
+ return;
+ }
+
+ ifp = zevpn->vxlan_if;
+ if (!ifp)
+ return;
+ zif = ifp->info;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return;
+
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if =
+ zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
+ if (!vlan_if)
+ return;
+
+ /* Del primary MAC-IP */
+ zebra_evpn_del_macip_for_intf(vlan_if, zevpn);
+
+ return;
+}
+
+/*
+ * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC
+ * notifications, to see if they are of interest.
+ */
+zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp,
+ struct interface *br_if, vlanid_t vid)
+{
+ struct zebra_ns *zns;
+ struct route_node *rn;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif;
+ struct zebra_l2info_bridge *br;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ uint8_t bridge_vlan_aware;
+ zebra_evpn_t *zevpn;
+ int found = 0;
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert(zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ if (!tmp_if)
+ continue;
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative(tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware || vxl->access_vlan == vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ zevpn = zebra_evpn_lookup(vxl->vni);
+ return zevpn;
+}
+
+/*
+ * Map SVI and associated bridge to an EVPN. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ */
+zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp,
+ struct interface *br_if)
+{
+ struct zebra_ns *zns;
+ struct route_node *rn;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif;
+ struct zebra_l2info_bridge *br;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ uint8_t bridge_vlan_aware;
+ vlanid_t vid = 0;
+ zebra_evpn_t *zevpn;
+ int found = 0;
+
+ if (!br_if)
+ return NULL;
+
+ /* Make sure the linked interface is a bridge. */
+ if (!IS_ZEBRA_IF_BRIDGE(br_if))
+ return NULL;
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert(zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+ if (bridge_vlan_aware) {
+ struct zebra_l2info_vlan *vl;
+
+ if (!IS_ZEBRA_IF_VLAN(ifp))
+ return NULL;
+
+ zif = ifp->info;
+ assert(zif);
+ vl = &zif->l2info.vl;
+ vid = vl->vid;
+ }
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ if (!tmp_if)
+ continue;
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative(tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware || vxl->access_vlan == vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ zevpn = zebra_evpn_lookup(vxl->vni);
+ return zevpn;
+}
+
+/* Map to MAC-VLAN interface corresponding to specified SVI interface.
+ */
+struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if,
+ struct interface *svi_if)
+{
+ struct zebra_ns *zns;
+ struct route_node *rn;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif;
+ int found = 0;
+
+ /* Defensive check, caller expected to invoke only with valid bridge. */
+ if (!br_if)
+ return NULL;
+
+ if (!svi_if) {
+ zlog_debug("svi_if is not passed.");
+ return NULL;
+ }
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert(zif);
+
+ /* Identify corresponding VLAN interface. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ /* Check oper status of the SVI. */
+ if (!tmp_if || !if_is_operative(tmp_if))
+ continue;
+ zif = tmp_if->info;
+
+ if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN)
+ continue;
+
+ if (zif->link == svi_if) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found ? tmp_if : NULL;
+}
+
+/*
+ * Install MAC hash entry - called upon access VLAN change.
+ */
+void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_mac_t *mac;
+ struct mac_walk_ctx *wctx = ctxt;
+
+ mac = (zebra_mac_t *)bucket->data;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
+ zebra_evpn_rem_mac_install(wctx->zevpn, mac, false);
+}
+
+/*
+ * Read and populate local MACs and neighbors corresponding to this EVPN.
+ */
+void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp)
+{
+ struct zebra_ns *zns;
+ struct zebra_if *zif;
+ struct interface *vlan_if;
+ struct zebra_l2info_vxlan *vxl;
+ struct interface *vrr_if;
+
+ zif = ifp->info;
+ vxl = &zif->l2info.vxl;
+ zns = zebra_ns_lookup(NS_DEFAULT);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
+ ifp->name, ifp->ifindex, zevpn->vni,
+ zif->brslave_info.bridge_ifindex);
+
+ macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
+ if (vlan_if) {
+
+ /* Add SVI MAC-IP */
+ zebra_evpn_add_macip_for_intf(vlan_if, zevpn);
+
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zebra_evpn_add_macip_for_intf(vrr_if, zevpn);
+
+ neigh_read_for_vlan(zns, vlan_if);
+ }
+}
+
+/*
+ * Hash function for EVPN.
+ */
+unsigned int zebra_evpn_hash_keymake(const void *p)
+{
+ const zebra_evpn_t *zevpn = p;
+
+ return (jhash_1word(zevpn->vni, 0));
+}
+
+/*
+ * Compare 2 evpn hash entries.
+ */
+bool zebra_evpn_hash_cmp(const void *p1, const void *p2)
+{
+ const zebra_evpn_t *zevpn1 = p1;
+ const zebra_evpn_t *zevpn2 = p2;
+
+ return (zevpn1->vni == zevpn2->vni);
+}
+
+int zebra_evpn_list_cmp(void *p1, void *p2)
+{
+ const zebra_evpn_t *zevpn1 = p1;
+ const zebra_evpn_t *zevpn2 = p2;
+
+ if (zevpn1->vni == zevpn2->vni)
+ return 0;
+ return (zevpn1->vni < zevpn2->vni) ? -1 : 1;
+}
+
+/*
+ * Callback to allocate VNI hash entry.
+ */
+void *zebra_evpn_alloc(void *p)
+{
+ const zebra_evpn_t *tmp_vni = p;
+ zebra_evpn_t *zevpn;
+
+ zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(zebra_evpn_t));
+ zevpn->vni = tmp_vni->vni;
+ return ((void *)zevpn);
+}
+
+/*
+ * Look up EVPN hash entry.
+ */
+zebra_evpn_t *zebra_evpn_lookup(vni_t vni)
+{
+ struct zebra_vrf *zvrf;
+ zebra_evpn_t tmp_vni;
+ zebra_evpn_t *zevpn = NULL;
+
+ zvrf = zebra_vrf_get_evpn();
+ assert(zvrf);
+ memset(&tmp_vni, 0, sizeof(zebra_evpn_t));
+ tmp_vni.vni = vni;
+ zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni);
+
+ return zevpn;
+}
+
+/*
+ * Add EVPN hash entry.
+ */
+zebra_evpn_t *zebra_evpn_add(vni_t vni)
+{
+ struct zebra_vrf *zvrf;
+ zebra_evpn_t tmp_zevpn;
+ zebra_evpn_t *zevpn = NULL;
+
+ zvrf = zebra_vrf_get_evpn();
+ assert(zvrf);
+ memset(&tmp_zevpn, 0, sizeof(zebra_evpn_t));
+ tmp_zevpn.vni = vni;
+ zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc);
+ assert(zevpn);
+
+ zebra_evpn_evpn_es_init(zevpn);
+
+ /* Create hash table for MAC */
+ zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table");
+
+ /* Create hash table for neighbors */
+ zevpn->neigh_table = zebra_neigh_db_create("Zebra EVPN Neighbor Table");
+
+ return zevpn;
+}
+
+/*
+ * Delete EVPN hash entry.
+ */
+int zebra_evpn_del(zebra_evpn_t *zevpn)
+{
+ struct zebra_vrf *zvrf;
+ zebra_evpn_t *tmp_zevpn;
+
+ zvrf = zebra_vrf_get_evpn();
+ assert(zvrf);
+
+ /* Free the neighbor hash table. */
+ hash_free(zevpn->neigh_table);
+ zevpn->neigh_table = NULL;
+
+ /* Free the MAC hash table. */
+ hash_free(zevpn->mac_table);
+ zevpn->mac_table = NULL;
+
+ zebra_evpn_evpn_es_cleanup(zevpn);
+
+ /* Free the EVPN hash entry and allocated memory. */
+ tmp_zevpn = hash_release(zvrf->evpn_table, zevpn);
+ XFREE(MTYPE_ZEVPN, tmp_zevpn);
+
+ return 0;
+}
+
+/*
+ * Inform BGP about local EVPN addition.
+ */
+int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn)
+{
+ struct zserv *client;
+ struct stream *s;
+ int rc;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
+ stream_putl(s, zevpn->vni);
+ stream_put_in_addr(s, &zevpn->local_vtep_ip);
+ stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
+ stream_put_in_addr(s, &zevpn->mcast_grp);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send EVPN_ADD %u %s tenant vrf %s to %s", zevpn->vni,
+ inet_ntoa(zevpn->local_vtep_ip),
+ vrf_id_to_name(zevpn->vrf_id),
+ zebra_route_string(client->proto));
+
+ client->vniadd_cnt++;
+ rc = zserv_send_message(client, s);
+
+ if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) {
+ zevpn->flags |= ZEVPN_READY_FOR_BGP;
+ /* once the EVPN is sent the ES-EVIs can also be replayed
+ * to BGP
+ */
+ zebra_evpn_update_all_es(zevpn);
+ }
+ return rc;
+}
+
+/*
+ * Inform BGP about local EVPN deletion.
+ */
+int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ if (zevpn->flags & ZEVPN_READY_FOR_BGP) {
+ zevpn->flags &= ~ZEVPN_READY_FOR_BGP;
+ /* the ES-EVIs must be removed from BGP before the EVPN is */
+ zebra_evpn_update_all_es(zevpn);
+ }
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id());
+ stream_putl(s, zevpn->vni);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni,
+ zebra_route_string(client->proto));
+
+ client->vnidel_cnt++;
+ return zserv_send_message(client, s);
+}
+
+/*
+ * See if remote VTEP matches with prefix.
+ */
+static int zebra_evpn_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
+{
+ return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip));
+}
+
+/*
+ * Locate remote VTEP in EVPN hash table.
+ */
+zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, struct in_addr *vtep_ip)
+{
+ zebra_vtep_t *zvtep;
+
+ if (!zevpn)
+ return NULL;
+
+ for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
+ if (zebra_evpn_vtep_match(vtep_ip, zvtep))
+ break;
+ }
+
+ return zvtep;
+}
+
+/*
+ * Add remote VTEP to EVPN hash table.
+ */
+zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip,
+ int flood_control)
+
+{
+ zebra_vtep_t *zvtep;
+
+ zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(zebra_vtep_t));
+
+ zvtep->vtep_ip = *vtep_ip;
+ zvtep->flood_control = flood_control;
+
+ if (zevpn->vteps)
+ zevpn->vteps->prev = zvtep;
+ zvtep->next = zevpn->vteps;
+ zevpn->vteps = zvtep;
+
+ return zvtep;
+}
+
+/*
+ * Remove remote VTEP from EVPN hash table.
+ */
+int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep)
+{
+ if (zvtep->next)
+ zvtep->next->prev = zvtep->prev;
+ if (zvtep->prev)
+ zvtep->prev->next = zvtep->next;
+ else
+ zevpn->vteps = zvtep->next;
+
+ zvtep->prev = zvtep->next = NULL;
+ XFREE(MTYPE_ZEVPN_VTEP, zvtep);
+
+ return 0;
+}
+
+/*
+ * Delete all remote VTEPs for this EVPN (upon VNI delete). Also
+ * uninstall from kernel if asked to.
+ */
+int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall)
+{
+ zebra_vtep_t *zvtep, *zvtep_next;
+
+ if (!zevpn)
+ return -1;
+
+ for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) {
+ zvtep_next = zvtep->next;
+ if (uninstall)
+ zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip);
+ zebra_evpn_vtep_del(zevpn, zvtep);
+ }
+
+ return 0;
+}
+
+/*
+ * Install remote VTEP into the kernel if the remote VTEP has asked
+ * for head-end-replication.
+ */
+int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep)
+{
+ if (is_vxlan_flooding_head_end() &&
+ (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) {
+ if (ZEBRA_DPLANE_REQUEST_FAILURE ==
+ dplane_vtep_add(zevpn->vxlan_if,
+ &zvtep->vtep_ip, zevpn->vni))
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Uninstall remote VTEP from the kernel.
+ */
+int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip)
+{
+ if (!zevpn->vxlan_if) {
+ zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
+ zevpn->vni, zevpn);
+ return -1;
+ }
+
+ if (ZEBRA_DPLANE_REQUEST_FAILURE ==
+ dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Install or uninstall flood entries in the kernel corresponding to
+ * remote VTEPs. This is invoked upon change to BUM handling.
+ */
+void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket,
+ void *zvrf)
+{
+ zebra_evpn_t *zevpn;
+ zebra_vtep_t *zvtep;
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+ if (!zevpn)
+ return;
+
+ for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) {
+ if (is_vxlan_flooding_head_end())
+ zebra_evpn_vtep_install(zevpn, zvtep);
+ else
+ zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip);
+ }
+}
+
+/*
+ * Cleanup EVPN/VTEP and update kernel
+ */
+void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg)
+{
+ zebra_evpn_t *zevpn = NULL;
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+
+ /* Free up all neighbors and MACs, if any. */
+ zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
+ zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zebra_evpn_vtep_del_all(zevpn, 1);
+
+ /* Delete the hash entry. */
+ zebra_evpn_del(zevpn);
+}
+
+static void
+zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, esi_t *esi)
+{
+ struct sync_mac_ip_ctx ctx;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool sticky;
+ bool remote_gw;
+ zebra_neigh_t *n = NULL;
+
+ sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ /* if sticky or remote-gw ignore updates from the peer */
+ if (sticky || remote_gw) {
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH
+ || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "Ignore sync-macip vni %u mac %s%s%s%s%s",
+ zevpn->vni,
+ prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, ipbuf,
+ sizeof(ipbuf))
+ : "",
+ sticky ? " sticky" : "",
+ remote_gw ? " remote_gw" : "");
+ return;
+ }
+
+ if (ipa_len) {
+ n = zebra_evpn_neigh_lookup(zevpn, ipaddr);
+ if (n
+ && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq))
+ return;
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.mac = zebra_evpn_proc_sync_mac_update(
+ zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx);
+ if (ctx.ignore_macip || !ctx.mac || !ipa_len)
+ return;
+
+ zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq,
+ esi, &ctx);
+}
+
+/************************** remote mac-ip handling **************************/
+/* Process a remote MACIP add from BGP. */
+void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq,
+ struct in_addr vtep_ip, esi_t *esi)
+{
+ zebra_evpn_t *zevpn;
+ zebra_vtep_t *zvtep;
+ zebra_mac_t *mac = NULL;
+ struct interface *ifp = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_vrf *zvrf;
+
+ /* Locate EVPN hash entry - expected to exist. */
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
+ zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni);
+ return;
+ }
+
+ ifp = zevpn->vxlan_if;
+ if (ifp)
+ zif = ifp->info;
+ if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) {
+ zlog_warn(
+ "Ignoring remote MACIP ADD VNI %u, invalid interface state or info",
+ vni);
+ return;
+ }
+
+ /* Type-2 routes from another PE can be interpreted as remote or
+ * SYNC based on the destination ES -
+ * SYNC - if ES is local
+ * REMOTE - if ES is not local
+ */
+ if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) {
+ zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len,
+ ipaddr, flags, seq, esi);
+ return;
+ }
+
+ /* The remote VTEP specified should normally exist, but it is
+ * possible that when peering comes up, peer may advertise MACIP
+ * routes before advertising type-3 routes.
+ */
+ if (vtep_ip.s_addr) {
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
+ if (!zvtep) {
+ zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip,
+ VXLAN_FLOOD_DISABLED);
+ if (!zvtep) {
+ flog_err(
+ EC_ZEBRA_VTEP_ADD_FAILED,
+ "Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD",
+ vni, zevpn);
+ return;
+ }
+
+ zebra_evpn_vtep_install(zevpn, zvtep);
+ }
+ }
+
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
+ if (!zvrf)
+ return;
+
+
+ if (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr,
+ &mac, vtep_ip, flags, seq, esi)
+ != 0)
+ return;
+
+ process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags,
+ seq);
+}
+
+/* Process a remote MACIP delete from BGP. */
+void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ struct in_addr vtep_ip)
+{
+ zebra_evpn_t *zevpn;
+ zebra_mac_t *mac = NULL;
+ zebra_neigh_t *n = NULL;
+ struct interface *ifp = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_ns *zns;
+ struct zebra_l2info_vxlan *vxl;
+ struct zebra_vrf *zvrf;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+
+ /* Locate EVPN hash entry - expected to exist. */
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni);
+ return;
+ }
+
+ ifp = zevpn->vxlan_if;
+ if (ifp)
+ zif = ifp->info;
+ if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Ignoring remote MACIP DEL VNI %u, invalid interface state or info",
+ vni);
+ return;
+ }
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ vxl = &zif->l2info.vxl;
+
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (ipa_len)
+ n = zebra_evpn_neigh_lookup(zevpn, ipaddr);
+
+ if (n && !mac) {
+ zlog_warn(
+ "Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni);
+ return;
+ }
+
+ /* If the remote mac or neighbor doesn't exist there is nothing
+ * more to do. Otherwise, uninstall the entry and then remove it.
+ */
+ if (!mac && !n)
+ return;
+
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
+
+ /* Ignore the delete if this mac is a gateway mac-ip */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
+ && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
+ zlog_warn(
+ "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
+ vni, prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
+ return;
+ }
+
+ /* Uninstall remote neighbor or MAC. */
+ if (n)
+ zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr);
+ else {
+ /* DAD: when MAC is freeze state as remote learn event,
+ * remote mac-ip delete event is received will result in freeze
+ * entry removal, first fetch kernel for the same entry present
+ * as LOCAL and reachable, avoid deleting this entry instead
+ * use kerenel local entry to update during unfreeze time.
+ */
+ if (zvrf->dad_freeze
+ && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)
+ && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry",
+ __func__,
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ mac->flags);
+ macfdb_read_specific_mac(zns, zif->brslave_info.br_if,
+ macaddr, vxl->access_vlan);
+ }
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ if (!ipa_len)
+ zebra_evpn_sync_mac_del(mac);
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_evpn_rem_mac_del(zevpn, mac);
+ }
+ }
+}
+
+/************************** EVPN BGP config management ************************/
+void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_evpn_t *zevpn = NULL;
+
+ zevpn = (zebra_evpn_t *)bucket->data;
+ zevpn->advertise_gw_macip = 0;
+ zevpn->advertise_svi_macip = 0;
+ zevpn->advertise_subnet = 0;
+
+ zebra_evpn_neigh_del_all(zevpn, 1, 0,
+ DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP);
+ zebra_evpn_mac_del_all(zevpn, 1, 0,
+ DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP);
+ zebra_evpn_vtep_del_all(zevpn, 1);
+}
diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h
new file mode 100644
index 0000000000..3b6a5b21e8
--- /dev/null
+++ b/zebra/zebra_evpn.h
@@ -0,0 +1,208 @@
+/*
+ * Zebra EVPN Data structures and definitions
+ * These are "internal" to this function.
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ * Copyright (C) 2020 Volta Networks.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; 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_EVPN_H
+#define _ZEBRA_EVPN_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+#include "bitfield.h"
+
+#include "zebra/zebra_l2.h"
+#include "zebra/interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct zebra_evpn_t_ zebra_evpn_t;
+typedef struct zebra_vtep_t_ zebra_vtep_t;
+
+RB_HEAD(zebra_es_evi_rb_head, zebra_evpn_es_evi);
+RB_PROTOTYPE(zebra_es_evi_rb_head, zebra_evpn_es_evi, rb_node,
+ zebra_es_evi_rb_cmp);
+
+/* Private Structure to pass callback data for hash iterator */
+struct zebra_evpn_show {
+ struct vty *vty;
+ json_object *json;
+ struct zebra_vrf *zvrf;
+ bool use_json;
+};
+
+/*
+ * VTEP info
+ *
+ * Right now, this just has each remote VTEP's IP address.
+ */
+struct zebra_vtep_t_ {
+ /* Remote IP. */
+ /* NOTE: Can only be IPv4 right now. */
+ struct in_addr vtep_ip;
+ /* Flood mode (one of enum vxlan_flood_control) based on the PMSI
+ * tunnel type advertised by the remote VTEP
+ */
+ int flood_control;
+
+ /* Links. */
+ struct zebra_vtep_t_ *next;
+ struct zebra_vtep_t_ *prev;
+};
+
+/*
+ * VNI hash table
+ *
+ * Contains information pertaining to a VNI:
+ * - the list of remote VTEPs (with this VNI)
+ */
+struct zebra_evpn_t_ {
+ /* VNI - key */
+ vni_t vni;
+
+ /* ES flags */
+ uint32_t flags;
+#define ZEVPN_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */
+
+ /* Flag for advertising gw macip */
+ uint8_t advertise_gw_macip;
+
+ /* Flag for advertising svi macip */
+ uint8_t advertise_svi_macip;
+
+ /* Flag for advertising gw macip */
+ uint8_t advertise_subnet;
+
+ /* Corresponding VxLAN interface. */
+ struct interface *vxlan_if;
+
+ /* List of remote VTEPs */
+ zebra_vtep_t *vteps;
+
+ /* Local IP */
+ struct in_addr local_vtep_ip;
+
+ /* PIM-SM MDT group for BUM flooding */
+ struct in_addr mcast_grp;
+
+ /* tenant VRF, if any */
+ vrf_id_t vrf_id;
+
+ /* List of local or remote MAC */
+ struct hash *mac_table;
+
+ /* List of local or remote neighbors (MAC+IP) */
+ struct hash *neigh_table;
+
+ /* RB tree of ES-EVIs */
+ struct zebra_es_evi_rb_head es_evi_rb_tree;
+
+ /* List of local ESs */
+ struct list *local_es_evi_list;
+};
+
+struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if);
+
+static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn)
+{
+ struct interface *ifp;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+
+ ifp = zevpn->vxlan_if;
+ if (!ifp)
+ return NULL;
+ zif = ifp->info;
+ if (!zif)
+ return NULL;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return NULL;
+ zl2_info = zif->l2info.vxl;
+ return zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
+}
+
+int advertise_gw_macip_enabled(zebra_evpn_t *zevpn);
+int advertise_svi_macip_enabled(zebra_evpn_t *zevpn);
+void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt);
+void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]);
+void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data);
+int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn);
+int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn);
+int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp,
+ int advertise);
+int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ethaddr *macaddr, struct ipaddr *ip);
+int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip);
+void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt);
+void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt);
+void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket,
+ void *ctxt);
+zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp,
+ struct interface *br_if, vlanid_t vid);
+zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp,
+ struct interface *br_if);
+struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if,
+ struct interface *svi_if);
+void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp);
+unsigned int zebra_evpn_hash_keymake(const void *p);
+bool zebra_evpn_hash_cmp(const void *p1, const void *p2);
+int zebra_evpn_list_cmp(void *p1, void *p2);
+void *zebra_evpn_alloc(void *p);
+zebra_evpn_t *zebra_evpn_lookup(vni_t vni);
+zebra_evpn_t *zebra_evpn_add(vni_t vni);
+int zebra_evpn_del(zebra_evpn_t *zevpn);
+int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn);
+int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn);
+zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn,
+ struct in_addr *vtep_ip);
+zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip,
+ int flood_control);
+int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep);
+int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall);
+int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep);
+int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip);
+void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket,
+ void *zvrf);
+void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg);
+void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq,
+ struct in_addr vtep_ip, esi_t *esi);
+void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ struct in_addr vtep_ip);
+void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_ZEBRA_EVPN_H */
diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c
new file mode 100644
index 0000000000..b9cc02a276
--- /dev/null
+++ b/zebra/zebra_evpn_mac.c
@@ -0,0 +1,2231 @@
+/*
+ * Zebra EVPN for VxLAN code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "interface.h"
+#include "jhash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "vlan.h"
+#include "json.h"
+
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mh.h"
+#include "zebra/zebra_evpn_mac.h"
+#include "zebra/zebra_evpn_neigh.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, MAC, "EVPN MAC");
+
+/*
+ * Return number of valid MACs in an EVPN's MAC hash table - all
+ * remote MACs and non-internal (auto) local MACs count.
+ */
+uint32_t num_valid_macs(zebra_evpn_t *zevpn)
+{
+ unsigned int i;
+ uint32_t num_macs = 0;
+ struct hash *hash;
+ struct hash_bucket *hb;
+ zebra_mac_t *mac;
+
+ hash = zevpn->mac_table;
+ if (!hash)
+ return num_macs;
+ for (i = 0; i < hash->size; i++) {
+ for (hb = hash->index[i]; hb; hb = hb->next) {
+ mac = (zebra_mac_t *)hb->data;
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+ || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
+ || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
+ num_macs++;
+ }
+ }
+
+ return num_macs;
+}
+
+uint32_t num_dup_detected_macs(zebra_evpn_t *zevpn)
+{
+ unsigned int i;
+ uint32_t num_macs = 0;
+ struct hash *hash;
+ struct hash_bucket *hb;
+ zebra_mac_t *mac;
+
+ hash = zevpn->mac_table;
+ if (!hash)
+ return num_macs;
+ for (i = 0; i < hash->size; i++) {
+ for (hb = hash->index[i]; hb; hb = hb->next) {
+ mac = (zebra_mac_t *)hb->data;
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ num_macs++;
+ }
+ }
+
+ return num_macs;
+}
+
+/*
+ * Install remote MAC into the forwarding plane.
+ */
+int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac,
+ bool was_static)
+{
+ const struct zebra_if *zif, *br_zif;
+ const struct zebra_l2info_vxlan *vxl;
+ bool sticky;
+ enum zebra_dplane_result res;
+ const struct interface *br_ifp;
+ vlanid_t vid;
+ uint32_t nhg_id;
+ struct in_addr vtep_ip;
+
+ if (!(mac->flags & ZEBRA_MAC_REMOTE))
+ return 0;
+
+ zif = zevpn->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ br_ifp = zif->brslave_info.br_if;
+ if (br_ifp == NULL)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ sticky = !!CHECK_FLAG(mac->flags,
+ (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
+
+ /* If nexthop group for the FDB entry is inactive (not programmed in
+ * the dataplane) the MAC entry cannot be installed
+ */
+ if (mac->es) {
+ if (!(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE))
+ return -1;
+ nhg_id = mac->es->nhg_id;
+ vtep_ip.s_addr = 0;
+ } else {
+ nhg_id = 0;
+ vtep_ip = mac->fwd_info.r_vtep_ip;
+ }
+
+ br_zif = (const struct zebra_if *)(br_ifp->info);
+
+ if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
+ vid = vxl->access_vlan;
+ else
+ vid = 0;
+
+ res = dplane_rem_mac_add(zevpn->vxlan_if, br_ifp, vid, &mac->macaddr,
+ vtep_ip, sticky, nhg_id, was_static);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * Uninstall remote MAC from the forwarding plane.
+ */
+int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac)
+{
+ const struct zebra_if *zif, *br_zif;
+ const struct zebra_l2info_vxlan *vxl;
+ struct in_addr vtep_ip;
+ const struct interface *ifp, *br_ifp;
+ vlanid_t vid;
+ enum zebra_dplane_result res;
+
+ if (!(mac->flags & ZEBRA_MAC_REMOTE))
+ return 0;
+
+ if (!zevpn->vxlan_if) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "VNI %u hash %p couldn't be uninstalled - no intf",
+ zevpn->vni, zevpn);
+ return -1;
+ }
+
+ zif = zevpn->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ br_ifp = zif->brslave_info.br_if;
+ if (br_ifp == NULL)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ br_zif = (const struct zebra_if *)br_ifp->info;
+
+ if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
+ vid = vxl->access_vlan;
+ else
+ vid = 0;
+
+ ifp = zevpn->vxlan_if;
+ vtep_ip = mac->fwd_info.r_vtep_ip;
+
+ res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * Decrement neighbor refcount of MAC; uninstall and free it if
+ * appropriate.
+ */
+void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac)
+{
+ if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
+ return;
+
+ /* If all remote neighbors referencing a remote MAC go away,
+ * we need to uninstall the MAC.
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+ && remote_neigh_count(mac) == 0) {
+ zebra_evpn_rem_mac_uninstall(zevpn, mac);
+ zebra_evpn_es_mac_deref_entry(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+ }
+
+ /* If no neighbors, delete the MAC. */
+ if (list_isempty(mac->neigh_list))
+ zebra_evpn_mac_del(zevpn, mac);
+}
+
+static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac,
+ struct interface **ifpP,
+ vlanid_t *vid)
+{
+ /* if the mac is associated with an ES we must get the access
+ * info from the ES
+ */
+ if (mac->es) {
+ struct zebra_if *zif;
+
+ /* get the access port from the es */
+ *ifpP = mac->es->zif ? mac->es->zif->ifp : NULL;
+ /* get the vlan from the EVPN */
+ if (mac->zevpn->vxlan_if) {
+ zif = mac->zevpn->vxlan_if->info;
+ *vid = zif->l2info.vxl.access_vlan;
+ } else {
+ *vid = 0;
+ }
+ } else {
+ struct zebra_ns *zns;
+
+ *vid = mac->fwd_info.local.vid;
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ *ifpP = if_lookup_by_index_per_ns(zns,
+ mac->fwd_info.local.ifindex);
+ }
+}
+
+static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t)
+{
+ struct zebra_vrf *zvrf = NULL;
+ zebra_mac_t *mac = NULL;
+ zebra_evpn_t *zevpn = NULL;
+ struct listnode *node = NULL;
+ zebra_neigh_t *nbr = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ mac = THREAD_ARG(t);
+
+ /* since this is asynchronous we need sanity checks*/
+ zvrf = vrf_info_lookup(mac->zevpn->vrf_id);
+ if (!zvrf)
+ return 0;
+
+ zevpn = zebra_evpn_lookup(mac->zevpn->vni);
+ if (!zevpn)
+ return 0;
+
+ mac = zebra_evpn_mac_lookup(zevpn, &mac->macaddr);
+ if (!mac)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired",
+ __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags, mac->dad_count, listcount(mac->neigh_list));
+
+ /* Remove all IPs as duplicate associcated with this MAC */
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
+ ZEBRA_NEIGH_SET_INACTIVE(nbr);
+ else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE))
+ zebra_evpn_rem_neigh_install(
+ zevpn, nbr, false /*was_static*/);
+ }
+
+ UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+ nbr->dad_count = 0;
+ nbr->detect_start_time.tv_sec = 0;
+ nbr->dad_dup_detect_time = 0;
+ }
+
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+ mac->dad_count = 0;
+ mac->detect_start_time.tv_sec = 0;
+ mac->detect_start_time.tv_usec = 0;
+ mac->dad_dup_detect_time = 0;
+ mac->dad_mac_auto_recovery_timer = NULL;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ /* Inform to BGP */
+ if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr,
+ mac->flags, mac->loc_seq,
+ mac->es))
+ return -1;
+
+ /* Process all neighbors associated with this MAC. */
+ zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0,
+ 0 /*es_change*/);
+
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac);
+
+ /* Install the entry. */
+ zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
+ }
+
+ return 0;
+}
+
+static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
+ zebra_mac_t *mac,
+ struct in_addr vtep_ip,
+ bool do_dad, bool *is_dup_detect,
+ bool is_local)
+{
+ zebra_neigh_t *nbr;
+ struct listnode *node = NULL;
+ struct timeval elapsed = {0, 0};
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ bool reset_params = false;
+
+ if (!(zvrf->dup_addr_detect && do_dad))
+ return;
+
+ /* MAC is detected as duplicate,
+ * Local MAC event -> hold on advertising to BGP.
+ * Remote MAC event -> hold on installing it.
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u",
+ __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags, mac->dad_count,
+ zvrf->dad_freeze_time);
+
+ /* For duplicate MAC do not update
+ * client but update neigh due to
+ * this MAC update.
+ */
+ if (zvrf->dad_freeze)
+ *is_dup_detect = true;
+
+ return;
+ }
+
+ /* Check if detection time (M-secs) expired.
+ * Reset learn count and detection start time.
+ */
+ monotime_since(&mac->detect_start_time, &elapsed);
+ reset_params = (elapsed.tv_sec > zvrf->dad_time);
+ if (is_local && !reset_params) {
+ /* RFC-7432: A PE/VTEP that detects a MAC mobility
+ * event via LOCAL learning starts an M-second timer.
+ *
+ * NOTE: This is the START of the probe with count is
+ * 0 during LOCAL learn event.
+ * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time)
+ */
+ reset_params = !mac->dad_count;
+ }
+
+ if (reset_params) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u",
+ __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags, mac->dad_count);
+
+ mac->dad_count = 0;
+ /* Start dup. addr detection (DAD) start time,
+ * ONLY during LOCAL learn.
+ */
+ if (is_local)
+ monotime(&mac->detect_start_time);
+
+ } else if (!is_local) {
+ /* For REMOTE MAC, increment detection count
+ * ONLY while in probe window, once window passed,
+ * next local learn event should trigger DAD.
+ */
+ mac->dad_count++;
+ }
+
+ /* For LOCAL MAC learn event, once count is reset above via either
+ * initial/start detection time or passed the probe time, the count
+ * needs to be incremented.
+ */
+ if (is_local)
+ mac->dad_count++;
+
+ if (mac->dad_count >= zvrf->dad_max_moves) {
+ flog_warn(EC_ZEBRA_DUP_MAC_DETECTED,
+ "VNI %u: MAC %s detected as duplicate during %s VTEP %s",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ is_local ? "local update, last" :
+ "remote update, from", inet_ntoa(vtep_ip));
+
+ SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
+
+ /* Capture Duplicate detection time */
+ mac->dad_dup_detect_time = monotime(NULL);
+
+ /* Mark all IPs/Neighs as duplicate
+ * associcated with this MAC
+ */
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
+
+ /* Ony Mark IPs which are Local */
+ if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
+ continue;
+
+ SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+
+ nbr->dad_dup_detect_time = monotime(NULL);
+
+ flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+ "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr,
+ buf, sizeof(buf)),
+ ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ is_local ? "local" : "remote");
+ }
+
+ /* Start auto recovery timer for this MAC */
+ THREAD_OFF(mac->dad_mac_auto_recovery_timer);
+ if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start",
+ __func__,
+ prefix_mac2str(&mac->macaddr, buf,
+ sizeof(buf)),
+ mac->flags, zvrf->dad_freeze_time);
+
+ thread_add_timer(zrouter.master,
+ zebra_evpn_dad_mac_auto_recovery_exp,
+ mac, zvrf->dad_freeze_time,
+ &mac->dad_mac_auto_recovery_timer);
+ }
+
+ /* In case of local update, do not inform to client (BGPd),
+ * upd_neigh for neigh sequence change.
+ */
+ if (zvrf->dad_freeze)
+ *is_dup_detect = true;
+ }
+}
+
+/*
+ * Print a specific MAC entry.
+ */
+void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
+{
+ struct vty *vty;
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct zebra_vrf *zvrf;
+ struct timeval detect_start_time = {0, 0};
+ char timebuf[MONOTIME_STRLEN];
+ char thread_buf[THREAD_TIMER_STRLEN];
+
+ zvrf = zebra_vrf_get_evpn();
+ if (!zvrf)
+ return;
+
+ vty = (struct vty *)ctxt;
+ prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
+
+ if (json) {
+ json_object *json_mac = json_object_new_object();
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ struct interface *ifp;
+ vlanid_t vid;
+
+ zebra_evpn_mac_get_access_info(mac, &ifp, &vid);
+ json_object_string_add(json_mac, "type", "local");
+ if (ifp) {
+ json_object_string_add(json_mac, "intf",
+ ifp->name);
+ json_object_int_add(json_mac, "ifindex",
+ ifp->ifindex);
+ }
+ if (vid)
+ json_object_int_add(json_mac, "vlan", vid);
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ json_object_string_add(json_mac, "type", "remote");
+ json_object_string_add(
+ json_mac, "remoteVtep",
+ inet_ntoa(mac->fwd_info.r_vtep_ip));
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
+ json_object_string_add(json_mac, "type", "auto");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+ json_object_boolean_true_add(json_mac, "stickyMac");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
+ json_object_boolean_true_add(json_mac,
+ "defaultGateway");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
+ json_object_boolean_true_add(json_mac,
+ "remoteGatewayMac");
+
+ json_object_int_add(json_mac, "localSequence", mac->loc_seq);
+ json_object_int_add(json_mac, "remoteSequence", mac->rem_seq);
+
+ json_object_int_add(json_mac, "detectionCount", mac->dad_count);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ json_object_boolean_true_add(json_mac, "isDuplicate");
+ else
+ json_object_boolean_false_add(json_mac, "isDuplicate");
+
+ json_object_int_add(json_mac, "syncNeighCount",
+ mac->sync_neigh_cnt);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE))
+ json_object_boolean_true_add(json_mac, "localInactive");
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY))
+ json_object_boolean_true_add(json_mac, "peerProxy");
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ json_object_boolean_true_add(json_mac, "peerActive");
+ if (mac->hold_timer)
+ json_object_string_add(
+ json_mac, "peerActiveHold",
+ thread_timer_to_hhmmss(thread_buf,
+ sizeof(thread_buf),
+ mac->hold_timer));
+ if (mac->es)
+ json_object_string_add(json_mac, "esi",
+ mac->es->esi_str);
+ /* print all the associated neigh */
+ if (!listcount(mac->neigh_list))
+ json_object_string_add(json_mac, "neighbors", "none");
+ else {
+ json_object *json_active_nbrs = json_object_new_array();
+ json_object *json_inactive_nbrs =
+ json_object_new_array();
+ json_object *json_nbrs = json_object_new_object();
+
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ json_object_array_add(
+ json_active_nbrs,
+ json_object_new_string(
+ ipaddr2str(
+ &n->ip, buf2,
+ sizeof(buf2))));
+ else
+ json_object_array_add(
+ json_inactive_nbrs,
+ json_object_new_string(
+ ipaddr2str(
+ &n->ip, buf2,
+ sizeof(buf2))));
+ }
+
+ json_object_object_add(json_nbrs, "active",
+ json_active_nbrs);
+ json_object_object_add(json_nbrs, "inactive",
+ json_inactive_nbrs);
+ json_object_object_add(json_mac, "neighbors",
+ json_nbrs);
+ }
+
+ json_object_object_add(json, buf1, json_mac);
+ } else {
+ vty_out(vty, "MAC: %s\n", buf1);
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ struct interface *ifp;
+ vlanid_t vid;
+
+ zebra_evpn_mac_get_access_info(mac, &ifp, &vid);
+
+ if (mac->es)
+ vty_out(vty, " ESI: %s\n", mac->es->esi_str);
+
+ if (ifp)
+ vty_out(vty, " Intf: %s(%u)", ifp->name,
+ ifp->ifindex);
+ else
+ vty_out(vty, " Intf: -");
+ vty_out(vty, " VLAN: %u", vid);
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ if (mac->es)
+ vty_out(vty, " Remote ES: %s",
+ mac->es->esi_str);
+ else
+ vty_out(vty, " Remote VTEP: %s",
+ inet_ntoa(mac->fwd_info.r_vtep_ip));
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+ vty_out(vty, " Auto Mac ");
+ }
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+ vty_out(vty, " Sticky Mac ");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
+ vty_out(vty, " Default-gateway Mac ");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
+ vty_out(vty, " Remote-gateway Mac ");
+
+ vty_out(vty, "\n");
+ vty_out(vty, " Sync-info: neigh#: %u", mac->sync_neigh_cnt);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE))
+ vty_out(vty, " local-inactive");
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY))
+ vty_out(vty, " peer-proxy");
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ vty_out(vty, " peer-active");
+ if (mac->hold_timer)
+ vty_out(vty, " (ht: %s)",
+ thread_timer_to_hhmmss(thread_buf,
+ sizeof(thread_buf),
+ mac->hold_timer));
+ vty_out(vty, "\n");
+ vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq,
+ mac->rem_seq);
+ vty_out(vty, "\n");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
+ vty_out(vty, " Duplicate, detected at %s",
+ time_to_string(mac->dad_dup_detect_time,
+ timebuf));
+ } else if (mac->dad_count) {
+ monotime_since(&mac->detect_start_time,
+ &detect_start_time);
+ if (detect_start_time.tv_sec <= zvrf->dad_time) {
+ time_to_string(mac->detect_start_time.tv_sec,
+ timebuf);
+ vty_out(vty,
+ " Duplicate detection started at %s, detection count %u\n",
+ timebuf, mac->dad_count);
+ }
+ }
+
+ /* print all the associated neigh */
+ vty_out(vty, " Neighbors:\n");
+ if (!listcount(mac->neigh_list))
+ vty_out(vty, " No Neighbors\n");
+ else {
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+ vty_out(vty, " %s %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)),
+ (IS_ZEBRA_NEIGH_ACTIVE(n)
+ ? "Active"
+ : "Inactive"));
+ }
+ }
+
+ vty_out(vty, "\n");
+ }
+}
+
+static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf,
+ uint32_t flags_buf_sz)
+{
+ snprintf(flags_buf, flags_buf_sz, "%s%s%s%s",
+ mac->sync_neigh_cnt ?
+ "N" : "",
+ (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ?
+ "P" : "",
+ (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ?
+ "X" : "",
+ (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ?
+ "I" : "");
+
+ return flags_buf;
+}
+
+/*
+ * Print MAC hash entry - called for display of all MACs.
+ */
+void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ struct vty *vty;
+ json_object *json_mac_hdr = NULL, *json_mac = NULL;
+ zebra_mac_t *mac;
+ char buf1[ETHER_ADDR_STRLEN];
+ struct mac_walk_ctx *wctx = ctxt;
+ char flags_buf[6];
+
+ vty = wctx->vty;
+ json_mac_hdr = wctx->json;
+ mac = (zebra_mac_t *)bucket->data;
+
+ prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
+
+ if (json_mac_hdr)
+ json_mac = json_object_new_object();
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ struct interface *ifp;
+ vlanid_t vid;
+
+ if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
+ return;
+
+ zebra_evpn_mac_get_access_info(mac, &ifp, &vid);
+ if (json_mac_hdr == NULL) {
+ vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local",
+ zebra_evpn_print_mac_flags(mac, flags_buf,
+ sizeof(flags_buf)),
+ ifp ? ifp->name : "-");
+ } else {
+ json_object_string_add(json_mac, "type", "local");
+ if (ifp)
+ json_object_string_add(json_mac, "intf",
+ ifp->name);
+ }
+ if (vid) {
+ if (json_mac_hdr == NULL)
+ vty_out(vty, " %-5u", vid);
+ else
+ json_object_int_add(json_mac, "vlan", vid);
+ } else /* No vid? fill out the space */
+ if (json_mac_hdr == NULL)
+ vty_out(vty, " %-5s", "");
+ if (json_mac_hdr == NULL) {
+ vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq);
+ vty_out(vty, "\n");
+ } else {
+ json_object_int_add(json_mac, "localSequence",
+ mac->loc_seq);
+ json_object_int_add(json_mac, "remoteSequence",
+ mac->rem_seq);
+ json_object_int_add(json_mac, "detectionCount",
+ mac->dad_count);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ json_object_boolean_true_add(json_mac,
+ "isDuplicate");
+ else
+ json_object_boolean_false_add(json_mac,
+ "isDuplicate");
+ json_object_object_add(json_mac_hdr, buf1, json_mac);
+ }
+
+ wctx->count++;
+
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+
+ if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
+ && !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
+ &wctx->r_vtep_ip))
+ return;
+
+ if (json_mac_hdr == NULL) {
+ if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
+ && (wctx->count == 0)) {
+ vty_out(vty, "\nVNI %u\n\n", wctx->zevpn->vni);
+ vty_out(vty, "%-17s %-6s %-5s%-30s %-5s %s\n",
+ "MAC", "Type", "Flags",
+ "Intf/Remote ES/VTEP", "VLAN",
+ "Seq #'s");
+ }
+ vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1,
+ "remote",
+ zebra_evpn_print_mac_flags(mac, flags_buf,
+ sizeof(flags_buf)),
+ mac->es ? mac->es->esi_str
+ : inet_ntoa(mac->fwd_info.r_vtep_ip),
+ "", mac->loc_seq, mac->rem_seq);
+ } else {
+ json_object_string_add(json_mac, "type", "remote");
+ json_object_string_add(
+ json_mac, "remoteVtep",
+ inet_ntoa(mac->fwd_info.r_vtep_ip));
+ json_object_object_add(json_mac_hdr, buf1, json_mac);
+ json_object_int_add(json_mac, "localSequence",
+ mac->loc_seq);
+ json_object_int_add(json_mac, "remoteSequence",
+ mac->rem_seq);
+ json_object_int_add(json_mac, "detectionCount",
+ mac->dad_count);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ json_object_boolean_true_add(json_mac,
+ "isDuplicate");
+ else
+ json_object_boolean_false_add(json_mac,
+ "isDuplicate");
+ }
+
+ wctx->count++;
+ }
+}
+
+/*
+ * Print MAC hash entry in detail - called for display of all MACs.
+ */
+void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt)
+{
+ struct vty *vty;
+ json_object *json_mac_hdr = NULL;
+ zebra_mac_t *mac;
+ struct mac_walk_ctx *wctx = ctxt;
+ char buf1[ETHER_ADDR_STRLEN];
+
+ vty = wctx->vty;
+ json_mac_hdr = wctx->json;
+ mac = (zebra_mac_t *)bucket->data;
+ if (!mac)
+ return;
+
+ wctx->count++;
+ prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
+
+ zebra_evpn_print_mac(mac, vty, json_mac_hdr);
+}
+
+/*
+ * Inform BGP about local MACIP.
+ */
+int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
+ struct ipaddr *ip, uint8_t flags,
+ uint32_t seq, int state,
+ struct zebra_evpn_es *es, uint16_t cmd)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ int ipa_len;
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
+ esi_t *esi = es ? &es->esi : zero_esi;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, cmd, zebra_vrf_get_evpn_id());
+ stream_putl(s, vni);
+ stream_put(s, macaddr->octet, ETH_ALEN);
+ if (ip) {
+ ipa_len = 0;
+ if (IS_IPADDR_V4(ip))
+ ipa_len = IPV4_MAX_BYTELEN;
+ else if (IS_IPADDR_V6(ip))
+ ipa_len = IPV6_MAX_BYTELEN;
+
+ stream_putl(s, ipa_len); /* IP address length */
+ if (ipa_len)
+ stream_put(s, &ip->ip.addr, ipa_len); /* IP address */
+ } else
+ stream_putl(s, 0); /* Just MAC. */
+
+ if (cmd == ZEBRA_MACIP_ADD) {
+ stream_putc(s, flags); /* sticky mac/gateway mac */
+ stream_putl(s, seq); /* sequence number */
+ stream_put(s, esi, sizeof(esi_t));
+ } else {
+ stream_putl(s, state); /* state - active/inactive */
+ }
+
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s",
+ (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni,
+ es ? es->esi_str : "-",
+ zebra_route_string(client->proto));
+
+ if (cmd == ZEBRA_MACIP_ADD)
+ client->macipadd_cnt++;
+ else
+ client->macipdel_cnt++;
+
+ return zserv_send_message(client, s);
+}
+
+static unsigned int mac_hash_keymake(const void *p)
+{
+ const zebra_mac_t *pmac = p;
+ const void *pnt = (void *)pmac->macaddr.octet;
+
+ return jhash(pnt, ETH_ALEN, 0xa5a5a55a);
+}
+
+/*
+ * Compare two MAC addresses.
+ */
+static bool mac_cmp(const void *p1, const void *p2)
+{
+ const zebra_mac_t *pmac1 = p1;
+ const zebra_mac_t *pmac2 = p2;
+
+ if (pmac1 == NULL && pmac2 == NULL)
+ return true;
+
+ if (pmac1 == NULL || pmac2 == NULL)
+ return false;
+
+ return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN)
+ == 0);
+}
+
+/*
+ * Callback to allocate MAC hash entry.
+ */
+static void *zebra_evpn_mac_alloc(void *p)
+{
+ const zebra_mac_t *tmp_mac = p;
+ zebra_mac_t *mac;
+
+ mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
+ *mac = *tmp_mac;
+
+ return ((void *)mac);
+}
+
+/*
+ * Add MAC entry.
+ */
+zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr)
+{
+ zebra_mac_t tmp_mac;
+ zebra_mac_t *mac = NULL;
+
+ memset(&tmp_mac, 0, sizeof(zebra_mac_t));
+ memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN);
+ mac = hash_get(zevpn->mac_table, &tmp_mac, zebra_evpn_mac_alloc);
+ assert(mac);
+
+ mac->zevpn = zevpn;
+ mac->dad_mac_auto_recovery_timer = NULL;
+
+ mac->neigh_list = list_new();
+ mac->neigh_list->cmp = neigh_list_cmp;
+
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char buf[ETHER_ADDR_STRLEN];
+
+ zlog_debug("%s: MAC %s flags 0x%x", __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags);
+ }
+ return mac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
+{
+ zebra_mac_t *tmp_mac;
+
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char buf[ETHER_ADDR_STRLEN];
+
+ zlog_debug("%s: MAC %s flags 0x%x", __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags);
+ }
+
+ /* force de-ref any ES entry linked to the MAC */
+ zebra_evpn_es_mac_deref_entry(mac);
+
+ /* Cancel proxy hold timer */
+ zebra_evpn_mac_stop_hold_timer(mac);
+
+ /* Cancel auto recovery */
+ THREAD_OFF(mac->dad_mac_auto_recovery_timer);
+
+ list_delete(&mac->neigh_list);
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_mac = hash_release(zevpn->mac_table, mac);
+ XFREE(MTYPE_MAC, tmp_mac);
+
+ return 0;
+}
+
+static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx,
+ zebra_mac_t *mac)
+{
+ if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL))
+ return true;
+ else if ((wctx->flags & DEL_REMOTE_MAC)
+ && (mac->flags & ZEBRA_MAC_REMOTE))
+ return true;
+ else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP)
+ && (mac->flags & ZEBRA_MAC_REMOTE)
+ && IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip))
+ return true;
+ else if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_AUTO)
+ && !listcount(mac->neigh_list)) {
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char buf[ETHER_ADDR_STRLEN];
+
+ zlog_debug(
+ "%s: Del MAC %s flags 0x%x", __func__,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ mac->flags);
+ }
+ wctx->uninstall = 0;
+
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Free MAC hash entry (callback)
+ */
+static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_mac_t *mac = bucket->data;
+
+ if (zebra_evpn_check_mac_del_from_db(wctx, mac)) {
+ if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) {
+ zebra_evpn_mac_send_del_to_client(wctx->zevpn->vni,
+ &mac->macaddr,
+ mac->flags, false);
+ }
+ if (wctx->uninstall) {
+ if (zebra_evpn_mac_is_static(mac))
+ zebra_evpn_sync_mac_dp_install(
+ mac, false /* set_inactive */,
+ true /* force_clear_static */,
+ __func__);
+
+ if (mac->flags & ZEBRA_MAC_REMOTE)
+ zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac);
+ }
+
+ zebra_evpn_mac_del(wctx->zevpn, mac);
+ }
+
+ return;
+}
+
+/*
+ * Delete all MAC entries for this EVPN.
+ */
+void zebra_evpn_mac_del_all(zebra_evpn_t *zevpn, int uninstall, int upd_client,
+ uint32_t flags)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!zevpn->mac_table)
+ return;
+
+ memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ wctx.zevpn = zevpn;
+ wctx.uninstall = uninstall;
+ wctx.upd_client = upd_client;
+ wctx.flags = flags;
+
+ hash_iterate(zevpn->mac_table, zebra_evpn_mac_del_hash_entry, &wctx);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac)
+{
+ zebra_mac_t tmp;
+ zebra_mac_t *pmac;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.macaddr, mac, ETH_ALEN);
+ pmac = hash_lookup(zevpn->mac_table, &tmp);
+
+ return pmac;
+}
+
+/*
+ * Inform BGP about local MAC addition.
+ */
+int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
+ uint32_t mac_flags, uint32_t seq,
+ struct zebra_evpn_es *es)
+{
+ uint8_t flags = 0;
+
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_LOCAL_INACTIVE)) {
+ /* host reachability has not been verified locally */
+
+ /* if no ES peer is claiming reachability we can't advertise the
+ * entry
+ */
+ if (!CHECK_FLAG(mac_flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ return 0;
+
+ /* ES peers are claiming reachability; we will
+ * advertise the entry but with a proxy flag
+ */
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+
+ return zebra_evpn_macip_send_msg_to_client(vni, macaddr, NULL, flags,
+ seq, ZEBRA_NEIGH_ACTIVE, es,
+ ZEBRA_MACIP_ADD);
+}
+
+/*
+ * Inform BGP about local MAC deletion.
+ */
+int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
+ uint32_t flags, bool force)
+{
+ if (!force) {
+ if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE)
+ && !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ /* the host was not advertised - nothing to delete */
+ return 0;
+ }
+
+ return zebra_evpn_macip_send_msg_to_client(
+ vni, macaddr, NULL, 0 /* flags */, 0 /* seq */,
+ ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL);
+}
+
+/*
+ * wrapper to create a MAC hash table
+ */
+struct hash *zebra_mac_db_create(const char *desc)
+{
+ return hash_create(mac_hash_keymake, mac_cmp, desc);
+}
+
+/* program sync mac flags in the dataplane */
+void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
+ bool force_clear_static, const char *caller)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ struct interface *ifp;
+ bool sticky;
+ bool set_static;
+ zebra_evpn_t *zevpn = mac->zevpn;
+ vlanid_t vid;
+ struct zebra_if *zif;
+ struct interface *br_ifp;
+
+ /* get the access vlan from the vxlan_device */
+ zebra_evpn_mac_get_access_info(mac, &ifp, &vid);
+
+ if (!ifp) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port",
+ caller, zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags,
+ set_inactive ? "inactive " : "");
+ return;
+ }
+
+ zif = ifp->info;
+ br_ifp = zif->brslave_info.br_if;
+ if (!br_ifp) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br",
+ caller, zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags,
+ set_inactive ? "inactive " : "");
+ return;
+ }
+
+ sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ if (force_clear_static)
+ set_static = false;
+ else
+ set_static = zebra_evpn_mac_is_static(mac);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s",
+ zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags,
+ set_static ? "static " : "",
+ set_inactive ? "inactive " : "");
+
+ dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky,
+ set_static, set_inactive);
+}
+
+void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
+ bool new_bgp_ready)
+{
+ if (new_bgp_ready)
+ zebra_evpn_mac_send_add_to_client(mac->zevpn->vni,
+ &mac->macaddr, mac->flags,
+ mac->loc_seq, mac->es);
+ else if (old_bgp_ready)
+ zebra_evpn_mac_send_del_to_client(mac->zevpn->vni,
+ &mac->macaddr, mac->flags,
+ true /* force */);
+}
+
+/* MAC hold timer is used to age out peer-active flag.
+ *
+ * During this wait time we expect the dataplane component or an
+ * external neighmgr daemon to probe existing hosts to independently
+ * establish their presence on the ES.
+ */
+static int zebra_evpn_mac_hold_exp_cb(struct thread *t)
+{
+ zebra_mac_t *mac;
+ bool old_bgp_ready;
+ bool new_bgp_ready;
+ bool old_static;
+ bool new_static;
+ char macbuf[ETHER_ADDR_STRLEN];
+
+ mac = THREAD_ARG(t);
+ /* the purpose of the hold timer is to age out the peer-active
+ * flag
+ */
+ if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ return 0;
+
+ old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ old_static = zebra_evpn_mac_is_static(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE);
+ new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ new_static = zebra_evpn_mac_is_static(mac);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-mac vni %u mac %s es %s 0x%x hold expired",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags);
+
+ /* re-program the local mac in the dataplane if the mac is no
+ * longer static
+ */
+ if (old_static != new_static)
+ zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ /* inform bgp if needed */
+ if (old_bgp_ready != new_bgp_ready)
+ zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready,
+ new_bgp_ready);
+
+ return 0;
+}
+
+static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+
+ if (mac->hold_timer)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-mac vni %u mac %s es %s 0x%x hold started",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags);
+ thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac,
+ zmh_info->mac_hold_time, &mac->hold_timer);
+}
+
+void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+
+ if (!mac->hold_timer)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-mac vni %u mac %s es %s 0x%x hold stopped",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->flags);
+ THREAD_OFF(mac->hold_timer);
+}
+
+void zebra_evpn_sync_mac_del(zebra_mac_t *mac)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ bool old_static;
+ bool new_static;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-mac del vni %u mac %s es %s seq %d f 0x%x",
+ mac->zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->loc_seq,
+ mac->flags);
+ old_static = zebra_evpn_mac_is_static(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ zebra_evpn_mac_start_hold_timer(mac);
+ new_static = zebra_evpn_mac_is_static(mac);
+
+ if (old_static != new_static)
+ /* program the local mac in the kernel */
+ zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+}
+
+static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
+ zebra_mac_t *mac, uint32_t seq,
+ uint16_t ipa_len,
+ struct ipaddr *ipaddr)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ uint32_t tmp_seq;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+ tmp_seq = mac->loc_seq;
+ else
+ tmp_seq = mac->rem_seq;
+
+ if (seq < tmp_seq) {
+ /* if the mac was never advertised to bgp we must accept
+ * whatever sequence number bgp sends
+ * XXX - check with Vivek
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
+ && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-macip accept vni %u mac %s%s%s lower seq %u f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, ipbuf,
+ sizeof(ipbuf))
+ : "",
+ tmp_seq, mac->flags);
+ return true;
+ }
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-macip ignore vni %u mac %s%s%s as existing has higher seq %u f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, ipbuf,
+ sizeof(ipbuf))
+ : "",
+ tmp_seq, mac->flags);
+ return false;
+ }
+
+ return true;
+}
+
+zebra_mac_t *
+zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, esi_t *esi,
+ struct sync_mac_ip_ctx *ctx)
+{
+ zebra_mac_t *mac;
+ bool inform_bgp = false;
+ bool inform_dataplane = false;
+ bool seq_change = false;
+ bool es_change = false;
+ uint32_t tmp_seq;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool old_local = false;
+ bool old_bgp_ready;
+ bool new_bgp_ready;
+
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!mac) {
+ /* if it is a new local path we need to inform both
+ * the control protocol and the data-plane
+ */
+ inform_bgp = true;
+ inform_dataplane = true;
+ ctx->mac_created = true;
+ ctx->mac_inactive = true;
+
+ /* create the MAC and associate it with the dest ES */
+ mac = zebra_evpn_mac_add(zevpn, macaddr);
+ zebra_evpn_es_mac_ref(mac, esi);
+
+ /* local mac activated by an ES peer */
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ /* if mac-only route setup peer flags */
+ if (!ipa_len) {
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT))
+ SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY);
+ else
+ SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE);
+ }
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
+ old_bgp_ready = false;
+ new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ } else {
+ uint32_t old_flags;
+ uint32_t new_flags;
+ bool old_static;
+ bool new_static;
+ bool sticky;
+ bool remote_gw;
+
+ old_flags = mac->flags;
+ sticky = !!CHECK_FLAG(old_flags, ZEBRA_MAC_STICKY);
+ remote_gw = !!CHECK_FLAG(old_flags, ZEBRA_MAC_REMOTE_DEF_GW);
+ if (sticky || remote_gw) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "Ignore sync-macip vni %u mac %s%s%s%s%s",
+ zevpn->vni,
+ prefix_mac2str(macaddr, macbuf,
+ sizeof(macbuf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, ipbuf,
+ sizeof(ipbuf))
+ : "",
+ sticky ? " sticky" : "",
+ remote_gw ? " remote_gw" : "");
+ ctx->ignore_macip = true;
+ return NULL;
+ }
+ if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, ipa_len,
+ ipaddr)) {
+ ctx->ignore_macip = true;
+ return NULL;
+ }
+
+ old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL);
+ old_static = zebra_evpn_mac_is_static(mac);
+
+ /* re-build the mac flags */
+ new_flags = 0;
+ SET_FLAG(new_flags, ZEBRA_MAC_LOCAL);
+ /* retain old local activity flag */
+ if (old_flags & ZEBRA_MAC_LOCAL) {
+ new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE);
+ } else {
+ new_flags |= ZEBRA_MAC_LOCAL_INACTIVE;
+ ctx->mac_inactive = true;
+ }
+ if (ipa_len) {
+ /* if mac-ip route do NOT update the peer flags
+ * i.e. retain only flags as is
+ */
+ new_flags |= (old_flags & ZEBRA_MAC_ALL_PEER_FLAGS);
+ } else {
+ /* if mac-only route update peer flags */
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) {
+ SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_PROXY);
+ /* if the mac was peer-active previously we
+ * need to keep the flag and start the
+ * holdtimer on it. the peer-active flag is
+ * cleared on holdtimer expiry.
+ */
+ if (CHECK_FLAG(old_flags,
+ ZEBRA_MAC_ES_PEER_ACTIVE)) {
+ SET_FLAG(new_flags,
+ ZEBRA_MAC_ES_PEER_ACTIVE);
+ zebra_evpn_mac_start_hold_timer(mac);
+ }
+ } else {
+ SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_ACTIVE);
+ /* stop hold timer if a peer has verified
+ * reachability
+ */
+ zebra_evpn_mac_stop_hold_timer(mac);
+ }
+ }
+ mac->rem_seq = 0;
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ mac->flags = new_flags;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags))
+ zlog_debug(
+ "sync-mac vni %u mac %s old_f 0x%x new_f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ old_flags, mac->flags);
+
+ /* update es */
+ es_change = zebra_evpn_es_mac_ref(mac, esi);
+ /* if mac dest change - inform both sides */
+ if (es_change) {
+ inform_bgp = true;
+ inform_dataplane = true;
+ ctx->mac_inactive = true;
+ }
+ /* if peer-flag is being set notify dataplane that the
+ * entry must not be expired because of local inactivity
+ */
+ new_static = zebra_evpn_mac_is_static(mac);
+ if (old_static != new_static)
+ inform_dataplane = true;
+
+ old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(old_flags);
+ new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ if (old_bgp_ready != new_bgp_ready)
+ inform_bgp = true;
+ }
+
+
+ /* update sequence number; if that results in a new local sequence
+ * inform bgp
+ */
+ tmp_seq = MAX(mac->loc_seq, seq);
+ if (tmp_seq != mac->loc_seq) {
+ mac->loc_seq = tmp_seq;
+ seq_change = true;
+ inform_bgp = true;
+ }
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s",
+ ctx->mac_created ? "created" : "updated", zevpn->vni,
+ prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-", mac->loc_seq,
+ mac->flags, inform_bgp ? " inform_bgp" : "",
+ inform_dataplane ? " inform_dp" : "");
+
+ if (inform_bgp)
+ zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready,
+ new_bgp_ready);
+
+ /* neighs using the mac may need to be re-sent to
+ * bgp with updated info
+ */
+ if (seq_change || es_change || !old_local)
+ zebra_evpn_process_neigh_on_local_mac_change(
+ zevpn, mac, seq_change, es_change);
+
+ if (inform_dataplane) {
+ if (ipa_len)
+ /* if the mac is being created as a part of MAC-IP
+ * route wait for the neigh to be updated or
+ * created before programming the mac
+ */
+ ctx->mac_dp_update_deferred = true;
+ else
+ /* program the local mac in the kernel. when the ES
+ * change we need to force the dataplane to reset
+ * the activity as we are yet to establish activity
+ * locally
+ */
+ zebra_evpn_sync_mac_dp_install(
+ mac, ctx->mac_inactive,
+ false /* force_clear_static */, __func__);
+ }
+
+ return mac;
+}
+
+/* update local fowarding info. return true if a dest-ES change
+ * is detected
+ */
+static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac,
+ struct interface *ifp,
+ vlanid_t vid)
+{
+ struct zebra_if *zif = ifp->info;
+ bool es_change;
+
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+
+ es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es);
+
+ if (!mac->es) {
+ /* if es is set fwd_info is not-relevant/taped-out */
+ mac->fwd_info.local.ifindex = ifp->ifindex;
+ mac->fwd_info.local.vid = vid;
+ }
+
+ return es_change;
+}
+
+/* Notify Local MACs to the clienti, skips GW MAC */
+static void zebra_evpn_send_mac_hash_entry_to_client(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_mac_t *zmac = bucket->data;
+
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW))
+ return;
+
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL))
+ zebra_evpn_mac_send_add_to_client(wctx->zevpn->vni,
+ &zmac->macaddr, zmac->flags,
+ zmac->loc_seq, zmac->es);
+}
+
+/* Iterator to Notify Local MACs of a EVPN */
+void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevpn)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!zevpn->mac_table)
+ return;
+
+ memset(&wctx, 0, sizeof(struct mac_walk_ctx));
+ wctx.zevpn = zevpn;
+
+ hash_iterate(zevpn->mac_table, zebra_evpn_send_mac_hash_entry_to_client,
+ &wctx);
+}
+
+void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
+{
+ zebra_evpn_process_neigh_on_remote_mac_del(zevpn, mac);
+ /* the remote sequence number in the auto mac entry
+ * needs to be reset to 0 as the mac entry may have
+ * been removed on all VTEPs (including
+ * the originating one)
+ */
+ mac->rem_seq = 0;
+
+ /* If all remote neighbors referencing a remote MAC
+ * go away, we need to uninstall the MAC.
+ */
+ if (remote_neigh_count(mac) == 0) {
+ zebra_evpn_rem_mac_uninstall(zevpn, mac);
+ zebra_evpn_es_mac_deref_entry(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+ }
+
+ if (list_isempty(mac->neigh_list))
+ zebra_evpn_mac_del(zevpn, mac);
+ else
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+}
+
+/* Print Duplicate MAC */
+void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_mac_t *mac;
+
+ mac = (zebra_mac_t *)bucket->data;
+ if (!mac)
+ return;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ zebra_evpn_print_mac_hash(bucket, ctxt);
+}
+
+/* Print Duplicate MAC in detail */
+void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_mac_t *mac;
+
+ mac = (zebra_mac_t *)bucket->data;
+ if (!mac)
+ return;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ zebra_evpn_print_mac_hash_detail(bucket, ctxt);
+}
+
+int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ struct ethaddr *macaddr, uint16_t ipa_len,
+ struct ipaddr *ipaddr, zebra_mac_t **macp,
+ struct in_addr vtep_ip, uint8_t flags,
+ uint32_t seq, esi_t *esi)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ uint32_t tmp_seq;
+ bool sticky;
+ bool remote_gw;
+ int update_mac = 0;
+ bool do_dad = false;
+ bool is_dup_detect = false;
+ esi_t *old_esi;
+ bool old_static = false;
+ zebra_mac_t *mac;
+
+ sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+
+ /* Ignore if the mac is already present as a gateway mac */
+ if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)
+ && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
+ zevpn->vni,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1))
+ : "");
+ return -1;
+ }
+
+ old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi;
+
+ /* check if the remote MAC is unknown or has a change.
+ * If so, that needs to be updated first. Note that client could
+ * install MAC and MACIP separately or just install the latter.
+ */
+ if (!mac || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+ || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)
+ || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)
+ || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip)
+ || memcmp(old_esi, esi, sizeof(esi_t)) || seq != mac->rem_seq)
+ update_mac = 1;
+
+ if (update_mac) {
+ if (!mac) {
+ mac = zebra_evpn_mac_add(zevpn, macaddr);
+ if (!mac) {
+ zlog_warn(
+ "Failed to add MAC %s VNI %u Remote VTEP %s",
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ zevpn->vni, inet_ntoa(vtep_ip));
+ return -1;
+ }
+
+ zebra_evpn_es_mac_ref(mac, esi);
+
+ /* Is this MAC created for a MACIP? */
+ if (ipa_len)
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ } else {
+ zebra_evpn_es_mac_ref(mac, esi);
+
+ /* When host moves but changes its (MAC,IP)
+ * binding, BGP may install a MACIP entry that
+ * corresponds to "older" location of the host
+ * in transient situations (because {IP1,M1}
+ * is a different route from {IP1,M2}). Check
+ * the sequence number and ignore this update
+ * if appropriate.
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+ tmp_seq = mac->loc_seq;
+ else
+ tmp_seq = mac->rem_seq;
+
+ if (seq < tmp_seq) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ ipa_len ? " IP " : "",
+ ipa_len ? ipaddr2str(
+ ipaddr, buf1,
+ sizeof(buf1))
+ : "",
+ tmp_seq, mac->flags);
+ return -1;
+ }
+ }
+
+ /* Check MAC's curent state is local (this is the case
+ * where MAC has moved from L->R) and check previous
+ * detection started via local learning.
+ * RFC-7432: A PE/VTEP that detects a MAC mobility
+ * event via local learning starts an M-second timer.
+ *
+ * VTEP-IP or seq. change alone is not considered
+ * for dup. detection.
+ *
+ * MAC is already marked duplicate set dad, then
+ * is_dup_detect will be set to not install the entry.
+ */
+ if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+ && mac->dad_count)
+ || CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
+ do_dad = true;
+
+ /* Remove local MAC from BGP. */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ /* force drop the sync flags */
+ old_static = zebra_evpn_mac_is_static(mac);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "sync-mac->remote vni %u mac %s es %s seq %d f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ mac->es ? mac->es->esi_str : "-",
+ mac->loc_seq, mac->flags);
+ zebra_evpn_mac_clear_sync_info(mac);
+ zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr,
+ mac->flags,
+ false /* force */);
+ }
+
+ /* Set "auto" and "remote" forwarding info. */
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS);
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+ mac->fwd_info.r_vtep_ip = vtep_ip;
+
+ if (sticky)
+ SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ else
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+
+ if (remote_gw)
+ SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
+ else
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
+
+ zebra_evpn_dup_addr_detect_for_mac(
+ zvrf, mac, mac->fwd_info.r_vtep_ip, do_dad,
+ &is_dup_detect, false);
+
+ if (!is_dup_detect) {
+ zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac);
+ /* Install the entry. */
+ zebra_evpn_rem_mac_install(zevpn, mac, old_static);
+ }
+ }
+
+ /* Update seq number. */
+ mac->rem_seq = seq;
+
+ /* If there is no IP, return after clearing AUTO flag of MAC. */
+ if (!ipa_len) {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ return -1;
+ }
+ *macp = mac;
+ return 0;
+}
+
+int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
+ struct interface *ifp,
+ struct ethaddr *macaddr, vlanid_t vid,
+ bool sticky, bool local_inactive,
+ bool dp_static)
+{
+ zebra_mac_t *mac;
+ char buf[ETHER_ADDR_STRLEN];
+ bool mac_sticky = false;
+ bool inform_client = false;
+ bool upd_neigh = false;
+ bool is_dup_detect = false;
+ struct in_addr vtep_ip = {.s_addr = 0};
+ bool es_change = false;
+ bool new_bgp_ready;
+ /* assume inactive if not present or if not local */
+ bool old_local_inactive = true;
+ bool old_bgp_ready = false;
+ bool inform_dataplane = false;
+ bool new_static = false;
+
+ assert(ifp);
+ /* Check if we need to create or update or it is a NO-OP. */
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!mac) {
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s",
+ sticky ? "sticky " : "",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, vid, zevpn->vni,
+ local_inactive ? " local-inactive" : "");
+
+ mac = zebra_evpn_mac_add(zevpn, macaddr);
+ if (!mac) {
+ flog_err(
+ EC_ZEBRA_MAC_ADD_FAILED,
+ "Failed to add MAC %s intf %s(%u) VID %u VNI %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, vid, zevpn->vni);
+ return -1;
+ }
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ es_change = zebra_evpn_local_mac_update_fwd_info(mac, ifp, vid);
+ if (sticky)
+ SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ inform_client = true;
+ } else {
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x",
+ sticky ? "sticky " : "",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, vid, zevpn->vni,
+ local_inactive ? "local-inactive " : "",
+ mac->flags);
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ struct interface *old_ifp;
+ vlanid_t old_vid;
+ bool old_static;
+
+ zebra_evpn_mac_get_access_info(mac, &old_ifp, &old_vid);
+ old_bgp_ready =
+ zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ old_local_inactive =
+ !!(mac->flags & ZEBRA_MAC_LOCAL_INACTIVE);
+ old_static = zebra_evpn_mac_is_static(mac);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+ mac_sticky = true;
+
+ /*
+ * Update any changes and if changes are relevant to
+ * BGP, note it.
+ */
+ if (mac_sticky == sticky && old_ifp == ifp
+ && old_vid == vid
+ && old_local_inactive == local_inactive
+ && dp_static == old_static) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, "
+ "entry exists and has not changed ",
+ sticky ? "sticky " : "",
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ ifp->name, ifp->ifindex, vid,
+ zevpn->vni,
+ local_inactive
+ ? " local_inactive"
+ : "");
+ return 0;
+ }
+ if (mac_sticky != sticky) {
+ if (sticky)
+ SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ else
+ UNSET_FLAG(mac->flags,
+ ZEBRA_MAC_STICKY);
+ inform_client = true;
+ }
+
+ es_change = zebra_evpn_local_mac_update_fwd_info(
+ mac, ifp, vid);
+ /* If an es_change is detected we need to advertise
+ * the route with a sequence that is one
+ * greater. This is need to indicate a mac-move
+ * to the ES peers
+ */
+ if (es_change) {
+ mac->loc_seq = mac->loc_seq + 1;
+ /* force drop the peer/sync info as it is
+ * simply no longer relevant
+ */
+ if (CHECK_FLAG(mac->flags,
+ ZEBRA_MAC_ALL_PEER_FLAGS)) {
+ zebra_evpn_mac_clear_sync_info(mac);
+ new_static =
+ zebra_evpn_mac_is_static(mac);
+ /* if we clear peer-flags we
+ * also need to notify the dataplane
+ * to drop the static flag
+ */
+ if (old_static != new_static)
+ inform_dataplane = true;
+ }
+ }
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
+ || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+ bool do_dad = false;
+
+ /*
+ * MAC has either moved or was "internally" created due
+ * to a neighbor learn and is now actually learnt. If
+ * it was learnt as a remote sticky MAC, this is an
+ * operator error.
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
+ flog_warn(
+ EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT,
+ "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u",
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ inet_ntoa(mac->fwd_info.r_vtep_ip),
+ zevpn->vni);
+ return 0;
+ }
+
+ /* If an actual move, compute MAC's seq number */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ mac->loc_seq =
+ MAX(mac->rem_seq + 1, mac->loc_seq);
+ vtep_ip = mac->fwd_info.r_vtep_ip;
+ /* Trigger DAD for remote MAC */
+ do_dad = true;
+ }
+
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ es_change = zebra_evpn_local_mac_update_fwd_info(
+ mac, ifp, vid);
+ if (sticky)
+ SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ else
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ /*
+ * We have to inform BGP of this MAC as well as process
+ * all neighbors.
+ */
+ inform_client = true;
+ upd_neigh = true;
+
+ zebra_evpn_dup_addr_detect_for_mac(
+ zvrf, mac, vtep_ip, do_dad, &is_dup_detect,
+ true);
+ if (is_dup_detect) {
+ inform_client = false;
+ upd_neigh = false;
+ }
+ }
+ }
+
+ /* if the dataplane thinks the entry is sync but it is
+ * not sync in zebra we need to re-install to fixup
+ */
+ if (dp_static) {
+ new_static = zebra_evpn_mac_is_static(mac);
+ if (!new_static)
+ inform_dataplane = true;
+ }
+
+ if (local_inactive)
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
+ else
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
+
+ new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ /* if local-activity has changed we need update bgp
+ * even if bgp already knows about the mac
+ */
+ if ((old_local_inactive != local_inactive)
+ || (new_bgp_ready != old_bgp_ready)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "local mac vni %u mac %s es %s seq %d f 0x%x%s",
+ zevpn->vni,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ mac->es ? mac->es->esi_str : "", mac->loc_seq,
+ mac->flags,
+ local_inactive ? " local-inactive" : "");
+ inform_client = true;
+ }
+
+ if (es_change) {
+ inform_client = true;
+ upd_neigh = true;
+ }
+
+ /* Inform dataplane if required. */
+ if (inform_dataplane)
+ zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ /* Inform BGP if required. */
+ if (inform_client)
+ zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready,
+ new_bgp_ready);
+
+ /* Process all neighbors associated with this MAC, if required. */
+ if (upd_neigh)
+ zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0,
+ es_change);
+
+ return 0;
+}
+
+int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
+ struct interface *ifp)
+{
+ zebra_mac_t *mac;
+ char buf[ETHER_ADDR_STRLEN];
+ bool old_bgp_ready;
+ bool new_bgp_ready;
+ /* If entry doesn't exist, nothing to do. */
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!mac)
+ return 0;
+
+ /* Is it a local entry? */
+ if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
+ ifp->ifindex, mac->fwd_info.local.vid, zevpn->vni,
+ mac->loc_seq, mac->flags, listcount(mac->neigh_list));
+
+ old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ if (zebra_evpn_mac_is_static(mac)) {
+ /* this is a synced entry and can only be removed when the
+ * es-peers stop advertising it.
+ */
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug(
+ "re-add sync-mac vni %u mac %s es %s seq %d f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ mac->es ? mac->es->esi_str : "-", mac->loc_seq,
+ mac->flags);
+
+ /* inform-bgp about change in local-activity if any */
+ if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) {
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE);
+ new_bgp_ready =
+ zebra_evpn_mac_is_ready_for_bgp(mac->flags);
+ zebra_evpn_mac_send_add_del_to_client(
+ mac, old_bgp_ready, new_bgp_ready);
+ }
+
+ /* re-install the entry in the kernel */
+ zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ return 0;
+ }
+
+ /* Update all the neigh entries associated with this mac */
+ zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac);
+
+ /* Remove MAC from BGP. */
+ zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags,
+ false /* force */);
+
+ zebra_evpn_es_mac_deref_entry(mac);
+
+ /*
+ * If there are no neigh associated with the mac delete the mac
+ * else mark it as AUTO for forward reference
+ */
+ if (!listcount(mac->neigh_list)) {
+ zebra_evpn_mac_del(zevpn, mac);
+ } else {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ }
+
+ return 0;
+}
+
+int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip, zebra_mac_t **macp,
+ struct ethaddr *macaddr, vlanid_t vlan_id)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ zebra_mac_t *mac;
+
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!mac) {
+ mac = zebra_evpn_mac_add(zevpn, macaddr);
+ if (!mac) {
+ flog_err(EC_ZEBRA_MAC_ADD_FAILED,
+ "Failed to add MAC %s intf %s(%u) VID %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, vlan_id);
+ return -1;
+ }
+ }
+
+ /* Set "local" forwarding info. */
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ mac->fwd_info.local.ifindex = ifp->ifindex;
+ mac->fwd_info.local.vid = vlan_id;
+
+ *macp = mac;
+
+ return 0;
+}
diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h
new file mode 100644
index 0000000000..39aaf1fb30
--- /dev/null
+++ b/zebra/zebra_evpn_mac.h
@@ -0,0 +1,263 @@
+/*
+ * Zebra EVPN MAC Data structures and definitions
+ * These are "internal" to this function.
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ * Copyright (C) 2020 Volta Networks.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; 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_EVPN_MAC_H
+#define _ZEBRA_EVPN_MAC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct zebra_mac_t_ zebra_mac_t;
+
+struct host_rb_entry {
+ RB_ENTRY(host_rb_entry) hl_entry;
+
+ struct prefix p;
+};
+
+RB_HEAD(host_rb_tree_entry, host_rb_entry);
+RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry,
+ host_rb_entry_compare);
+/*
+ * MAC hash table.
+ *
+ * This table contains the MAC addresses pertaining to this VNI.
+ * This includes local MACs learnt on an attached VLAN that maps
+ * to this VNI as well as remote MACs learnt and installed by BGP.
+ * Local MACs will be known either on a VLAN sub-interface or
+ * on (port, VLAN); however, it is sufficient for zebra to maintain
+ * against the VNI i.e., it does not need to retain the local "port"
+ * information. The correct VNI will be obtained as zebra maintains
+ * the mapping (of VLAN to VNI).
+ */
+struct zebra_mac_t_ {
+ /* MAC address. */
+ struct ethaddr macaddr;
+
+ uint32_t flags;
+#define ZEBRA_MAC_LOCAL 0x01
+#define ZEBRA_MAC_REMOTE 0x02
+#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */
+#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */
+#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */
+#define ZEBRA_MAC_DEF_GW 0x20
+/* remote VTEP advertised MAC as default GW */
+#define ZEBRA_MAC_REMOTE_DEF_GW 0x40
+#define ZEBRA_MAC_DUPLICATE 0x80
+#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */
+/* MAC is locally active on an ethernet segment peer */
+#define ZEBRA_MAC_ES_PEER_ACTIVE 0x200
+/* MAC has been proxy-advertised by peers. This means we need to
+ * keep the entry for forwarding but cannot advertise it
+ */
+#define ZEBRA_MAC_ES_PEER_PROXY 0x400
+/* We have not been able to independently establish that the host is
+ * local connected but one or more ES peers claims it is.
+ * We will maintain the entry for forwarding purposes and continue
+ * to advertise it as locally attached but with a "proxy" flag
+ */
+#define ZEBRA_MAC_LOCAL_INACTIVE 0x800
+
+#define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL | ZEBRA_MAC_LOCAL_INACTIVE)
+#define ZEBRA_MAC_ALL_PEER_FLAGS \
+ (ZEBRA_MAC_ES_PEER_PROXY | ZEBRA_MAC_ES_PEER_ACTIVE)
+
+ /* back pointer to zevpn */
+ zebra_evpn_t *zevpn;
+
+ /* Local or remote info. */
+ union {
+ struct {
+ ifindex_t ifindex;
+ vlanid_t vid;
+ } local;
+
+ struct in_addr r_vtep_ip;
+ } fwd_info;
+
+ /* Local or remote ES */
+ struct zebra_evpn_es *es;
+ /* memory used to link the mac to the es */
+ struct listnode es_listnode;
+
+ /* Mobility sequence numbers associated with this entry. */
+ uint32_t rem_seq;
+ uint32_t loc_seq;
+
+ /* List of neigh associated with this mac */
+ struct list *neigh_list;
+
+ /* list of hosts pointing to this remote RMAC */
+ struct host_rb_tree_entry host_rb;
+
+ /* Duplicate mac detection */
+ uint32_t dad_count;
+
+ struct thread *dad_mac_auto_recovery_timer;
+
+ struct timeval detect_start_time;
+
+ time_t dad_dup_detect_time;
+
+ /* used for ageing out the PEER_ACTIVE flag */
+ struct thread *hold_timer;
+
+ /* number of neigh entries (using this mac) that have
+ * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY
+ */
+ uint32_t sync_neigh_cnt;
+};
+
+/*
+ * Context for MAC hash walk - used by callbacks.
+ */
+struct mac_walk_ctx {
+ zebra_evpn_t *zevpn; /* EVPN hash */
+ struct zebra_vrf *zvrf; /* VRF - for client notification. */
+ int uninstall; /* uninstall from kernel? */
+ int upd_client; /* uninstall from client? */
+
+ uint32_t flags;
+#define DEL_LOCAL_MAC 0x1
+#define DEL_REMOTE_MAC 0x2
+#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC)
+#define DEL_REMOTE_MAC_FROM_VTEP 0x4
+#define SHOW_REMOTE_MAC_FROM_VTEP 0x8
+
+ struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */
+
+ struct vty *vty; /* Used by VTY handlers */
+ uint32_t count; /* Used by VTY handlers */
+ struct json_object *json; /* Used for JSON Output */
+ bool print_dup; /* Used to print dup addr list */
+};
+
+struct rmac_walk_ctx {
+ struct vty *vty;
+ struct json_object *json;
+};
+
+/* temporary datastruct to pass info between the mac-update and
+ * neigh-update while handling mac-ip routes
+ */
+struct sync_mac_ip_ctx {
+ bool ignore_macip;
+ bool mac_created;
+ bool mac_inactive;
+ bool mac_dp_update_deferred;
+ zebra_mac_t *mac;
+};
+
+/**************************** SYNC MAC handling *****************************/
+/**************************** SYNC MAC handling *****************************/
+/* if the mac has been added of a mac-route from the peer
+ * or if it is being referenced by a neigh added by the
+ * peer we cannot let it age out i.e. we set the static bit
+ * in the dataplane
+ */
+static inline bool zebra_evpn_mac_is_static(zebra_mac_t *mac)
+{
+ return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || mac->sync_neigh_cnt);
+}
+
+/* mac needs to be locally active or active on an ES peer */
+static inline bool zebra_evpn_mac_is_ready_for_bgp(uint32_t flags)
+{
+ return (flags & ZEBRA_MAC_LOCAL)
+ && (!(flags & ZEBRA_MAC_LOCAL_INACTIVE)
+ || (flags & ZEBRA_MAC_ES_PEER_ACTIVE));
+}
+
+void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac);
+
+static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac)
+{
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS);
+ zebra_evpn_mac_stop_hold_timer(mac);
+}
+
+struct hash *zebra_mac_db_create(const char *desc);
+uint32_t num_valid_macs(zebra_evpn_t *zevi);
+uint32_t num_dup_detected_macs(zebra_evpn_t *zevi);
+int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac);
+int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac,
+ bool was_static);
+void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac);
+zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi, struct ethaddr *mac);
+zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi, struct ethaddr *macaddr);
+int zebra_evpn_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac);
+int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr,
+ struct ipaddr *ip, uint8_t flags,
+ uint32_t seq, int state,
+ struct zebra_evpn_es *es, uint16_t cmd);
+void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
+void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
+ bool force_clear_static,
+ const char *caller);
+void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready,
+ bool new_bgp_ready);
+
+void zebra_evpn_mac_del_all(zebra_evpn_t *zevi, int uninstall, int upd_client,
+ uint32_t flags);
+int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
+ uint32_t mac_flags, uint32_t seq,
+ struct zebra_evpn_es *es);
+int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
+ uint32_t flags, bool force);
+void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevi);
+zebra_mac_t *
+zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevi, struct ethaddr *macaddr,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, esi_t *esi,
+ struct sync_mac_ip_ctx *ctx);
+void zebra_evpn_sync_mac_del(zebra_mac_t *mac);
+void zebra_evpn_rem_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac);
+void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket,
+ void *ctxt);
+int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ struct ethaddr *macaddr, uint16_t ipa_len,
+ struct ipaddr *ipaddr, zebra_mac_t **macp,
+ struct in_addr vtep_ip, uint8_t flags,
+ uint32_t seq, esi_t *esi);
+
+int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
+ struct interface *ifp,
+ struct ethaddr *macaddr, vlanid_t vid,
+ bool sticky, bool local_inactive,
+ bool dp_static);
+int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
+ struct interface *ifp);
+int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip, zebra_mac_t **macp,
+ struct ethaddr *macaddr, vlanid_t vlan_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_ZEBRA_EVPN_MAC_H */
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
new file mode 100644
index 0000000000..029480eb4a
--- /dev/null
+++ b/zebra/zebra_evpn_mh.c
@@ -0,0 +1,2147 @@
+/*
+ * Zebra EVPN multihoming code
+ *
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "jhash.h"
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "stream.h"
+#include "table.h"
+#include "vlan.h"
+#include "vxlan.h"
+
+#include "zebra/zebra_router.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_l2.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mac.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_evpn_mh.h"
+#include "zebra/zebra_nhg.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZACC_BD, "Access Broadcast Domain");
+DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment");
+DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI");
+DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info");
+DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES");
+
+static void zebra_evpn_es_get_one_base_evpn(void);
+static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
+ zebra_evpn_t *zevpn, bool add);
+static void zebra_evpn_local_es_del(struct zebra_evpn_es *es);
+static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid,
+ struct ethaddr *sysmac);
+
+esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
+
+/*****************************************************************************/
+/* Ethernet Segment to EVI association -
+ * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI
+ * (zebra_evpn_t.es_evi_rb_tree).
+ * 2. Each local ES-EVI entry is sent to BGP which advertises it as an
+ * EAD-EVI (Type-1 EVPN) route
+ * 3. Local ES-EVI setup is re-evaluated on the following triggers -
+ * a. When an ESI is set or cleared on an access port.
+ * b. When an access port associated with an ESI is deleted.
+ * c. When VLAN member ship changes on an access port.
+ * d. When a VXLAN_IF is set or cleared on an access broadcast domain.
+ * e. When a L2-VNI is added or deleted for a VxLAN_IF.
+ * 4. Currently zebra doesn't remote ES-EVIs. Those are managed and maintained
+ * entirely in BGP which consolidates them into a remote ES. The remote ES
+ * is then sent to zebra which allocates a NHG for it.
+ */
+
+/* compare ES-IDs for the ES-EVI RB tree maintained per-EVPN */
+static int zebra_es_evi_rb_cmp(const struct zebra_evpn_es_evi *es_evi1,
+ const struct zebra_evpn_es_evi *es_evi2)
+{
+ return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES);
+}
+RB_GENERATE(zebra_es_evi_rb_head, zebra_evpn_es_evi,
+ rb_node, zebra_es_evi_rb_cmp);
+
+/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES
+ * tables.
+ */
+static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es,
+ zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi *es_evi;
+
+ es_evi = XCALLOC(MTYPE_ZES_EVI, sizeof(struct zebra_evpn_es_evi));
+
+ es_evi->es = es;
+ es_evi->zevpn = zevpn;
+
+ /* insert into the EVPN-ESI rb tree */
+ if (RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi)) {
+ XFREE(MTYPE_ZES_EVI, es_evi);
+ return NULL;
+ }
+
+ /* add to the ES's VNI list */
+ listnode_init(&es_evi->es_listnode, es_evi);
+ listnode_add(es->es_evi_list, &es_evi->es_listnode);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s evi %d new",
+ es_evi->es->esi_str, es_evi->zevpn->vni);
+
+ return es_evi;
+}
+
+/* returns TRUE if the EVPN is ready to be sent to BGP */
+static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn)
+{
+ return !!(zevpn->flags & ZEVPN_READY_FOR_BGP);
+}
+
+/* Evaluate if the es_evi is ready to be sent BGP -
+ * 1. If it is ready an add is sent to BGP
+ * 2. If it is not ready a del is sent (if the ES had been previously added
+ * to BGP).
+ */
+static void zebra_evpn_es_evi_re_eval_send_to_client(
+ struct zebra_evpn_es_evi *es_evi)
+{
+ bool old_ready;
+ bool new_ready;
+
+ old_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP);
+
+ /* ES and L2-VNI have to be individually ready for BGP */
+ if ((es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) &&
+ (es_evi->es->flags & ZEBRA_EVPNES_READY_FOR_BGP) &&
+ zebra_evpn_send_to_client_ok(es_evi->zevpn))
+ es_evi->flags |= ZEBRA_EVPNES_EVI_READY_FOR_BGP;
+ else
+ es_evi->flags &= ~ZEBRA_EVPNES_EVI_READY_FOR_BGP;
+
+ new_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP);
+
+ if (old_ready == new_ready)
+ return;
+
+ if (new_ready)
+ zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn,
+ true /* add */);
+ else
+ zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn,
+ false /* add */);
+}
+
+/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free
+ * up the memory.
+ */
+static void zebra_evpn_es_evi_free(struct zebra_evpn_es_evi *es_evi)
+{
+ struct zebra_evpn_es *es = es_evi->es;
+ zebra_evpn_t *zevpn = es_evi->zevpn;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s evi %d free",
+ es_evi->es->esi_str, es_evi->zevpn->vni);
+
+ /* remove from the ES's VNI list */
+ list_delete_node(es->es_evi_list, &es_evi->es_listnode);
+
+ /* remove from the VNI-ESI rb tree */
+ RB_REMOVE(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi);
+
+ /* remove from the VNI-ESI rb tree */
+ XFREE(MTYPE_ZES_EVI, es_evi);
+}
+
+/* find the ES-EVI in the per-L2-VNI RB tree */
+static struct zebra_evpn_es_evi *zebra_evpn_es_evi_find(
+ struct zebra_evpn_es *es, zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi es_evi;
+
+ es_evi.es = es;
+
+ return RB_FIND(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, &es_evi);
+}
+
+/* Tell BGP about an ES-EVI deletion and then delete it */
+static void zebra_evpn_local_es_evi_do_del(struct zebra_evpn_es_evi *es_evi)
+{
+ if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL))
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("local es %s evi %d del",
+ es_evi->es->esi_str, es_evi->zevpn->vni);
+
+ if (es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP) {
+ /* send a del only if add was sent for it earlier */
+ zebra_evpn_es_evi_send_to_client(es_evi->es,
+ es_evi->zevpn, false /* add */);
+ }
+
+ /* delete it from the EVPN's local list */
+ list_delete_node(es_evi->zevpn->local_es_evi_list,
+ &es_evi->l2vni_listnode);
+
+ es_evi->flags &= ~ZEBRA_EVPNES_EVI_LOCAL;
+ zebra_evpn_es_evi_free(es_evi);
+}
+static void zebra_evpn_local_es_evi_del(struct zebra_evpn_es *es,
+ zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi *es_evi;
+
+ es_evi = zebra_evpn_es_evi_find(es, zevpn);
+ if (es_evi)
+ zebra_evpn_local_es_evi_do_del(es_evi);
+}
+
+/* Create an ES-EVI if it doesn't already exist and tell BGP */
+static void zebra_evpn_local_es_evi_add(struct zebra_evpn_es *es,
+ zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi *es_evi;
+
+ es_evi = zebra_evpn_es_evi_find(es, zevpn);
+ if (!es_evi) {
+ es_evi = zebra_evpn_es_evi_new(es, zevpn);
+ if (!es_evi)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("local es %s evi %d add",
+ es_evi->es->esi_str, es_evi->zevpn->vni);
+ es_evi->flags |= ZEBRA_EVPNES_EVI_LOCAL;
+ /* add to the EVPN's local list */
+ listnode_init(&es_evi->l2vni_listnode, es_evi);
+ listnode_add(zevpn->local_es_evi_list, &es_evi->l2vni_listnode);
+
+ zebra_evpn_es_evi_re_eval_send_to_client(es_evi);
+ }
+}
+
+static void zebra_evpn_es_evi_show_entry(struct vty *vty,
+ struct zebra_evpn_es_evi *es_evi, json_object *json)
+{
+ char type_str[4];
+
+ if (json) {
+ /* XXX */
+ } else {
+ type_str[0] = '\0';
+ if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+
+ vty_out(vty, "%-8d %-30s %-4s\n",
+ es_evi->zevpn->vni, es_evi->es->esi_str,
+ type_str);
+ }
+}
+
+static void zebra_evpn_es_evi_show_entry_detail(struct vty *vty,
+ struct zebra_evpn_es_evi *es_evi, json_object *json)
+{
+ char type_str[4];
+
+ if (json) {
+ /* XXX */
+ } else {
+ type_str[0] = '\0';
+ if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+
+ vty_out(vty, "VNI %d ESI: %s\n",
+ es_evi->zevpn->vni, es_evi->es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " Ready for BGP: %s\n",
+ (es_evi->flags &
+ ZEBRA_EVPNES_EVI_READY_FOR_BGP) ?
+ "yes" : "no");
+ vty_out(vty, "\n");
+ }
+}
+
+static void zebra_evpn_es_evi_show_one_evpn(zebra_evpn_t *zevpn,
+ struct vty *vty, json_object *json, int detail)
+{
+ struct zebra_evpn_es_evi *es_evi;
+
+ RB_FOREACH(es_evi, zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree) {
+ if (detail)
+ zebra_evpn_es_evi_show_entry_detail(vty, es_evi, json);
+ else
+ zebra_evpn_es_evi_show_entry(vty, es_evi, json);
+ }
+}
+
+struct evpn_mh_show_ctx {
+ struct vty *vty;
+ json_object *json;
+ int detail;
+};
+
+static void zebra_evpn_es_evi_show_one_evpn_hash_cb(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data;
+ struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt;
+
+ zebra_evpn_es_evi_show_one_evpn(zevpn, wctx->vty,
+ wctx->json, wctx->detail);
+}
+
+void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail)
+{
+ json_object *json = NULL;
+ struct zebra_vrf *zvrf;
+ struct evpn_mh_show_ctx wctx;
+
+ zvrf = zebra_vrf_get_evpn();
+
+ memset(&wctx, 0, sizeof(wctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ wctx.detail = detail;
+
+ if (!detail && !json) {
+ vty_out(vty, "Type: L local, R remote\n");
+ vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type");
+ }
+ /* Display all L2-VNIs */
+ hash_iterate(zvrf->evpn_table, zebra_evpn_es_evi_show_one_evpn_hash_cb,
+ &wctx);
+}
+
+void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail)
+{
+ json_object *json = NULL;
+ zebra_evpn_t *zevpn;
+
+ zevpn = zebra_evpn_lookup(vni);
+ if (zevpn) {
+ if (!detail && !json) {
+ vty_out(vty, "Type: L local, R remote\n");
+ vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type");
+ }
+ } else {
+ if (!uj)
+ vty_out(vty, "VNI %d doesn't exist\n", vni);
+ }
+ zebra_evpn_es_evi_show_one_evpn(zevpn, vty, json, detail);
+}
+
+/* Initialize the ES tables maintained per-L2_VNI */
+void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn)
+{
+ /* Initialize the ES-EVI RB tree */
+ RB_INIT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree);
+
+ /* Initialize the local and remote ES lists maintained for quick
+ * walks by type
+ */
+ zevpn->local_es_evi_list = list_new();
+ listset_app_node_mem(zevpn->local_es_evi_list);
+}
+
+/* Cleanup the ES info maintained per- EVPN */
+void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi *es_evi;
+ struct zebra_evpn_es_evi *es_evi_next;
+
+ RB_FOREACH_SAFE(es_evi, zebra_es_evi_rb_head,
+ &zevpn->es_evi_rb_tree, es_evi_next) {
+ zebra_evpn_local_es_evi_do_del(es_evi);
+ }
+
+ list_delete(&zevpn->local_es_evi_list);
+ zebra_evpn_es_clear_base_evpn(zevpn);
+}
+
+/* called when the oper state or bridge membership changes for the
+ * vxlan device
+ */
+void zebra_evpn_update_all_es(zebra_evpn_t *zevpn)
+{
+ struct zebra_evpn_es_evi *es_evi;
+ struct listnode *node;
+
+ /* the EVPN is now elgible as a base for EVPN-MH */
+ if (zebra_evpn_send_to_client_ok(zevpn))
+ zebra_evpn_es_set_base_evpn(zevpn);
+ else
+ zebra_evpn_es_clear_base_evpn(zevpn);
+
+ for (ALL_LIST_ELEMENTS_RO(zevpn->local_es_evi_list, node, es_evi))
+ zebra_evpn_es_evi_re_eval_send_to_client(es_evi);
+}
+
+/*****************************************************************************/
+/* Access broadcast domains (BD)
+ * 1. These broadcast domains can be VLAN aware (in which case
+ * the key is VID) or VLAN unaware (in which case the key is
+ * 2. A VID-BD is created when a VLAN is associated with an access port or
+ * when the VLAN is associated with VXLAN_IF
+ * 3. A BD is translated into ES-EVI entries when a VNI is associated
+ * with the broadcast domain
+ */
+/* Hash key for VLAN based broadcast domains */
+static unsigned int zebra_evpn_acc_vl_hash_keymake(const void *p)
+{
+ const struct zebra_evpn_access_bd *acc_bd = p;
+
+ return jhash_1word(acc_bd->vid, 0);
+}
+
+/* Compare two VLAN based broadcast domains */
+static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2)
+{
+ const struct zebra_evpn_access_bd *acc_bd1 = p1;
+ const struct zebra_evpn_access_bd *acc_bd2 = p2;
+
+ if (acc_bd1 == NULL && acc_bd2 == NULL)
+ return true;
+
+ if (acc_bd1 == NULL || acc_bd2 == NULL)
+ return false;
+
+ return (acc_bd1->vid == acc_bd2->vid);
+}
+
+/* Lookup VLAN based broadcast domain */
+static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+ struct zebra_evpn_access_bd tmp;
+
+ tmp.vid = vid;
+ acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp);
+
+ return acc_bd;
+}
+
+/* A new broadcast domain can be created when a VLAN member or VLAN<=>VxLAN_IF
+ * mapping is added.
+ */
+static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_new(vlanid_t vid)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d add", vid);
+
+ acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd));
+
+ acc_bd->vid = vid;
+
+ /* Initialize the mbr list */
+ acc_bd->mbr_zifs = list_new();
+
+ /* Add to hash */
+ if (!hash_get(zmh_info->evpn_vlan_table, acc_bd, hash_alloc_intern)) {
+ XFREE(MTYPE_ZACC_BD, acc_bd);
+ return NULL;
+ }
+
+ return acc_bd;
+}
+
+/* Free VLAN based broadcast domain -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+static void zebra_evpn_acc_vl_free(struct zebra_evpn_access_bd *acc_bd)
+{
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d del", acc_bd->vid);
+
+ /* cleanup resources maintained against the ES */
+ list_delete(&acc_bd->mbr_zifs);
+
+ /* remove EVI from various tables */
+ hash_release(zmh_info->evpn_vlan_table, acc_bd);
+
+ XFREE(MTYPE_ZACC_BD, acc_bd);
+}
+
+static void zebra_evpn_acc_vl_cleanup_all(struct hash_bucket *bucket, void *arg)
+{
+ struct zebra_evpn_access_bd *acc_bd = bucket->data;
+
+ zebra_evpn_acc_vl_free(acc_bd);
+}
+
+/* called when a bd mbr is removed or VxLAN_IF is diassociated from the access
+ * VLAN
+ */
+static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd)
+{
+ if (!list_isempty(acc_bd->mbr_zifs) || acc_bd->vxlan_zif)
+ return;
+
+ /* if there are no references free the EVI */
+ zebra_evpn_acc_vl_free(acc_bd);
+}
+
+/* called when a EVPN-L2VNI is set or cleared against a BD */
+static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd,
+ zebra_evpn_t *zevpn, zebra_evpn_t *old_zevpn)
+{
+ struct zebra_if *zif;
+ struct listnode *node;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d l2-vni %u set",
+ acc_bd->vid, zevpn ? zevpn->vni : 0);
+
+ for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) {
+ if (!zif->es_info.es)
+ continue;
+
+ if (zevpn)
+ zebra_evpn_local_es_evi_add(zif->es_info.es, zevpn);
+ else if (old_zevpn)
+ zebra_evpn_local_es_evi_del(zif->es_info.es, old_zevpn);
+ }
+}
+
+/* handle VLAN->VxLAN_IF association */
+void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+ struct zebra_if *old_vxlan_zif;
+ zebra_evpn_t *old_zevpn;
+
+ if (!vid)
+ return;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (!acc_bd)
+ acc_bd = zebra_evpn_acc_vl_new(vid);
+
+ old_vxlan_zif = acc_bd->vxlan_zif;
+ acc_bd->vxlan_zif = vxlan_zif;
+ if (vxlan_zif == old_vxlan_zif)
+ return;
+
+ old_zevpn = acc_bd->zevpn;
+ acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni);
+ if (acc_bd->zevpn == old_zevpn)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d vni %u ref",
+ acc_bd->vid, vxlan_zif->l2info.vxl.vni);
+
+ if (old_zevpn)
+ zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn);
+
+ if (acc_bd->zevpn)
+ zebra_evpn_acc_bd_evpn_set(acc_bd, acc_bd->zevpn, NULL);
+}
+
+/* handle VLAN->VxLAN_IF deref */
+void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+
+ if (!vid)
+ return;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (!acc_bd)
+ return;
+
+ /* clear vxlan_if only if it matches */
+ if (acc_bd->vxlan_zif != vxlan_zif)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d vni %u deref",
+ acc_bd->vid, vxlan_zif->l2info.vxl.vni);
+
+ if (acc_bd->zevpn)
+ zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn);
+
+ acc_bd->zevpn = NULL;
+ acc_bd->vxlan_zif = NULL;
+
+ /* if there are no other references the access_bd can be freed */
+ zebra_evpn_acc_bd_free_on_deref(acc_bd);
+}
+
+/* handle EVPN add/del */
+void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn,
+ bool set)
+{
+ struct zebra_l2info_vxlan *vxl;
+ struct zebra_evpn_access_bd *acc_bd;
+
+ if (!zif)
+ return;
+
+ /* locate access_bd associated with the vxlan device */
+ vxl = &zif->l2info.vxl;
+ acc_bd = zebra_evpn_acc_vl_find(vxl->access_vlan);
+ if (!acc_bd)
+ return;
+
+ if (set) {
+ zebra_evpn_es_set_base_evpn(zevpn);
+ if (acc_bd->zevpn != zevpn) {
+ acc_bd->zevpn = zevpn;
+ zebra_evpn_acc_bd_evpn_set(acc_bd, zevpn, NULL);
+ }
+ } else {
+ if (acc_bd->zevpn) {
+ zebra_evpn_t *old_zevpn = acc_bd->zevpn;
+ acc_bd->zevpn = NULL;
+ zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn);
+ }
+ }
+}
+
+/* handle addition of new VLAN members */
+void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+
+ if (!vid)
+ return;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (!acc_bd)
+ acc_bd = zebra_evpn_acc_vl_new(vid);
+
+ if (listnode_lookup(acc_bd->mbr_zifs, zif))
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d mbr %s ref",
+ vid, zif->ifp->name);
+
+ listnode_add(acc_bd->mbr_zifs, zif);
+ if (acc_bd->zevpn && zif->es_info.es)
+ zebra_evpn_local_es_evi_add(zif->es_info.es, acc_bd->zevpn);
+}
+
+/* handle deletion of VLAN members */
+void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif)
+{
+ struct zebra_evpn_access_bd *acc_bd;
+ struct listnode *node;
+
+ if (!vid)
+ return;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (!acc_bd)
+ return;
+
+ node = listnode_lookup(acc_bd->mbr_zifs, zif);
+ if (!node)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("access vlan %d mbr %s deref",
+ vid, zif->ifp->name);
+
+ list_delete_node(acc_bd->mbr_zifs, node);
+
+ if (acc_bd->zevpn && zif->es_info.es)
+ zebra_evpn_local_es_evi_del(zif->es_info.es, acc_bd->zevpn);
+
+ /* if there are no other references the access_bd can be freed */
+ zebra_evpn_acc_bd_free_on_deref(acc_bd);
+}
+
+static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty,
+ struct zebra_evpn_access_bd *acc_bd, json_object *json)
+{
+ struct zebra_if *zif;
+ struct listnode *node;
+
+ if (json) {
+ /* XXX */
+ } else {
+ vty_out(vty, "VLAN: %u\n", acc_bd->vid);
+ vty_out(vty, " VxLAN Interface: %s\n",
+ acc_bd->vxlan_zif ?
+ acc_bd->vxlan_zif->ifp->name : "-");
+ vty_out(vty, " L2-VNI: %d\n",
+ acc_bd->zevpn ? acc_bd->zevpn->vni : 0);
+ vty_out(vty, " Member Count: %d\n",
+ listcount(acc_bd->mbr_zifs));
+ vty_out(vty, " Members: \n");
+ for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif))
+ vty_out(vty, " %s\n", zif->ifp->name);
+ vty_out(vty, "\n");
+ }
+}
+
+static void zebra_evpn_acc_vl_show_entry(struct vty *vty,
+ struct zebra_evpn_access_bd *acc_bd, json_object *json)
+{
+ if (!json)
+ vty_out(vty, "%-5u %21s %-8d %u\n",
+ acc_bd->vid,
+ acc_bd->vxlan_zif ?
+ acc_bd->vxlan_zif->ifp->name : "-",
+ acc_bd->zevpn ? acc_bd->zevpn->vni : 0,
+ listcount(acc_bd->mbr_zifs));
+}
+
+static void zebra_evpn_acc_vl_show_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ struct evpn_mh_show_ctx *wctx = ctxt;
+ struct zebra_evpn_access_bd *acc_bd = bucket->data;
+
+ if (wctx->detail)
+ zebra_evpn_acc_vl_show_entry_detail(wctx->vty,
+ acc_bd, wctx->json);
+ else
+ zebra_evpn_acc_vl_show_entry(wctx->vty,
+ acc_bd, wctx->json);
+}
+
+void zebra_evpn_acc_vl_show(struct vty *vty, bool uj)
+{
+ json_object *json = NULL;
+ struct evpn_mh_show_ctx wctx;
+
+ memset(&wctx, 0, sizeof(wctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ wctx.detail = false;
+
+ if (!json)
+ vty_out(vty, "%-5s %21s %-8s %s\n",
+ "VLAN", "VxLAN-IF", "L2-VNI", "# Members");
+
+ hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash,
+ &wctx);
+}
+
+void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj)
+{
+ json_object *json = NULL;
+ struct evpn_mh_show_ctx wctx;
+
+ memset(&wctx, 0, sizeof(wctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ wctx.detail = true;
+
+ hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash,
+ &wctx);
+}
+
+void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid)
+{
+ json_object *json = NULL;
+ struct zebra_evpn_access_bd *acc_bd;
+
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (!acc_bd) {
+ if (!json) {
+ vty_out(vty, "VLAN %u not present\n", vid);
+ return;
+ }
+ }
+ zebra_evpn_acc_vl_show_entry_detail(vty, acc_bd, json);
+}
+
+/* Initialize VLAN member bitmap on an interface. Although VLAN membership
+ * is independent of EVPN we only process it if its of interest to EVPN-MH
+ * i.e. on access ports that can be setup as Ethernet Segments. And that is
+ * intended as an optimization.
+ */
+void zebra_evpn_if_init(struct zebra_if *zif)
+{
+ if (!zebra_evpn_is_if_es_capable(zif))
+ return;
+
+ if (!bf_is_inited(zif->vlan_bitmap))
+ bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX);
+
+ /* if an es_id and sysmac are already present against the interface
+ * activate it
+ */
+ zebra_evpn_local_es_update(zif, zif->es_info.lid, &zif->es_info.sysmac);
+}
+
+/* handle deletion of an access port by removing it from all associated
+ * broadcast domains.
+ */
+void zebra_evpn_if_cleanup(struct zebra_if *zif)
+{
+ vlanid_t vid;
+
+ if (!bf_is_inited(zif->vlan_bitmap))
+ return;
+
+ bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) {
+ zebra_evpn_vl_mbr_deref(vid, zif);
+ }
+
+ bf_free(zif->vlan_bitmap);
+
+ /* Delete associated Ethernet Segment */
+ if (zif->es_info.es)
+ zebra_evpn_local_es_del(zif->es_info.es);
+}
+
+/*****************************************************************************
+ * L2 NH/NHG Management
+ * A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This
+ * NH is then added to the L2-ECMP-NHG associated with the ES.
+ */
+static uint32_t zebra_evpn_nhid_alloc(bool is_nhg)
+{
+ uint32_t id;
+ int type;
+
+ bf_assign_index(zmh_info->nh_id_bitmap, id);
+
+ if (!id)
+ return 0;
+
+ type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT;
+ return (id | type);
+}
+
+static void zebra_evpn_nhid_free(uint32_t nh_id)
+{
+ uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK);
+
+ if (!id)
+ return;
+
+ bf_release_index(zmh_info->nh_id_bitmap, id);
+}
+
+/* The MAC ECMP group is activated on the first VTEP */
+static void zebra_evpn_nhg_update(struct zebra_evpn_es *es)
+{
+ uint32_t nh_cnt = 0;
+ struct nh_grp nh_ids[ES_VTEP_MAX_CNT];
+ struct zebra_evpn_es_vtep *es_vtep;
+ struct listnode *node;
+
+ if (!es->nhg_id)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (!es_vtep->nh_id)
+ continue;
+
+ if (nh_cnt >= ES_VTEP_MAX_CNT)
+ break;
+
+ memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp));
+ nh_ids[nh_cnt].id = es_vtep->nh_id;
+ ++nh_cnt;
+ }
+
+ if (nh_cnt) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH) {
+ char nh_str[ES_VTEP_LIST_STR_SZ];
+ uint32_t i;
+ char nh_buf[16];
+
+ nh_str[0] = '\0';
+ for (i = 0; i < nh_cnt; ++i) {
+ snprintf(nh_buf, sizeof(nh_buf), "%u ",
+ nh_ids[i].id);
+ strlcat(nh_str, nh_buf, sizeof(nh_str));
+ }
+ zlog_debug("es %s nhg 0x%x add %s",
+ es->esi_str, es->nhg_id, nh_str);
+ }
+
+ es->flags |= ZEBRA_EVPNES_NHG_ACTIVE;
+ kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids);
+ } else {
+ if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
+ zlog_debug("es %s nhg 0x%x del",
+ es->esi_str, es->nhg_id);
+ es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE;
+ kernel_del_mac_nhg(es->nhg_id);
+ }
+ }
+
+ /* XXX - update remote macs associated with the ES */
+}
+
+static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep)
+{
+ if (es_vtep->nh_id)
+ return;
+
+ es_vtep->nh_id = zebra_evpn_nhid_alloc(false);
+
+ if (!es_vtep->nh_id)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
+ zlog_debug("es %s vtep %s nh 0x%x add",
+ es_vtep->es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id);
+ /* install the NH */
+ kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip);
+ /* add the NH to the parent NHG */
+ zebra_evpn_nhg_update(es_vtep->es);
+}
+
+static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep)
+{
+ uint32_t nh_id;
+
+ if (!es_vtep->nh_id)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NH)
+ zlog_debug("es %s vtep %s nh 0x%x del",
+ es_vtep->es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id);
+
+ nh_id = es_vtep->nh_id;
+ es_vtep->nh_id = 0;
+
+ /* remove the NH from the parent NHG */
+ zebra_evpn_nhg_update(es_vtep->es);
+ /* uninstall the NH */
+ kernel_del_mac_nh(nh_id);
+ zebra_evpn_nhid_free(nh_id);
+
+}
+
+/*****************************************************************************/
+/* Ethernet Segment Management
+ * 1. Ethernet Segment is a collection of links attached to the same
+ * server (MHD) or switch (MHN)
+ * 2. An Ethernet Segment can span multiple PEs and is identified by the
+ * 10-byte ES-ID.
+ * 3. Zebra manages the local ESI configuration.
+ * 4. It also maintains the aliasing that maps an ESI (local or remote)
+ * to one or more PEs/VTEPs.
+ * 5. remote ESs are added by BGP (on rxing EAD Type-1 routes)
+ */
+/* A list of remote VTEPs is maintained for each ES. This list includes -
+ * 1. VTEPs for which we have imported the ESR i.e. ES-peers
+ * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI
+ * have been imported into one or more EVPNs
+ */
+static int zebra_evpn_es_vtep_cmp(void *p1, void *p2)
+{
+ const struct zebra_evpn_es_vtep *es_vtep1 = p1;
+ const struct zebra_evpn_es_vtep *es_vtep2 = p2;
+
+ return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr;
+}
+
+static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_new(
+ struct zebra_evpn_es *es, struct in_addr vtep_ip)
+{
+ struct zebra_evpn_es_vtep *es_vtep;
+
+ es_vtep = XCALLOC(MTYPE_ZES_VTEP, sizeof(*es_vtep));
+
+ es_vtep->es = es;
+ es_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ listnode_init(&es_vtep->es_listnode, es_vtep);
+ listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode);
+
+ return es_vtep;
+}
+
+static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep)
+{
+ struct zebra_evpn_es *es = es_vtep->es;
+
+ list_delete_node(es->es_vtep_list, &es_vtep->es_listnode);
+ /* update the L2-NHG associated with the ES */
+ zebra_evpn_nh_del(es_vtep);
+ XFREE(MTYPE_ZES_VTEP, es_vtep);
+}
+
+
+/* check if VTEP is already part of the list */
+static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_find(
+ struct zebra_evpn_es *es, struct in_addr vtep_ip)
+{
+ struct listnode *node = NULL;
+ struct zebra_evpn_es_vtep *es_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr)
+ return es_vtep;
+ }
+ return NULL;
+}
+
+static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct zebra_evpn_es_vtep *es_vtep;
+
+ es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip);
+
+ if (!es_vtep) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s vtep %s add",
+ es->esi_str, inet_ntoa(vtep_ip));
+ es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip);
+ /* update the L2-NHG associated with the ES */
+ zebra_evpn_nh_add(es_vtep);
+ }
+}
+
+static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct zebra_evpn_es_vtep *es_vtep;
+
+ es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip);
+
+ if (es_vtep) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s vtep %s del",
+ es->esi_str, inet_ntoa(vtep_ip));
+ zebra_evpn_es_vtep_free(es_vtep);
+ }
+}
+
+/* compare ES-IDs for the global ES RB tree */
+static int zebra_es_rb_cmp(const struct zebra_evpn_es *es1,
+ const struct zebra_evpn_es *es2)
+{
+ return memcmp(&es1->esi, &es2->esi, ESI_BYTES);
+}
+RB_GENERATE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);
+
+/* Lookup ES */
+struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi)
+{
+ struct zebra_evpn_es tmp;
+
+ memcpy(&tmp.esi, esi, sizeof(esi_t));
+ return RB_FIND(zebra_es_rb_head, &zmh_info->es_rb_tree, &tmp);
+}
+
+/* A new local es is created when a local-es-id and sysmac is configured
+ * against an interface.
+ */
+static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi)
+{
+ struct zebra_evpn_es *es;
+
+ es = XCALLOC(MTYPE_ZES, sizeof(struct zebra_evpn_es));
+
+ /* fill in ESI */
+ memcpy(&es->esi, esi, sizeof(esi_t));
+ esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str));
+
+ /* Add to rb_tree */
+ if (RB_INSERT(zebra_es_rb_head, &zmh_info->es_rb_tree, es)) {
+ XFREE(MTYPE_ZES, es);
+ return NULL;
+ }
+
+ /* Initialise the ES-EVI list */
+ es->es_evi_list = list_new();
+ listset_app_node_mem(es->es_evi_list);
+
+ /* Initialise the VTEP list */
+ es->es_vtep_list = list_new();
+ listset_app_node_mem(es->es_vtep_list);
+ es->es_vtep_list->cmp = zebra_evpn_es_vtep_cmp;
+
+ /* mac entries associated with the ES */
+ es->mac_list = list_new();
+ listset_app_node_mem(es->mac_list);
+
+ /* reserve a NHG */
+ es->nhg_id = zebra_evpn_nhid_alloc(true);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s nhg 0x%x new", es->esi_str, es->nhg_id);
+
+ return es;
+}
+
+/* Free a given ES -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+static struct zebra_evpn_es *zebra_evpn_es_free(struct zebra_evpn_es *es)
+{
+ /* If the ES has a local or remote reference it cannot be freed.
+ * Free is also prevented if there are MAC entries referencing
+ * it.
+ */
+ if ((es->flags & (ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_REMOTE)) ||
+ listcount(es->mac_list))
+ return es;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s free", es->esi_str);
+
+ /* If the NHG is still installed uninstall it and free the id */
+ if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) {
+ es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE;
+ kernel_del_mac_nhg(es->nhg_id);
+ }
+ zebra_evpn_nhid_free(es->nhg_id);
+
+ /* cleanup resources maintained against the ES */
+ list_delete(&es->es_evi_list);
+ list_delete(&es->es_vtep_list);
+ list_delete(&es->mac_list);
+
+ /* remove from the VNI-ESI rb tree */
+ RB_REMOVE(zebra_es_rb_head, &zmh_info->es_rb_tree, es);
+
+ XFREE(MTYPE_ZES, es);
+
+ return NULL;
+}
+
+/* Inform BGP about local ES addition */
+static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es)
+{
+ struct zserv *client;
+ struct stream *s;
+ uint8_t oper_up;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_LOCAL_ES_ADD, zebra_vrf_get_evpn_id());
+ stream_put(s, &es->esi, sizeof(esi_t));
+ stream_put_ipv4(s, zmh_info->es_originator_ip.s_addr);
+ oper_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP);
+ stream_putc(s, oper_up);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("send add local es %s %s to %s",
+ es->esi_str,
+ inet_ntoa(zmh_info->es_originator_ip),
+ zebra_route_string(client->proto));
+
+ client->local_es_add_cnt++;
+ return zserv_send_message(client, s);
+}
+
+/* Inform BGP about local ES deletion */
+static int zebra_evpn_es_send_del_to_client(struct zebra_evpn_es *es)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_LOCAL_ES_DEL, zebra_vrf_get_evpn_id());
+ stream_put(s, &es->esi, sizeof(esi_t));
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("send del local es %s to %s", es->esi_str,
+ zebra_route_string(client->proto));
+
+ client->local_es_del_cnt++;
+ return zserv_send_message(client, s);
+}
+
+/* XXX - call any time ZEBRA_EVPNES_LOCAL gets set or cleared */
+static void zebra_evpn_es_re_eval_send_to_client(struct zebra_evpn_es *es,
+ bool es_evi_re_reval)
+{
+ bool old_ready;
+ bool new_ready;
+ struct listnode *node;
+ struct zebra_evpn_es_evi *es_evi;
+
+ old_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP);
+
+ if ((es->flags & ZEBRA_EVPNES_LOCAL) &&
+ zmh_info->es_originator_ip.s_addr)
+ es->flags |= ZEBRA_EVPNES_READY_FOR_BGP;
+ else
+ es->flags &= ~ZEBRA_EVPNES_READY_FOR_BGP;
+
+ new_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP);
+ if (old_ready == new_ready)
+ return;
+
+ if (new_ready)
+ zebra_evpn_es_send_add_to_client(es);
+ else
+ zebra_evpn_es_send_del_to_client(es);
+
+ /* re-eval associated EVIs */
+ if (es_evi_re_reval) {
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, node, es_evi)) {
+ if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL))
+ continue;
+ zebra_evpn_es_evi_re_eval_send_to_client(es_evi);
+ }
+ }
+}
+
+void zebra_evpn_es_send_all_to_client(bool add)
+{
+ struct listnode *es_node;
+ struct listnode *evi_node;
+ struct zebra_evpn_es *es;
+ struct zebra_evpn_es_evi *es_evi;
+
+ if (!zmh_info)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, es_node, es)) {
+ if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) {
+ if (add)
+ zebra_evpn_es_send_add_to_client(es);
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list,
+ evi_node, es_evi)) {
+ if (!(es_evi->flags &
+ ZEBRA_EVPNES_EVI_READY_FOR_BGP))
+ continue;
+
+ if (add)
+ zebra_evpn_es_evi_send_to_client(
+ es, es_evi->zevpn,
+ true /* add */);
+ else
+ zebra_evpn_es_evi_send_to_client(
+ es, es_evi->zevpn,
+ false /* add */);
+ }
+ if (!add)
+ zebra_evpn_es_send_del_to_client(es);
+ }
+ }
+}
+
+/* walk the vlan bitmap associated with the zif and create or delete
+ * es_evis for all vlans associated with a VNI.
+ * XXX: This API is really expensive. optimize later if possible.
+ */
+static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es)
+{
+ struct zebra_if *zif = es->zif;
+ uint16_t vid;
+ struct zebra_evpn_access_bd *acc_bd;
+
+
+ bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) {
+ acc_bd = zebra_evpn_acc_vl_find(vid);
+ if (acc_bd->zevpn)
+ zebra_evpn_local_es_evi_add(es, acc_bd->zevpn);
+ }
+}
+
+static void zebra_evpn_es_local_mac_update(struct zebra_evpn_es *es,
+ bool force_clear_static)
+{
+ zebra_mac_t *mac;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) {
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) {
+ zebra_evpn_sync_mac_dp_install(
+ mac, false /* set_inactive */,
+ force_clear_static, __func__);
+ }
+ }
+}
+
+static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es,
+ struct zebra_if *zif)
+{
+ if (es->flags & ZEBRA_EVPNES_LOCAL)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("local es %s add; nhg 0x%x if %s",
+ es->esi_str, es->nhg_id, zif->ifp->name);
+
+ es->flags |= ZEBRA_EVPNES_LOCAL;
+ listnode_init(&es->local_es_listnode, es);
+ listnode_add(zmh_info->local_es_list, &es->local_es_listnode);
+
+ /* attach es to interface */
+ zif->es_info.es = es;
+
+ /* attach interface to es */
+ es->zif = zif;
+ if (if_is_operative(zif->ifp))
+ es->flags |= ZEBRA_EVPNES_OPER_UP;
+
+ /* setup base-vni if one doesn't already exist; the ES will get sent
+ * to BGP as a part of that process
+ */
+ if (!zmh_info->es_base_evpn)
+ zebra_evpn_es_get_one_base_evpn();
+ else
+ /* send notification to bgp */
+ zebra_evpn_es_re_eval_send_to_client(es,
+ false /* es_evi_re_reval */);
+
+ /* Setup ES-EVIs for all VxLAN stretched VLANs associated with
+ * the zif
+ */
+ zebra_evpn_es_setup_evis(es);
+ /* if there any local macs referring to the ES as dest we
+ * need to set the static reference on them if the MAC is
+ * synced from an ES peer
+ */
+ zebra_evpn_es_local_mac_update(es,
+ false /* force_clear_static */);
+}
+
+static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es *es)
+{
+ struct zebra_if *zif;
+
+ if (!(es->flags & ZEBRA_EVPNES_LOCAL))
+ return;
+
+ es->flags &= ~ZEBRA_EVPNES_LOCAL;
+ /* if there any local macs referring to the ES as dest we
+ * need to clear the static reference on them
+ */
+ zebra_evpn_es_local_mac_update(es,
+ true /* force_clear_static */);
+
+ /* clear the es from the parent interface */
+ zif = es->zif;
+ zif->es_info.es = NULL;
+ es->zif = NULL;
+
+ /* remove from the ES list */
+ list_delete_node(zmh_info->local_es_list, &es->local_es_listnode);
+
+ /* free up the ES if there is no remote reference */
+ zebra_evpn_es_free(es);
+}
+
+/* Delete an ethernet segment and inform BGP */
+static void zebra_evpn_local_es_del(struct zebra_evpn_es *es)
+{
+ struct zebra_evpn_es_evi *es_evi;
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
+ struct zebra_if *zif;
+
+ if (!CHECK_FLAG(es->flags, ZEBRA_EVPNES_LOCAL))
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES) {
+ zif = es->zif;
+ zlog_debug("local es %s del; nhg 0x%x if %s",
+ es->esi_str, es->nhg_id,
+ zif ? zif->ifp->name : "-");
+ }
+
+ /* remove all ES-EVIs associated with the ES */
+ for (ALL_LIST_ELEMENTS(es->es_evi_list, node, nnode, es_evi))
+ zebra_evpn_local_es_evi_do_del(es_evi);
+
+ /* send a del if the ES had been sent to BGP earlier */
+ if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
+ zebra_evpn_es_send_del_to_client(es);
+
+ zebra_evpn_es_local_info_clear(es);
+}
+
+/* eval remote info associated with the ES */
+static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es *es)
+{
+ /* if there are remote VTEPs the ES-EVI is classified as "remote" */
+ if (listcount(es->es_vtep_list)) {
+ if (!(es->flags & ZEBRA_EVPNES_REMOTE)) {
+ es->flags |= ZEBRA_EVPNES_REMOTE;
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("remote es %s add; nhg 0x%x",
+ es->esi_str, es->nhg_id);
+ }
+ } else {
+ if (es->flags & ZEBRA_EVPNES_REMOTE) {
+ es->flags &= ~ZEBRA_EVPNES_REMOTE;
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("remote es %s del; nhg 0x%x",
+ es->esi_str, es->nhg_id);
+ zebra_evpn_es_free(es);
+ }
+ }
+}
+
+/* A new local es is created when a local-es-id and sysmac is configured
+ * against an interface.
+ */
+static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid,
+ struct ethaddr *sysmac)
+{
+ struct zebra_evpn_es *old_es = zif->es_info.es;
+ struct zebra_evpn_es *es;
+ esi_t esi;
+ int offset = 0;
+ int field_bytes = 0;
+
+ /* Complete config of the ES-ID bootstraps the ES */
+ if (!lid || is_zero_mac(sysmac)) {
+ /* if in ES is attached to zif delete it */
+ if (old_es)
+ zebra_evpn_local_es_del(old_es);
+ return 0;
+ }
+
+ /* build 10-byte type-3-ESI -
+ * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes)
+ */
+ field_bytes = 1;
+ esi.val[offset] = ESI_TYPE_MAC;
+ offset += field_bytes;
+
+ field_bytes = ETH_ALEN;
+ memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes);
+ offset += field_bytes;
+
+ esi.val[offset++] = (uint8_t)(lid >> 16);
+ esi.val[offset++] = (uint8_t)(lid >> 8);
+ esi.val[offset++] = (uint8_t)lid;
+
+ if (old_es && !memcmp(&old_es->esi, &esi, sizeof(esi_t)))
+ /* dup - nothing to be done */
+ return 0;
+
+ /* release the old_es against the zif */
+ if (old_es)
+ zebra_evpn_local_es_del(old_es);
+
+ es = zebra_evpn_es_find(&esi);
+ if (es) {
+ /* if it exists against another interface flag an error */
+ if (es->zif && es->zif != zif)
+ return -1;
+ } else {
+ /* create new es */
+ es = zebra_evpn_es_new(&esi);
+ }
+
+ zebra_evpn_es_local_info_set(es, zif);
+
+ return 0;
+}
+
+static int zebra_evpn_remote_es_del(esi_t *esi, struct in_addr vtep_ip)
+{
+ char buf[ESI_STR_LEN];
+ struct zebra_evpn_es *es;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("remote es %s vtep %s del",
+ esi_to_str(esi, buf, sizeof(buf)),
+ inet_ntoa(vtep_ip));
+
+ es = zebra_evpn_es_find(esi);
+ if (!es) {
+ /* XXX - error log */
+ return -1;
+ }
+
+ zebra_evpn_es_vtep_del(es, vtep_ip);
+ zebra_evpn_es_remote_info_re_eval(es);
+
+ return 0;
+}
+
+/* force delete a remote ES on the way down */
+static void zebra_evpn_remote_es_flush(struct zebra_evpn_es *es)
+{
+ struct zebra_evpn_es_vtep *es_vtep;
+ struct listnode *node;
+ struct listnode *nnode;
+
+ for (ALL_LIST_ELEMENTS(es->es_vtep_list, node, nnode, es_vtep)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s vtep %s flush",
+ es->esi_str,
+ inet_ntoa(es_vtep->vtep_ip));
+ zebra_evpn_es_vtep_free(es_vtep);
+ zebra_evpn_es_remote_info_re_eval(es);
+ }
+}
+
+static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip)
+{
+ char buf[ESI_STR_LEN];
+ struct zebra_evpn_es *es;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("remote es %s vtep %s add",
+ esi_to_str(esi, buf, sizeof(buf)),
+ inet_ntoa(vtep_ip));
+
+ es = zebra_evpn_es_find(esi);
+ if (!es) {
+ es = zebra_evpn_es_new(esi);
+ if (!es) {
+ /* XXX - error log */
+ return -1;
+ }
+ }
+
+ zebra_evpn_es_vtep_add(es, vtep_ip);
+ zebra_evpn_es_remote_info_re_eval(es);
+
+ return 0;
+}
+
+void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ struct in_addr vtep_ip;
+ esi_t esi;
+
+ if (!is_evpn_enabled()) {
+ zlog_debug(
+ "%s: EVPN not enabled yet we received a es_add zapi call",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ memset(&esi, 0, sizeof(esi_t));
+ s = msg;
+
+ stream_get(&esi, s, sizeof(esi_t));
+ vtep_ip.s_addr = stream_get_ipv4(s);
+
+ if (hdr->command == ZEBRA_REMOTE_ES_VTEP_ADD)
+ zebra_evpn_remote_es_add(&esi, vtep_ip);
+ else
+ zebra_evpn_remote_es_del(&esi, vtep_ip);
+}
+
+void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac)
+{
+ struct zebra_evpn_es *es = mac->es;
+
+ mac->es = NULL;
+ if (!es)
+ return;
+
+ list_delete_node(es->mac_list, &mac->es_listnode);
+ if (!listcount(es->mac_list))
+ zebra_evpn_es_free(es);
+}
+
+/* Associate a MAC entry with a local or remote ES. Returns false if there
+ * was no ES change.
+ */
+bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, struct zebra_evpn_es *es)
+{
+ if (mac->es == es)
+ return false;
+
+ if (mac->es)
+ zebra_evpn_es_mac_deref_entry(mac);
+
+ if (!es)
+ return true;
+
+ mac->es = es;
+ listnode_init(&mac->es_listnode, mac);
+ listnode_add(es->mac_list, &mac->es_listnode);
+
+ return true;
+}
+
+bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi)
+{
+ struct zebra_evpn_es *es;
+
+ es = zebra_evpn_es_find(esi);
+ if (!es) {
+ es = zebra_evpn_es_new(esi);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("auto es %s add on mac ref", es->esi_str);
+ }
+
+ return zebra_evpn_es_mac_ref_entry(mac, es);
+}
+
+/* Inform BGP about local ES-EVI add or del */
+static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
+ zebra_evpn_t *zevpn, bool add)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s,
+ add ? ZEBRA_LOCAL_ES_EVI_ADD : ZEBRA_LOCAL_ES_EVI_DEL,
+ zebra_vrf_get_evpn_id());
+ stream_put(s, &es->esi, sizeof(esi_t));
+ stream_putl(s, zevpn->vni);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("send %s local es %s evi %u to %s",
+ add ? "add" : "del",
+ es->esi_str, zevpn->vni,
+ zebra_route_string(client->proto));
+
+ client->local_es_add_cnt++;
+ return zserv_send_message(client, s);
+}
+
+/* sysmac part of a local ESI has changed */
+static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif,
+ struct ethaddr *sysmac)
+{
+ int rv;
+
+ rv = zebra_evpn_local_es_update(zif, zif->es_info.lid, sysmac);
+ if (!rv)
+ memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr));
+
+ return rv;
+}
+
+/* local-ID part of ESI has changed */
+static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid)
+{
+ int rv;
+
+ rv = zebra_evpn_local_es_update(zif, lid, &zif->es_info.sysmac);
+ if (!rv)
+ zif->es_info.lid = lid;
+
+ return rv;
+}
+
+void zebra_evpn_es_cleanup(void)
+{
+ struct zebra_evpn_es *es;
+ struct zebra_evpn_es *es_next;
+
+ RB_FOREACH_SAFE(es, zebra_es_rb_head,
+ &zmh_info->es_rb_tree, es_next) {
+ zebra_evpn_local_es_del(es);
+ zebra_evpn_remote_es_flush(es);
+ }
+}
+
+/* Only certain types of access ports can be setup as an Ethernet Segment */
+bool zebra_evpn_is_if_es_capable(struct zebra_if *zif)
+{
+ if (zif->zif_type == ZEBRA_IF_BOND)
+ return true;
+
+ /* XXX: allow swpX i.e. a regular ethernet port to be an ES link too */
+ return false;
+}
+
+void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif)
+{
+ char buf[ETHER_ADDR_STRLEN];
+
+ if (zif->es_info.lid || !is_zero_mac(&zif->es_info.sysmac))
+ vty_out(vty, " EVPN MH: ES id %u ES sysmac %s\n",
+ zif->es_info.lid,
+ prefix_mac2str(&zif->es_info.sysmac,
+ buf, sizeof(buf)));
+}
+
+void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up)
+{
+ struct zebra_evpn_es *es = zif->es_info.es;
+ bool old_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP);
+
+ if (old_up == up)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es %s state changed to %s ",
+ es->esi_str,
+ up ? "up" : "down");
+ if (up)
+ es->flags |= ZEBRA_EVPNES_OPER_UP;
+ else
+ es->flags &= ~ZEBRA_EVPNES_OPER_UP;
+
+ /* inform BGP of the ES oper state change */
+ if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
+ zebra_evpn_es_send_add_to_client(es);
+}
+
+static char *zebra_evpn_es_vtep_str(char *vtep_str, struct zebra_evpn_es *es,
+ uint8_t vtep_str_size)
+{
+ struct zebra_evpn_es_vtep *zvtep;
+ struct listnode *node;
+ bool first = true;
+
+ vtep_str[0] = '\0';
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep)) {
+ if (first) {
+ first = false;
+ strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip),
+ vtep_str_size);
+ } else {
+ strlcat(vtep_str, ",", vtep_str_size);
+ strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip),
+ vtep_str_size);
+ }
+ }
+ return vtep_str;
+}
+
+static void zebra_evpn_es_show_entry(struct vty *vty,
+ struct zebra_evpn_es *es, json_object *json)
+{
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ];
+
+ if (json) {
+ /* XXX */
+ } else {
+ type_str[0] = '\0';
+ if (es->flags & ZEBRA_EVPNES_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es->flags & ZEBRA_EVPNES_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+
+ zebra_evpn_es_vtep_str(vtep_str, es, sizeof(vtep_str));
+
+ vty_out(vty, "%-30s %-4s %-21s %s\n",
+ es->esi_str, type_str,
+ es->zif ? es->zif->ifp->name : "-",
+ vtep_str);
+ }
+}
+
+static void zebra_evpn_es_show_entry_detail(struct vty *vty,
+ struct zebra_evpn_es *es, json_object *json)
+{
+ char type_str[80];
+ struct zebra_evpn_es_vtep *zvtep;
+ struct listnode *node;
+
+ if (json) {
+ /* XXX */
+ } else {
+ type_str[0] = '\0';
+ if (es->flags & ZEBRA_EVPNES_LOCAL)
+ strlcat(type_str, "Local", sizeof(type_str));
+ if (es->flags & ZEBRA_EVPNES_REMOTE) {
+ if (strnlen(type_str, sizeof(type_str)))
+ strlcat(type_str, ",", sizeof(type_str));
+ strlcat(type_str, "Remote", sizeof(type_str));
+ }
+
+ vty_out(vty, "ESI: %s\n", es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " Interface: %s\n",
+ (es->zif) ?
+ es->zif->ifp->name : "-");
+ vty_out(vty, " State: %s\n",
+ (es->flags & ZEBRA_EVPNES_OPER_UP) ?
+ "up" : "down");
+ vty_out(vty, " Ready for BGP: %s\n",
+ (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) ?
+ "yes" : "no");
+ vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list));
+ vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list));
+ vty_out(vty, " Nexthop group: 0x%x\n", es->nhg_id);
+ vty_out(vty, " VTEPs:\n");
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep))
+ vty_out(vty, " %s nh: 0x%x\n",
+ inet_ntoa(zvtep->vtep_ip),
+ zvtep->nh_id);
+
+ vty_out(vty, "\n");
+ }
+}
+
+void zebra_evpn_es_show(struct vty *vty, bool uj)
+{
+ struct zebra_evpn_es *es;
+ json_object *json = NULL;
+
+ if (uj) {
+ /* XXX */
+ } else {
+ vty_out(vty, "Type: L local, R remote\n");
+ vty_out(vty, "%-30s %-4s %-21s %s\n",
+ "ESI", "Type", "ES-IF", "VTEPs");
+ }
+
+ RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree)
+ zebra_evpn_es_show_entry(vty, es, json);
+}
+
+void zebra_evpn_es_show_detail(struct vty *vty, bool uj)
+{
+ struct zebra_evpn_es *es;
+ json_object *json = NULL;
+
+ RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree)
+ zebra_evpn_es_show_entry_detail(vty, es, json);
+}
+
+void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi)
+{
+ struct zebra_evpn_es *es;
+ char esi_str[ESI_STR_LEN];
+ json_object *json = NULL;
+
+ es = zebra_evpn_es_find(esi);
+
+ if (!es) {
+ esi_to_str(esi, esi_str, sizeof(esi_str));
+ vty_out(vty, "ESI %s does not exist\n", esi_str);
+ return;
+ }
+
+ zebra_evpn_es_show_entry_detail(vty, es, json);
+}
+
+int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp)
+{
+ struct zebra_if *zif = ifp->info;
+ char buf[ETHER_ADDR_STRLEN];
+
+ if (zif->es_info.lid)
+ vty_out(vty, " evpn mh es-id %u\n", zif->es_info.lid);
+
+ if (!is_zero_mac(&zif->es_info.sysmac))
+ vty_out(vty, " evpn mh es-sys-mac %s\n",
+ prefix_mac2str(&zif->es_info.sysmac,
+ buf, sizeof(buf)));
+ return 0;
+}
+
+#ifndef VTYSH_EXTRACT_PL
+#include "zebra/zebra_evpn_mh_clippy.c"
+#endif
+/* CLI for setting up sysmac part of ESI on an access port */
+DEFPY(zebra_evpn_es_sys_mac,
+ zebra_evpn_es_sys_mac_cmd,
+ "[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]",
+ NO_STR
+ "EVPN\n"
+ EVPN_MH_VTY_STR
+ "Ethernet segment system MAC\n"
+ MAC_STR
+)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct zebra_if *zif;
+ int ret = 0;
+
+ zif = ifp->info;
+
+ if (no) {
+ static struct ethaddr zero_mac;
+
+ ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to clear ES sysmac\n");
+ return CMD_WARNING;
+ }
+ } else {
+
+ if (!zebra_evpn_is_if_es_capable(zif)) {
+ vty_out(vty,
+ "%%ESI cannot be associated with this interface type\n");
+ return CMD_WARNING;
+ }
+
+ if (!mac || is_zero_mac(&mac->eth_addr)) {
+ vty_out(vty, "%%ES sysmac value is invalid\n");
+ return CMD_WARNING;
+ }
+
+ ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr);
+ if (ret == -1) {
+ vty_out(vty, "%%ESI already exists on a different interface\n");
+ return CMD_WARNING;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+/* CLI for setting up local-ID part of ESI on an access port */
+DEFPY(zebra_evpn_es_id,
+ zebra_evpn_es_id_cmd,
+ "[no$no] evpn mh es-id [(1-16777215)$es_lid]",
+ NO_STR
+ "EVPN\n"
+ EVPN_MH_VTY_STR
+ "Ethernet segment local identifier\n"
+ "ID\n"
+)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct zebra_if *zif;
+ int ret;
+
+ zif = ifp->info;
+
+ if (no) {
+ ret = zebra_evpn_es_lid_update(zif, 0);
+ if (ret == -1) {
+ vty_out(vty, "%%Failed to clear ES local id\n");
+ return CMD_WARNING;
+ }
+ } else {
+ if (!zebra_evpn_is_if_es_capable(zif)) {
+ vty_out(vty,
+ "%%ESI cannot be associated with this interface type\n");
+ return CMD_WARNING;
+ }
+
+ if (!es_lid) {
+ vty_out(vty, "%%Specify local ES ID\n");
+ return CMD_WARNING;
+ }
+ ret = zebra_evpn_es_lid_update(zif, es_lid);
+ if (ret == -1) {
+ vty_out(vty,
+ "%%ESI already exists on a different interface\n");
+ return CMD_WARNING;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+/*****************************************************************************/
+/* A base L2-VNI is maintained to derive parameters such as ES originator-IP.
+ * XXX: once single vxlan device model becomes available this will not be
+ * necessary
+ */
+/* called when a new vni is added or becomes oper up or becomes a bridge port */
+void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn)
+{
+ struct listnode *node;
+ struct zebra_evpn_es *es;
+
+ if (zmh_info->es_base_evpn) {
+ if (zmh_info->es_base_evpn != zevpn) {
+ /* unrelated EVPN; ignore it */
+ return;
+ }
+ /* check if the local vtep-ip has changed */
+ } else {
+ /* check if the EVPN can be used as base EVPN */
+ if (!zebra_evpn_send_to_client_ok(zevpn))
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es base vni set to %d",
+ zevpn->vni);
+ zmh_info->es_base_evpn = zevpn;
+ }
+
+ /* update local VTEP-IP */
+ if (zmh_info->es_originator_ip.s_addr ==
+ zmh_info->es_base_evpn->local_vtep_ip.s_addr)
+ return;
+
+ zmh_info->es_originator_ip.s_addr =
+ zmh_info->es_base_evpn->local_vtep_ip.s_addr;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es originator ip set to %s",
+ inet_ntoa(zmh_info->es_base_evpn->local_vtep_ip));
+
+ /* if originator ip changes we need to update bgp */
+ for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) {
+ if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP)
+ zebra_evpn_es_send_add_to_client(es);
+ else
+ zebra_evpn_es_re_eval_send_to_client(es,
+ true /* es_evi_re_reval */);
+ }
+}
+
+/* called when a vni is removed or becomes oper down or is removed from a
+ * bridge
+ */
+void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn)
+{
+ struct listnode *node;
+ struct zebra_evpn_es *es;
+
+ if (zmh_info->es_base_evpn != zevpn)
+ return;
+
+ zmh_info->es_base_evpn = NULL;
+ /* lost current base EVPN; try to find a new one */
+ zebra_evpn_es_get_one_base_evpn();
+
+ /* couldn't locate an eligible base evpn */
+ if (!zmh_info->es_base_evpn && zmh_info->es_originator_ip.s_addr) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("es originator ip cleared");
+
+ zmh_info->es_originator_ip.s_addr = 0;
+ /* lost originator ip */
+ for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) {
+ zebra_evpn_es_re_eval_send_to_client(es,
+ true /* es_evi_re_reval */);
+ }
+ }
+}
+
+/* Locate an "eligible" L2-VNI to follow */
+static int zebra_evpn_es_get_one_base_evpn_cb(struct hash_bucket *b, void *data)
+{
+ zebra_evpn_t *zevpn = b->data;
+
+ zebra_evpn_es_set_base_evpn(zevpn);
+
+ if (zmh_info->es_base_evpn)
+ return HASHWALK_ABORT;
+
+ return HASHWALK_CONTINUE;
+}
+
+/* locate a base_evpn to follow for the purposes of common params like
+ * originator IP
+ */
+static void zebra_evpn_es_get_one_base_evpn(void)
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = zebra_vrf_get_evpn();
+ hash_walk(zvrf->evpn_table, zebra_evpn_es_get_one_base_evpn_cb, NULL);
+}
+
+/*****************************************************************************/
+void zebra_evpn_mh_config_write(struct vty *vty)
+{
+ if (zmh_info->mac_hold_time != EVPN_MH_MAC_HOLD_TIME_DEF)
+ vty_out(vty, "evpn mh mac-holdtime %ld\n",
+ zmh_info->mac_hold_time);
+
+ if (zmh_info->neigh_hold_time != EVPN_MH_NEIGH_HOLD_TIME_DEF)
+ vty_out(vty, "evpn mh neigh-holdtime %ld\n",
+ zmh_info->neigh_hold_time);
+}
+
+int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
+ uint32_t duration, bool set_default)
+{
+ if (set_default)
+ zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF;
+
+ zmh_info->neigh_hold_time = duration;
+
+ return 0;
+}
+
+int zebra_evpn_mh_mac_holdtime_update(struct vty *vty,
+ uint32_t duration, bool set_default)
+{
+ if (set_default)
+ duration = EVPN_MH_MAC_HOLD_TIME_DEF;
+
+ zmh_info->mac_hold_time = duration;
+
+ return 0;
+}
+
+void zebra_evpn_interface_init(void)
+{
+ install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd);
+ install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd);
+}
+
+void zebra_evpn_mh_init(void)
+{
+ zrouter.mh_info = XCALLOC(MTYPE_ZMH_INFO, sizeof(*zrouter.mh_info));
+
+ zmh_info->mac_hold_time = EVPN_MH_MAC_HOLD_TIME_DEF;
+ zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF;
+ /* setup ES tables */
+ RB_INIT(zebra_es_rb_head, &zmh_info->es_rb_tree);
+ zmh_info->local_es_list = list_new();
+ listset_app_node_mem(zmh_info->local_es_list);
+
+ bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX);
+ bf_assign_zero_index(zmh_info->nh_id_bitmap);
+
+ /* setup broadcast domain tables */
+ zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake,
+ zebra_evpn_acc_vl_cmp, "access VLAN hash table");
+}
+
+void zebra_evpn_mh_terminate(void)
+{
+ list_delete(&zmh_info->local_es_list);
+
+ hash_iterate(zmh_info->evpn_vlan_table,
+ zebra_evpn_acc_vl_cleanup_all, NULL);
+ hash_free(zmh_info->evpn_vlan_table);
+}
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
new file mode 100644
index 0000000000..ed62677e3b
--- /dev/null
+++ b/zebra/zebra_evpn_mh.h
@@ -0,0 +1,239 @@
+/*
+ * Zebra EVPN MH Data structures and definitions
+ *
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _ZEBRA_EVPN_MH_H
+#define _ZEBRA_EVPN_MH_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+#include "bitfield.h"
+#include "zebra_vxlan.h"
+#include "zebra_vxlan_private.h"
+
+#define EVPN_MH_VTY_STR "Multihoming\n"
+
+/* Ethernet Segment entry -
+ * - Local and remote ESs are maintained in a global RB tree,
+ * zmh_info->es_rb_tree using ESI as key
+ * - Local ESs are added via zebra config (ZEBRA_EVPNES_LOCAL) when an
+ * access port is associated with an ES-ID
+ * - Remotes ESs are added by BGP based on received/remote EAD/Type-1 routes
+ * (ZEBRA_EVPNES_REMOTE)
+ * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are
+ * expected to have REMOTE ES peers.
+ */
+struct zebra_evpn_es {
+ esi_t esi;
+ char esi_str[ESI_STR_LEN];
+
+ /* ES flags */
+ uint32_t flags;
+#define ZEBRA_EVPNES_LOCAL (1 << 0) /* configured in zebra */
+#define ZEBRA_EVPNES_REMOTE (1 << 1) /* added by bgp */
+#define ZEBRA_EVPNES_OPER_UP (1 << 2) /* es->ifp is oper-up */
+#define ZEBRA_EVPNES_READY_FOR_BGP (1 << 3) /* ready to be sent to BGP */
+#define ZEBRA_EVPNES_NHG_ACTIVE (1 << 4) /* NHG has been installed */
+
+ /* memory used for adding the es to zmh_info->es_rb_tree */
+ RB_ENTRY(zebra_evpn_es) rb_node;
+
+ /* [EVPNES_LOCAL] memory used for linking the es to
+ * zmh_info->local_es_list
+ */
+ struct listnode local_es_listnode;
+
+ /* [EVPNES_LOCAL] corresponding interface */
+ struct zebra_if *zif;
+
+ /* list of ES-EVIs associated with the ES */
+ struct list *es_evi_list;
+
+ /* [!EVPNES_LOCAL] List of remote VTEPs (zebra_evpn_es_vtep) */
+ struct list *es_vtep_list;
+
+ /* list of zebra_mac entries using this ES as destination */
+ struct list *mac_list;
+
+ /* Nexthop group id */
+ uint32_t nhg_id;
+};
+RB_HEAD(zebra_es_rb_head, zebra_evpn_es);
+RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp);
+
+/* ES per-EVI info
+ * - ES-EVIs are maintained per-EVPN (vni->es_evi_rb_tree)
+ * - Local ES-EVIs are linked to per-EVPN list for quick access
+ * - Although some infrastucture is present for remote ES-EVIs, currently
+ * BGP does NOT send remote ES-EVIs to zebra. This may change in the
+ * future (but must be changed thoughtfully and only if needed as ES-EVI
+ * can get prolific and come in the way of rapid failovers)
+ */
+struct zebra_evpn_es_evi {
+ struct zebra_evpn_es *es;
+ zebra_evpn_t *zevpn;
+
+ /* ES-EVI flags */
+ uint32_t flags;
+ /* local ES-EVI */
+#define ZEBRA_EVPNES_EVI_LOCAL (1 << 0) /* created by zebra */
+#define ZEBRA_EVPNES_EVI_READY_FOR_BGP (1 << 1) /* ready to be sent to BGP */
+
+ /* memory used for adding the es_evi to
+ * es_evi->zevpn->es_evi_rb_tree
+ */
+ RB_ENTRY(zebra_evpn_es_evi) rb_node;
+ /* memory used for linking the es_evi to
+ * es_evi->zevpn->local_es_evi_list
+ */
+ struct listnode l2vni_listnode;
+ /* memory used for linking the es_evi to
+ * es_evi->es->es_evi_list
+ */
+ struct listnode es_listnode;
+};
+
+/* PE attached to an ES */
+struct zebra_evpn_es_vtep {
+ struct zebra_evpn_es *es; /* parent ES */
+ struct in_addr vtep_ip;
+
+ /* memory used for adding the entry to es->es_vtep_list */
+ struct listnode es_listnode;
+
+ /* MAC nexthop */
+ uint32_t nh_id;
+
+ /* XXX - maintain a backpointer to zebra_vtep_t */
+};
+
+/* Local/access-side broadcast domain - zebra_evpn_access_bd is added to -
+ * zrouter->evpn_vlan_table (for VLAN aware bridges) OR
+ * zrouter->evpn_bridge_table (for VLAN unaware bridges)
+ * XXX - support for VLAN unaware bridges is yet to be flushed out
+ */
+struct zebra_evpn_access_bd {
+ vlanid_t vid;
+
+ struct zebra_if *vxlan_zif; /* vxlan device */
+ /* list of members associated with the BD i.e. (potential) ESs */
+ struct list *mbr_zifs;
+ /* presence of zevpn activates the EVI on all the ESs in mbr_zifs */
+ zebra_evpn_t *zevpn;
+};
+
+/* multihoming information stored in zrouter */
+#define zmh_info (zrouter.mh_info)
+struct zebra_evpn_mh_info {
+ /* RB tree of Ethernet segments (used for EVPN-MH) */
+ struct zebra_es_rb_head es_rb_tree;
+ /* List of local ESs */
+ struct list *local_es_list;
+
+ /* EVPN MH broadcast domains indexed by the VID */
+ struct hash *evpn_vlan_table;
+
+ /* A base L2-VNI is maintained to derive parameters such as
+ * ES originator-IP.
+ * XXX: once single vxlan device model becomes available this will
+ * not be necessary
+ */
+ zebra_evpn_t *es_base_evpn;
+ struct in_addr es_originator_ip;
+
+ /* L2 NH and NHG ids -
+ * Most significant 8 bits is type. Lower 24 bits is the value
+ * allocated from the nh_id_bitmap.
+ */
+ bitfield_t nh_id_bitmap;
+#define EVPN_NH_ID_MAX (16*1024)
+#define EVPN_NH_ID_VAL_MASK 0xffffff
+#define EVPN_NH_ID_TYPE_POS 24
+/* The purpose of using different types for NHG and NH is NOT to manage the
+ * id space separately. It is simply to make debugging easier.
+ */
+#define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS)
+#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS)
+
+ /* XXX - re-visit the default hold timer value */
+#define EVPN_MH_MAC_HOLD_TIME_DEF (18 * 60)
+ long mac_hold_time;
+#define EVPN_MH_NEIGH_HOLD_TIME_DEF (18 * 60)
+ long neigh_hold_time;
+};
+
+static inline bool zebra_evpn_mac_is_es_local(zebra_mac_t *mac)
+{
+ return mac->es && (mac->es->flags & ZEBRA_EVPNES_LOCAL);
+}
+
+/* Returns true if the id is of L2-NHG or L2-NH type */
+static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id)
+{
+ return ((id & EVPN_NHG_ID_TYPE_BIT) ||
+ (id & EVPN_NH_ID_TYPE_BIT));
+}
+
+/*****************************************************************************/
+extern esi_t *zero_esi;
+extern void zebra_evpn_mh_init(void);
+extern void zebra_evpn_mh_terminate(void);
+extern bool zebra_evpn_is_if_es_capable(struct zebra_if *zif);
+extern void zebra_evpn_if_init(struct zebra_if *zif);
+extern void zebra_evpn_if_cleanup(struct zebra_if *zif);
+extern void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn);
+extern void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn);
+extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn,
+ bool set);
+extern void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn);
+extern void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn);
+extern void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif);
+extern void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif);
+extern void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif);
+extern void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif);
+extern void zebra_evpn_es_send_all_to_client(bool add);
+extern void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up);
+extern void zebra_evpn_es_show(struct vty *vty, bool uj);
+extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj);
+extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi);
+extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn);
+extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS);
+extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail);
+extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj,
+ vni_t vni, int detail);
+extern void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac);
+extern bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac,
+ struct zebra_evpn_es *es);
+extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi);
+extern struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi);
+extern void zebra_evpn_interface_init(void);
+extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp);
+extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj);
+extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj);
+extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid);
+extern void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif);
+extern void zebra_evpn_es_cleanup(void);
+extern int zebra_evpn_mh_mac_holdtime_update(struct vty *vty,
+ uint32_t duration, bool set_default);
+void zebra_evpn_mh_config_write(struct vty *vty);
+int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty,
+ uint32_t duration, bool set_default);
+
+#endif /* _ZEBRA_EVPN_MH_H */
diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c
new file mode 100644
index 0000000000..492052b1b2
--- /dev/null
+++ b/zebra/zebra_evpn_neigh.c
@@ -0,0 +1,2453 @@
+/*
+ * Zebra EVPN Neighbor code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "interface.h"
+#include "jhash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "vlan.h"
+#include "json.h"
+
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_router.h"
+#include "zebra/rt.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mh.h"
+#include "zebra/zebra_evpn_neigh.h"
+#include "zebra/zebra_evpn_mac.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "EVI Neighbor");
+
+/*
+ * Make hash key for neighbors.
+ */
+static unsigned int neigh_hash_keymake(const void *p)
+{
+ const zebra_neigh_t *n = p;
+ const struct ipaddr *ip = &n->ip;
+
+ if (IS_IPADDR_V4(ip))
+ return jhash_1word(ip->ipaddr_v4.s_addr, 0);
+
+ return jhash2(ip->ipaddr_v6.s6_addr32,
+ array_size(ip->ipaddr_v6.s6_addr32), 0);
+}
+
+/*
+ * Compare two neighbor hash structures.
+ */
+static bool neigh_cmp(const void *p1, const void *p2)
+{
+ const zebra_neigh_t *n1 = p1;
+ const zebra_neigh_t *n2 = p2;
+
+ if (n1 == NULL && n2 == NULL)
+ return true;
+
+ if (n1 == NULL || n2 == NULL)
+ return false;
+
+ return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0);
+}
+
+int neigh_list_cmp(void *p1, void *p2)
+{
+ const zebra_neigh_t *n1 = p1;
+ const zebra_neigh_t *n2 = p2;
+
+ return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr));
+}
+
+struct hash *zebra_neigh_db_create(const char *desc)
+{
+ return hash_create(neigh_hash_keymake, neigh_cmp, desc);
+}
+
+uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn)
+{
+ unsigned int i;
+ uint32_t num_neighs = 0;
+ struct hash *hash;
+ struct hash_bucket *hb;
+ zebra_neigh_t *nbr;
+
+ hash = zevpn->neigh_table;
+ if (!hash)
+ return num_neighs;
+ for (i = 0; i < hash->size; i++) {
+ for (hb = hash->index[i]; hb; hb = hb->next) {
+ nbr = (zebra_neigh_t *)hb->data;
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+ num_neighs++;
+ }
+ }
+
+ return num_neighs;
+}
+
+/*
+ * Helper function to determine maximum width of neighbor IP address for
+ * display - just because we're dealing with IPv6 addresses that can
+ * widely vary.
+ */
+void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_neigh_t *n;
+ char buf[INET6_ADDRSTRLEN];
+ struct neigh_walk_ctx *wctx = ctxt;
+ int width;
+
+ n = (zebra_neigh_t *)bucket->data;
+
+ ipaddr2str(&n->ip, buf, sizeof(buf));
+ width = strlen(buf);
+ if (width > wctx->addr_width)
+ wctx->addr_width = width;
+}
+
+/*
+ * Count of remote neighbors referencing this MAC.
+ */
+int remote_neigh_count(zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ int count = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Install remote neighbor into the kernel.
+ */
+int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ bool was_static)
+{
+ struct interface *vlan_if;
+ int flags;
+ int ret = 0;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE))
+ return 0;
+
+ vlan_if = zevpn_map_to_svi(zevpn);
+ if (!vlan_if)
+ return -1;
+
+ flags = DPLANE_NTF_EXT_LEARNED;
+ if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
+ flags |= DPLANE_NTF_ROUTER;
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+
+ dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, was_static);
+
+ return ret;
+}
+
+/*
+ * Install neighbor hash entry - called upon access VLAN change.
+ */
+void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_neigh_t *n;
+ struct neigh_walk_ctx *wctx = ctxt;
+
+ n = (zebra_neigh_t *)bucket->data;
+
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+ zebra_evpn_rem_neigh_install(wctx->zevpn, n,
+ false /*was_static*/);
+}
+
+/*
+ * Callback to allocate neighbor hash entry.
+ */
+static void *zebra_evpn_neigh_alloc(void *p)
+{
+ const zebra_neigh_t *tmp_n = p;
+ zebra_neigh_t *n;
+
+ n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n,
+ struct ethaddr *macaddr,
+ zebra_mac_t *mac,
+ bool send_mac_update)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool old_static;
+ bool new_static;
+
+ memcpy(&n->emac, macaddr, ETH_ALEN);
+ n->mac = mac;
+
+ /* Link to new MAC */
+ if (!mac)
+ return;
+
+ listnode_add_sort(mac->neigh_list, n);
+ if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) {
+ old_static = zebra_evpn_mac_is_static(mac);
+ ++mac->sync_neigh_cnt;
+ new_static = zebra_evpn_mac_is_static(mac);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-neigh ref mac vni %u ip %s mac %s ref %d",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ mac->sync_neigh_cnt);
+ if ((old_static != new_static) && send_mac_update)
+ /* program the local mac in the kernel */
+ zebra_evpn_sync_mac_dp_install(
+ mac, false /*set_inactive*/,
+ false /*force_clear_static*/, __func__);
+ }
+}
+
+/* sync-path that is active on an ES peer */
+static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n,
+ bool set_inactive,
+ bool force_clear_static,
+ const char *caller)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ bool set_static;
+ bool set_router;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ ifp = if_lookup_by_index_per_ns(zns, n->ifindex);
+ if (!ifp) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped",
+ caller, n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->ifindex, n->flags);
+ return;
+ }
+
+ if (force_clear_static)
+ set_static = false;
+ else
+ set_static = zebra_evpn_neigh_is_static(n);
+
+ set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ /* XXX - this will change post integration with the new kernel */
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE))
+ set_inactive = true;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s",
+ caller, n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ ifp->name, n->ifindex, n->flags,
+ set_router ? " router" : "",
+ set_static ? " static" : "",
+ set_inactive ? " inactive" : "");
+ dplane_local_neigh_add(ifp, &n->ip, &n->emac, set_router, set_static,
+ set_inactive);
+}
+
+/*
+ * Inform BGP about local neighbor addition.
+ */
+int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
+ struct ethaddr *macaddr,
+ zebra_mac_t *zmac, uint32_t neigh_flags,
+ uint32_t seq)
+{
+ uint8_t flags = 0;
+
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) {
+ /* host reachability has not been verified locally */
+
+ /* if no ES peer is claiming reachability we can't advertise
+ * the entry
+ */
+ if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ return 0;
+
+ /* ES peers are claiming reachability; we will
+ * advertise the entry but with a proxy flag
+ */
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ /* Set router flag (R-bit) based on local neigh entry add */
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP);
+
+ return zebra_evpn_macip_send_msg_to_client(
+ vni, macaddr, ip, flags, seq, ZEBRA_NEIGH_ACTIVE,
+ zmac ? zmac->es : NULL, ZEBRA_MACIP_ADD);
+}
+
+/*
+ * Inform BGP about local neighbor deletion.
+ */
+int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
+ struct ethaddr *macaddr, uint32_t flags,
+ int state, bool force)
+{
+ if (!force) {
+ if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE)
+ && !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ /* the neigh was not advertised - nothing to delete */
+ return 0;
+ }
+
+ return zebra_evpn_macip_send_msg_to_client(
+ vni, macaddr, ip, flags, 0, state, NULL, ZEBRA_MACIP_DEL);
+}
+
+static void zebra_evpn_neigh_send_add_del_to_client(zebra_neigh_t *n,
+ bool old_bgp_ready,
+ bool new_bgp_ready)
+{
+ if (new_bgp_ready)
+ zebra_evpn_neigh_send_add_to_client(n->zevpn->vni, &n->ip,
+ &n->emac, n->mac, n->flags,
+ n->loc_seq);
+ else if (old_bgp_ready)
+ zebra_evpn_neigh_send_del_to_client(n->zevpn->vni, &n->ip,
+ &n->emac, n->flags,
+ n->state, true /*force*/);
+}
+
+/* if the static flag associated with the neigh changes we need
+ * to update the sync-neigh references against the MAC
+ * and inform the dataplane about the static flag changes.
+ */
+void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static,
+ bool new_n_static, bool defer_n_dp,
+ bool defer_mac_dp, const char *caller)
+{
+ zebra_mac_t *mac = n->mac;
+ bool old_mac_static;
+ bool new_mac_static;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (old_n_static == new_n_static)
+ return;
+
+ /* update the neigh sync references in the dataplane. if
+ * the neigh is in the middle of updates the caller can
+ * request for a defer
+ */
+ if (!defer_n_dp)
+ zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ if (!mac)
+ return;
+
+ /* update the mac sync ref cnt */
+ old_mac_static = zebra_evpn_mac_is_static(mac);
+ if (new_n_static) {
+ ++mac->sync_neigh_cnt;
+ } else if (old_n_static) {
+ if (mac->sync_neigh_cnt)
+ --mac->sync_neigh_cnt;
+ }
+ new_mac_static = zebra_evpn_mac_is_static(mac);
+
+ /* update the mac sync references in the dataplane */
+ if ((old_mac_static != new_mac_static) && !defer_mac_dp)
+ zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s",
+ n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ n->flags, mac->sync_neigh_cnt,
+ old_n_static ? " old_n_static" : "",
+ new_n_static ? " new_n_static" : "",
+ old_mac_static ? " old_mac_static" : "",
+ new_mac_static ? " new_mac_static" : "", caller);
+}
+
+/* Neigh hold timer is used to age out peer-active flag.
+ *
+ * During this wait time we expect the dataplane component or an
+ * external neighmgr daemon to probe existing hosts to independently
+ * establish their presence on the ES.
+ */
+static int zebra_evpn_neigh_hold_exp_cb(struct thread *t)
+{
+ zebra_neigh_t *n;
+ bool old_bgp_ready;
+ bool new_bgp_ready;
+ bool old_n_static;
+ bool new_n_static;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ n = THREAD_ARG(t);
+ /* the purpose of the hold timer is to age out the peer-active
+ * flag
+ */
+ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ return 0;
+
+ old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ old_n_static = zebra_evpn_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+ new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ new_n_static = zebra_evpn_neigh_is_static(n);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ n->flags);
+
+ /* re-program the local neigh in the dataplane if the neigh is no
+ * longer static
+ */
+ if (old_n_static != new_n_static)
+ zebra_evpn_sync_neigh_static_chg(
+ n, old_n_static, new_n_static, false /*defer_n_dp*/,
+ false /*defer_mac_dp*/, __func__);
+
+ /* inform bgp if needed */
+ if (old_bgp_ready != new_bgp_ready)
+ zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready,
+ new_bgp_ready);
+
+ return 0;
+}
+
+static inline void zebra_evpn_neigh_start_hold_timer(zebra_neigh_t *n)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (n->hold_timer)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ n->flags);
+ thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n,
+ zmh_info->neigh_hold_time, &n->hold_timer);
+}
+
+static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n,
+ bool send_mac_update)
+{
+ zebra_mac_t *mac = n->mac;
+ zebra_evpn_t *zevpn = n->zevpn;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool old_static;
+ bool new_static;
+
+ n->mac = NULL;
+ if (!mac)
+ return;
+
+ if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && mac->sync_neigh_cnt) {
+ old_static = zebra_evpn_mac_is_static(mac);
+ --mac->sync_neigh_cnt;
+ new_static = zebra_evpn_mac_is_static(mac);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-neigh deref mac vni %u ip %s mac %s ref %d",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ mac->sync_neigh_cnt);
+ if ((old_static != new_static) && send_mac_update)
+ /* program the local mac in the kernel */
+ zebra_evpn_sync_mac_dp_install(
+ mac, false /* set_inactive */,
+ false /* force_clear_static */, __func__);
+ }
+
+ listnode_delete(mac->neigh_list, n);
+ zebra_evpn_deref_ip2mac(zevpn, mac);
+}
+
+bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ struct ethaddr *macaddr, uint32_t seq)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ uint32_t tmp_seq;
+
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
+ tmp_seq = n->loc_seq;
+ else
+ tmp_seq = n->rem_seq;
+
+ if (seq < tmp_seq) {
+ /* if the neigh was never advertised to bgp we must accept
+ * whatever sequence number bgp sends
+ * XXX - check with Vivek
+ */
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)
+ && !zebra_evpn_neigh_is_ready_for_bgp(n)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-macip accept vni %u mac %s IP %s lower seq %u f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, macbuf,
+ sizeof(macbuf)),
+ ipaddr2str(&n->ip, ipbuf,
+ sizeof(ipbuf)),
+ tmp_seq, n->flags);
+ return true;
+ }
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-macip ignore vni %u mac %s IP %s as existing has higher seq %u f 0x%x",
+ zevpn->vni,
+ prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ tmp_seq, n->flags);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Add neighbor entry.
+ */
+static zebra_neigh_t *zebra_evpn_neigh_add(zebra_evpn_t *zevpn,
+ struct ipaddr *ip,
+ struct ethaddr *mac,
+ zebra_mac_t *zmac, uint32_t n_flags)
+{
+ zebra_neigh_t tmp_n;
+ zebra_neigh_t *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(zebra_neigh_t));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc);
+ assert(n);
+
+ n->state = ZEBRA_NEIGH_INACTIVE;
+ n->zevpn = zevpn;
+ n->dad_ip_auto_recovery_timer = NULL;
+ n->flags = n_flags;
+
+ if (!zmac)
+ zmac = zebra_evpn_mac_lookup(zevpn, mac);
+ zebra_evpn_local_neigh_ref_mac(n, mac, zmac,
+ false /* send_mac_update */);
+
+ return n;
+}
+
+/*
+ * Delete neighbor entry.
+ */
+int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n)
+{
+ zebra_neigh_t *tmp_n;
+
+ if (n->mac)
+ listnode_delete(n->mac->neigh_list, n);
+
+ /* Cancel auto recovery */
+ THREAD_OFF(n->dad_ip_auto_recovery_timer);
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_n = hash_release(zevpn->neigh_table, n);
+ XFREE(MTYPE_NEIGH, tmp_n);
+
+ return 0;
+}
+
+void zebra_evpn_sync_neigh_del(zebra_neigh_t *n)
+{
+ bool old_n_static;
+ bool new_n_static;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ n->flags);
+
+ old_n_static = zebra_evpn_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ zebra_evpn_neigh_start_hold_timer(n);
+ new_n_static = zebra_evpn_neigh_is_static(n);
+
+ if (old_n_static != new_n_static)
+ zebra_evpn_sync_neigh_static_chg(
+ n, old_n_static, new_n_static, false /*defer-dp*/,
+ false /*defer_mac_dp*/, __func__);
+}
+
+zebra_neigh_t *
+zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, esi_t *esi,
+ struct sync_mac_ip_ctx *ctx)
+{
+ struct interface *ifp = NULL;
+ bool is_router;
+ zebra_mac_t *mac = ctx->mac;
+ uint32_t tmp_seq;
+ bool old_router = false;
+ bool old_bgp_ready = false;
+ bool new_bgp_ready;
+ bool inform_dataplane = false;
+ bool inform_bgp = false;
+ bool old_mac_static;
+ bool new_mac_static;
+ bool set_dp_inactive = false;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool created;
+ ifindex_t ifindex = 0;
+
+ /* locate l3-svi */
+ ifp = zevpn_map_to_svi(zevpn);
+ if (ifp)
+ ifindex = ifp->ifindex;
+
+ is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+ old_mac_static = zebra_evpn_mac_is_static(mac);
+
+ if (!n) {
+ uint32_t n_flags = 0;
+
+ /* New neighbor - create */
+ SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL);
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT))
+ SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY);
+ else
+ SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+ SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE);
+
+ n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, mac,
+ n_flags);
+ n->ifindex = ifindex;
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+
+ created = true;
+ inform_dataplane = true;
+ inform_bgp = true;
+ set_dp_inactive = true;
+ } else {
+ bool mac_change;
+ uint32_t old_flags = n->flags;
+ bool old_n_static;
+ bool new_n_static;
+
+ created = false;
+ old_n_static = zebra_evpn_neigh_is_static(n);
+ old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ old_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN);
+
+ /* deref and clear old info */
+ if (mac_change) {
+ if (old_bgp_ready) {
+ zebra_evpn_neigh_send_del_to_client(
+ zevpn->vni, &n->ip, &n->emac, n->flags,
+ n->state, false /*force*/);
+ old_bgp_ready = false;
+ }
+ if (n->mac)
+ zebra_evpn_local_neigh_deref_mac(
+ n, false /*send_mac_update*/);
+ }
+ /* clear old fwd info */
+ n->rem_seq = 0;
+ n->r_vtep_ip.s_addr = 0;
+
+ /* setup new flags */
+ n->flags = 0;
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ /* retain activity flag if the neigh was
+ * previously local
+ */
+ if (old_flags & ZEBRA_NEIGH_LOCAL) {
+ n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE);
+ } else {
+ inform_dataplane = true;
+ set_dp_inactive = true;
+ n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE;
+ }
+
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT))
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY);
+ else
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) {
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY);
+ /* if the neigh was peer-active previously we
+ * need to keep the flag and start the
+ * holdtimer on it. the peer-active flag is
+ * cleared on holdtimer expiry.
+ */
+ if (CHECK_FLAG(old_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) {
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+ zebra_evpn_neigh_start_hold_timer(n);
+ }
+ } else {
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+ /* stop hold timer if a peer has verified
+ * reachability
+ */
+ zebra_evpn_neigh_stop_hold_timer(n);
+ }
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags))
+ zlog_debug(
+ "sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ old_flags, n->flags);
+
+ new_n_static = zebra_evpn_neigh_is_static(n);
+ if (mac_change) {
+ set_dp_inactive = true;
+ n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE;
+ inform_dataplane = true;
+ zebra_evpn_local_neigh_ref_mac(
+ n, &mac->macaddr, mac,
+ false /*send_mac_update*/);
+ } else if (old_n_static != new_n_static) {
+ inform_dataplane = true;
+ /* if static flags have changed without a mac change
+ * we need to create the correct sync-refs against
+ * the existing mac
+ */
+ zebra_evpn_sync_neigh_static_chg(
+ n, old_n_static, new_n_static,
+ true /*defer_dp*/, true /*defer_mac_dp*/,
+ __func__);
+ }
+
+ /* Update the forwarding info. */
+ if (n->ifindex != ifindex) {
+ n->ifindex = ifindex;
+ inform_dataplane = true;
+ }
+ }
+
+ /* update the neigh seq. we don't bother with the mac seq as
+ * sync_mac_update already took care of that
+ */
+ tmp_seq = MAX(n->loc_seq, seq);
+ if (tmp_seq != n->loc_seq) {
+ n->loc_seq = tmp_seq;
+ inform_bgp = true;
+ }
+
+ /* Mark Router flag (R-bit) */
+ if (is_router)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+ else
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ if (old_router != is_router)
+ inform_dataplane = true;
+
+ new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ if (old_bgp_ready != new_bgp_ready)
+ inform_bgp = true;
+
+ new_mac_static = zebra_evpn_mac_is_static(mac);
+ if ((old_mac_static != new_mac_static) || ctx->mac_dp_update_deferred)
+ zebra_evpn_sync_mac_dp_install(mac, ctx->mac_inactive,
+ false /* force_clear_static */,
+ __func__);
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s",
+ created ? "created" : "updated", n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags,
+ inform_bgp ? " inform_bgp" : "",
+ inform_dataplane ? " inform_dp" : "");
+
+ if (inform_dataplane)
+ zebra_evpn_sync_neigh_dp_install(n, set_dp_inactive,
+ false /* force_clear_static */,
+ __func__);
+
+ if (inform_bgp)
+ zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready,
+ new_bgp_ready);
+
+ return n;
+}
+
+/*
+ * Uninstall remote neighbor from the kernel.
+ */
+static int zebra_evpn_neigh_uninstall(zebra_evpn_t *zevpn, zebra_neigh_t *n)
+{
+ struct interface *vlan_if;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE))
+ return 0;
+
+ vlan_if = zevpn_map_to_svi(zevpn);
+ if (!vlan_if)
+ return -1;
+
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ n->loc_seq = 0;
+
+ dplane_rem_neigh_delete(vlan_if, &n->ip);
+
+ return 0;
+}
+
+/*
+ * Free neighbor hash entry (callback)
+ */
+static void zebra_evpn_neigh_del_hash_entry(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct neigh_walk_ctx *wctx = arg;
+ zebra_neigh_t *n = bucket->data;
+
+ if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL))
+ || ((wctx->flags & DEL_REMOTE_NEIGH)
+ && (n->flags & ZEBRA_NEIGH_REMOTE))
+ || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP)
+ && (n->flags & ZEBRA_NEIGH_REMOTE)
+ && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) {
+ if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL))
+ zebra_evpn_neigh_send_del_to_client(
+ wctx->zevpn->vni, &n->ip, &n->emac, n->flags,
+ n->state, false /*force*/);
+
+ if (wctx->uninstall) {
+ if (zebra_evpn_neigh_is_static(n))
+ zebra_evpn_sync_neigh_dp_install(
+ n, false /* set_inactive */,
+ true /* force_clear_static */,
+ __func__);
+ if ((n->flags & ZEBRA_NEIGH_REMOTE))
+ zebra_evpn_neigh_uninstall(wctx->zevpn, n);
+ }
+
+ zebra_evpn_neigh_del(wctx->zevpn, n);
+ }
+
+ return;
+}
+
+/*
+ * Delete all neighbor entries for this EVPN.
+ */
+void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall,
+ int upd_client, uint32_t flags)
+{
+ struct neigh_walk_ctx wctx;
+
+ if (!zevpn->neigh_table)
+ return;
+
+ memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ wctx.zevpn = zevpn;
+ wctx.uninstall = uninstall;
+ wctx.upd_client = upd_client;
+ wctx.flags = flags;
+
+ hash_iterate(zevpn->neigh_table, zebra_evpn_neigh_del_hash_entry,
+ &wctx);
+}
+
+/*
+ * Look up neighbor hash entry.
+ */
+zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip)
+{
+ zebra_neigh_t tmp;
+ zebra_neigh_t *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(zevpn->neigh_table, &tmp);
+
+ return n;
+}
+
+/*
+ * Process all neighbors associated with a MAC upon the MAC being learnt
+ * locally or undergoing any other change (such as sequence number).
+ */
+void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac,
+ bool seq_change,
+ bool es_change)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ struct zebra_vrf *zvrf = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Processing neighbors on local MAC %s %s, VNI %u",
+ prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+ seq_change ? "CHANGE" : "ADD", zevpn->vni);
+
+ /* Walk all neighbors and mark any inactive local neighbors as
+ * active and/or update sequence number upon a move, and inform BGP.
+ * The action for remote neighbors is TBD.
+ * NOTE: We can't simply uninstall remote neighbors as the kernel may
+ * accidentally end up deleting a just-learnt local neighbor.
+ */
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change
+ || es_change) {
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ n->loc_seq = zmac->loc_seq;
+ if (!(zvrf->dup_addr_detect && zvrf->dad_freeze
+ && !!CHECK_FLAG(n->flags,
+ ZEBRA_NEIGH_DUPLICATE)))
+ zebra_evpn_neigh_send_add_to_client(
+ zevpn->vni, &n->ip, &n->emac,
+ n->mac, n->flags, n->loc_seq);
+ }
+ }
+ }
+}
+
+/*
+ * Process all neighbors associated with a local MAC upon the MAC being
+ * deleted.
+ */
+void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u",
+ prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+ zevpn->vni);
+
+ /* Walk all local neighbors and mark as inactive and inform
+ * BGP, if needed.
+ * TBD: There is currently no handling for remote neighbors. We
+ * don't expect them to exist, if they do, do we install the MAC
+ * as a remote MAC and the neighbor as remote?
+ */
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ n->loc_seq = 0;
+ zebra_evpn_neigh_send_del_to_client(
+ zevpn->vni, &n->ip, &n->emac, n->flags,
+ ZEBRA_NEIGH_ACTIVE, false /*force*/);
+ }
+ }
+ }
+}
+
+/*
+ * Process all neighbors associated with a MAC upon the MAC being remotely
+ * learnt.
+ */
+void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u",
+ prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
+ zevpn->vni);
+
+ /* Walk all local neighbors and mark as inactive and inform
+ * BGP, if needed.
+ */
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ n->loc_seq = 0;
+ zebra_evpn_neigh_send_del_to_client(
+ zevpn->vni, &n->ip, &n->emac, n->flags,
+ ZEBRA_NEIGH_ACTIVE, false /* force */);
+ }
+ }
+ }
+}
+
+/*
+ * Process all neighbors associated with a remote MAC upon the MAC being
+ * deleted.
+ */
+void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac)
+{
+ /* NOTE: Currently a NO-OP. */
+}
+
+static inline void zebra_evpn_local_neigh_update_log(
+ const char *pfx, zebra_neigh_t *n, bool is_router, bool local_inactive,
+ bool old_bgp_ready, bool new_bgp_ready, bool inform_dataplane,
+ bool inform_bgp, const char *sfx)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ return;
+
+ zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", pfx,
+ n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), n->flags,
+ is_router ? " router" : "",
+ local_inactive ? " local-inactive" : "",
+ old_bgp_ready ? " old_bgp_ready" : "",
+ new_bgp_ready ? " new_bgp_ready" : "",
+ inform_dataplane ? " inform_dp" : "",
+ inform_bgp ? " inform_bgp" : "", sfx);
+}
+
+/* As part Duplicate Address Detection (DAD) for IP mobility
+ * MAC binding changes, ensure to inherit duplicate flag
+ * from MAC.
+ */
+static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf,
+ zebra_mac_t *old_zmac,
+ zebra_mac_t *new_zmac,
+ zebra_neigh_t *nbr)
+{
+ bool is_old_mac_dup = false;
+ bool is_new_mac_dup = false;
+
+ if (!zvrf->dup_addr_detect)
+ return 0;
+ /* Check old or new MAC is detected as duplicate
+ * mark this neigh as duplicate
+ */
+ if (old_zmac)
+ is_old_mac_dup =
+ CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE);
+ if (new_zmac)
+ is_new_mac_dup =
+ CHECK_FLAG(new_zmac->flags, ZEBRA_MAC_DUPLICATE);
+ /* Old and/or new MAC can be in duplicate state,
+ * based on that IP/Neigh Inherits the flag.
+ * If New MAC is marked duplicate, inherit to the IP.
+ * If old MAC is duplicate but new MAC is not, clear
+ * duplicate flag for IP and reset detection params
+ * and let IP DAD retrigger.
+ */
+ if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+ SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+ /* Capture Duplicate detection time */
+ nbr->dad_dup_detect_time = monotime(NULL);
+ /* Mark neigh inactive */
+ ZEBRA_NEIGH_SET_INACTIVE(nbr);
+
+ return 1;
+ } else if (is_old_mac_dup && !is_new_mac_dup) {
+ UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+ nbr->dad_count = 0;
+ nbr->detect_start_time.tv_sec = 0;
+ nbr->detect_start_time.tv_usec = 0;
+ }
+ return 0;
+}
+
+static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t)
+{
+ struct zebra_vrf *zvrf = NULL;
+ zebra_neigh_t *nbr = NULL;
+ zebra_evpn_t *zevpn = NULL;
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[ETHER_ADDR_STRLEN];
+
+ nbr = THREAD_ARG(t);
+
+ /* since this is asynchronous we need sanity checks*/
+ zvrf = vrf_info_lookup(nbr->zevpn->vrf_id);
+ if (!zvrf)
+ return 0;
+
+ zevpn = zebra_evpn_lookup(nbr->zevpn->vni);
+ if (!zevpn)
+ return 0;
+
+ nbr = zebra_evpn_neigh_lookup(zevpn, &nbr->ip);
+ if (!nbr)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired",
+ __func__,
+ prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)),
+ ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags,
+ nbr->dad_count, zevpn->vni);
+
+ UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+ nbr->dad_count = 0;
+ nbr->detect_start_time.tv_sec = 0;
+ nbr->detect_start_time.tv_usec = 0;
+ nbr->dad_dup_detect_time = 0;
+ nbr->dad_ip_auto_recovery_timer = NULL;
+ ZEBRA_NEIGH_SET_ACTIVE(nbr);
+
+ /* Send to BGP */
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
+ zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip,
+ &nbr->emac, nbr->mac,
+ nbr->flags, nbr->loc_seq);
+ } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/);
+ }
+
+ return 0;
+}
+
+static void
+zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
+ struct in_addr vtep_ip, bool do_dad,
+ bool *is_dup_detect, bool is_local)
+{
+
+ struct timeval elapsed = {0, 0};
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ bool reset_params = false;
+
+ if (!zvrf->dup_addr_detect)
+ return;
+
+ /* IP is detected as duplicate or inherit dup
+ * state, hold on to install as remote entry
+ * only if freeze is enabled.
+ */
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u",
+ __func__,
+ prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
+ ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ nbr->flags, nbr->dad_count,
+ zvrf->dad_freeze_time);
+
+ if (zvrf->dad_freeze)
+ *is_dup_detect = true;
+
+ /* warn-only action, neigh will be installed.
+ * freeze action, it wil not be installed.
+ */
+ return;
+ }
+
+ if (!do_dad)
+ return;
+
+ /* Check if detection time (M-secs) expired.
+ * Reset learn count and detection start time.
+ * During remote mac add, count should already be 1
+ * via local learning.
+ */
+ monotime_since(&nbr->detect_start_time, &elapsed);
+ reset_params = (elapsed.tv_sec > zvrf->dad_time);
+
+ if (is_local && !reset_params) {
+ /* RFC-7432: A PE/VTEP that detects a MAC mobility
+ * event via LOCAL learning starts an M-second timer.
+ *
+ * NOTE: This is the START of the probe with count is
+ * 0 during LOCAL learn event.
+ */
+ reset_params = !nbr->dad_count;
+ }
+
+ if (reset_params) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u",
+ __func__,
+ prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
+ ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ nbr->flags, nbr->dad_count);
+ /* Reset learn count but do not start detection
+ * during REMOTE learn event.
+ */
+ nbr->dad_count = 0;
+ /* Start dup. addr detection (DAD) start time,
+ * ONLY during LOCAL learn.
+ */
+ if (is_local)
+ monotime(&nbr->detect_start_time);
+
+ } else if (!is_local) {
+ /* For REMOTE IP/Neigh, increment detection count
+ * ONLY while in probe window, once window passed,
+ * next local learn event should trigger DAD.
+ */
+ nbr->dad_count++;
+ }
+
+ /* For LOCAL IP/Neigh learn event, once count is reset above via either
+ * initial/start detection time or passed the probe time, the count
+ * needs to be incremented.
+ */
+ if (is_local)
+ nbr->dad_count++;
+
+ if (nbr->dad_count >= zvrf->dad_max_moves) {
+ flog_warn(
+ EC_ZEBRA_DUP_IP_DETECTED,
+ "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s",
+ nbr->zevpn->vni,
+ prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
+ ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ is_local ? "local update, last" : "remote update, from",
+ inet_ntoa(vtep_ip));
+
+ SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+
+ /* Capture Duplicate detection time */
+ nbr->dad_dup_detect_time = monotime(NULL);
+
+ /* Start auto recovery timer for this IP */
+ THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
+ if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start",
+ __func__,
+ prefix_mac2str(&nbr->emac, buf,
+ sizeof(buf)),
+ ipaddr2str(&nbr->ip, buf1,
+ sizeof(buf1)),
+ nbr->flags, zvrf->dad_freeze_time);
+
+ thread_add_timer(zrouter.master,
+ zebra_evpn_dad_ip_auto_recovery_exp,
+ nbr, zvrf->dad_freeze_time,
+ &nbr->dad_ip_auto_recovery_timer);
+ }
+ if (zvrf->dad_freeze)
+ *is_dup_detect = true;
+ }
+}
+
+int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
+ struct ipaddr *ip, struct ethaddr *macaddr,
+ bool is_router, bool local_inactive,
+ bool dp_static)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct zebra_vrf *zvrf;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL, *old_zmac = NULL;
+ uint32_t old_mac_seq = 0, mac_new_seq = 0;
+ bool upd_mac_seq = false;
+ bool neigh_mac_change = false;
+ bool neigh_on_hold = false;
+ bool neigh_was_remote = false;
+ bool do_dad = false;
+ struct in_addr vtep_ip = {.s_addr = 0};
+ bool inform_dataplane = false;
+ bool created = false;
+ bool new_static = false;
+ bool old_bgp_ready = false;
+ bool new_bgp_ready;
+
+ /* Check if the MAC exists. */
+ zmac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!zmac) {
+ /* create a dummy MAC if the MAC is not already present */
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("AUTO MAC %s created for neigh %s on VNI %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)),
+ zevpn->vni);
+
+ zmac = zebra_evpn_mac_add(zevpn, macaddr);
+ if (!zmac) {
+ zlog_debug("Failed to add MAC %s VNI %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zevpn->vni);
+ return -1;
+ }
+
+ memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info));
+ memset(&zmac->flags, 0, sizeof(uint32_t));
+ SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO);
+ } else {
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
+ /*
+ * We don't change the MAC to local upon a neighbor
+ * learn event, we wait for the explicit local MAC
+ * learn. However, we have to compute its sequence
+ * number in preparation for when it actually turns
+ * local.
+ */
+ upd_mac_seq = true;
+ }
+ }
+
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
+ if (!zvrf) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(" Unable to find vrf for: %d",
+ zevpn->vxlan_if->vrf_id);
+ return -1;
+ }
+
+ /* Check if the neighbor exists. */
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
+ if (!n) {
+ /* New neighbor - create */
+ n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0);
+ if (!n) {
+ flog_err(
+ EC_ZEBRA_MAC_ADD_FAILED,
+ "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
+ ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, zevpn->vni);
+ return -1;
+ }
+ /* Set "local" forwarding info. */
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ n->ifindex = ifp->ifindex;
+ created = true;
+ } else {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ bool mac_different;
+ bool cur_is_router;
+ bool old_local_inactive;
+
+ old_local_inactive = !!CHECK_FLAG(
+ n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE);
+
+ old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+
+ /* Note any changes and see if of interest to BGP. */
+ mac_different = !!memcmp(&n->emac, macaddr, ETH_ALEN);
+ cur_is_router =
+ !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+ new_static = zebra_evpn_neigh_is_static(n);
+ if (!mac_different && is_router == cur_is_router
+ && old_local_inactive == local_inactive
+ && dp_static != new_static) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ " Ignoring entry mac is the same and is_router == cur_is_router");
+ n->ifindex = ifp->ifindex;
+ return 0;
+ }
+
+ old_zmac = n->mac;
+ if (!mac_different) {
+ /* XXX - cleanup this code duplication */
+ bool is_neigh_freezed = false;
+
+ /* Only the router flag has changed. */
+ if (is_router)
+ SET_FLAG(n->flags,
+ ZEBRA_NEIGH_ROUTER_FLAG);
+ else
+ UNSET_FLAG(n->flags,
+ ZEBRA_NEIGH_ROUTER_FLAG);
+
+ if (local_inactive)
+ SET_FLAG(n->flags,
+ ZEBRA_NEIGH_LOCAL_INACTIVE);
+ else
+ UNSET_FLAG(n->flags,
+ ZEBRA_NEIGH_LOCAL_INACTIVE);
+ new_bgp_ready =
+ zebra_evpn_neigh_is_ready_for_bgp(n);
+
+ /* Neigh is in freeze state and freeze action
+ * is enabled, do not send update to client.
+ */
+ is_neigh_freezed =
+ (zvrf->dup_addr_detect
+ && zvrf->dad_freeze
+ && CHECK_FLAG(n->flags,
+ ZEBRA_NEIGH_DUPLICATE));
+
+ zebra_evpn_local_neigh_update_log(
+ "local", n, is_router, local_inactive,
+ old_bgp_ready, new_bgp_ready, false,
+ false, "flag-update");
+
+ /* if the neigh can no longer be advertised
+ * remove it from bgp
+ */
+ if (!is_neigh_freezed) {
+ zebra_evpn_neigh_send_add_del_to_client(
+ n, old_bgp_ready,
+ new_bgp_ready);
+ } else {
+ if (IS_ZEBRA_DEBUG_VXLAN
+ && IS_ZEBRA_NEIGH_ACTIVE(n))
+ zlog_debug(
+ " Neighbor active and frozen");
+ }
+ return 0;
+ }
+
+ /* The MAC has changed, need to issue a delete
+ * first as this means a different MACIP route.
+ * Also, need to do some unlinking/relinking.
+ * We also need to update the MAC's sequence number
+ * in different situations.
+ */
+ if (old_bgp_ready) {
+ zebra_evpn_neigh_send_del_to_client(
+ zevpn->vni, &n->ip, &n->emac, n->flags,
+ n->state, false /*force*/);
+ old_bgp_ready = false;
+ }
+ if (old_zmac) {
+ old_mac_seq = CHECK_FLAG(old_zmac->flags,
+ ZEBRA_MAC_REMOTE)
+ ? old_zmac->rem_seq
+ : old_zmac->loc_seq;
+ neigh_mac_change = upd_mac_seq = true;
+ zebra_evpn_local_neigh_deref_mac(
+ n, true /* send_mac_update */);
+ }
+
+ /* if mac changes abandon peer flags and tell
+ * dataplane to clear the static flag
+ */
+ if (zebra_evpn_neigh_clear_sync_info(n))
+ inform_dataplane = true;
+ /* Update the forwarding info. */
+ n->ifindex = ifp->ifindex;
+
+ /* Link to new MAC */
+ zebra_evpn_local_neigh_ref_mac(
+ n, macaddr, zmac, true /* send_mac_update */);
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ /*
+ * Neighbor has moved from remote to local. Its
+ * MAC could have also changed as part of the move.
+ */
+ if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN)
+ != 0) {
+ old_zmac = n->mac;
+ if (old_zmac) {
+ old_mac_seq =
+ CHECK_FLAG(old_zmac->flags,
+ ZEBRA_MAC_REMOTE)
+ ? old_zmac->rem_seq
+ : old_zmac->loc_seq;
+ neigh_mac_change = upd_mac_seq = true;
+ zebra_evpn_local_neigh_deref_mac(
+ n, true /* send_update */);
+ }
+
+ /* Link to new MAC */
+ zebra_evpn_local_neigh_ref_mac(
+ n, macaddr, zmac, true /*send_update*/);
+ }
+ /* Based on Mobility event Scenario-B from the
+ * draft, neigh's previous state was remote treat this
+ * event for DAD.
+ */
+ neigh_was_remote = true;
+ vtep_ip = n->r_vtep_ip;
+ /* Mark appropriately */
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ n->r_vtep_ip.s_addr = INADDR_ANY;
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ n->ifindex = ifp->ifindex;
+ }
+ }
+
+ /* If MAC was previously remote, or the neighbor had a different
+ * MAC earlier, recompute the sequence number.
+ */
+ if (upd_mac_seq) {
+ uint32_t seq1, seq2;
+
+ seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)
+ ? zmac->rem_seq + 1
+ : zmac->loc_seq;
+ seq2 = neigh_mac_change ? old_mac_seq + 1 : 0;
+ mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? MAX(seq1, seq2)
+ : zmac->loc_seq;
+ }
+
+ if (local_inactive)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE);
+ else
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE);
+
+ /* Mark Router flag (R-bit) */
+ if (is_router)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+ else
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ /* if the dataplane thinks that this is a sync entry but
+ * zebra doesn't we need to re-concile the diff
+ * by re-installing the dataplane entry
+ */
+ if (dp_static) {
+ new_static = zebra_evpn_neigh_is_static(n);
+ if (!new_static)
+ inform_dataplane = true;
+ }
+
+ /* Check old and/or new MAC detected as duplicate mark
+ * the neigh as duplicate
+ */
+ if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) {
+ flog_warn(
+ EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+ "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC",
+ zevpn->vni, prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ }
+
+ /* For IP Duplicate Address Detection (DAD) is trigger,
+ * when the event is extended mobility based on scenario-B
+ * from the draft, IP/Neigh's MAC binding changed and
+ * neigh's previous state was remote.
+ */
+ if (neigh_mac_change && neigh_was_remote)
+ do_dad = true;
+
+ zebra_evpn_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad,
+ &neigh_on_hold, true);
+
+ if (inform_dataplane)
+ zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ /* Before we program this in BGP, we need to check if MAC is locally
+ * learnt. If not, force neighbor to be inactive and reset its seq.
+ */
+ if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) {
+ zebra_evpn_local_neigh_update_log(
+ "local", n, is_router, local_inactive, false, false,
+ inform_dataplane, false, "auto-mac");
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ n->loc_seq = 0;
+ zmac->loc_seq = mac_new_seq;
+ return 0;
+ }
+
+ zebra_evpn_local_neigh_update_log("local", n, is_router, local_inactive,
+ false, false, inform_dataplane, true,
+ created ? "created" : "updated");
+
+ /* If the MAC's sequence number has changed, inform the MAC and all
+ * neighbors associated with the MAC to BGP, else just inform this
+ * neighbor.
+ */
+ if (upd_mac_seq && zmac->loc_seq != mac_new_seq) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Seq changed for MAC %s VNI %u - old %u new %u",
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zevpn->vni, zmac->loc_seq, mac_new_seq);
+ zmac->loc_seq = mac_new_seq;
+ if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr,
+ zmac->flags,
+ zmac->loc_seq, zmac->es))
+ return -1;
+ zebra_evpn_process_neigh_on_local_mac_change(zevpn, zmac, 1,
+ 0 /*es_change*/);
+ return 0;
+ }
+
+ n->loc_seq = zmac->loc_seq;
+
+ if (!neigh_on_hold) {
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready,
+ new_bgp_ready);
+ } else {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(" Neighbor on hold not sending");
+ }
+ return 0;
+}
+
+int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
+ struct ipaddr *ip, struct ethaddr *macaddr,
+ uint16_t state)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL;
+
+ /* If the neighbor is unknown, there is no further action. */
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
+ if (!n)
+ return 0;
+
+ /* If a remote entry, see if it needs to be refreshed */
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+#ifdef GNU_LINUX
+ if (state & NUD_STALE)
+ zebra_evpn_rem_neigh_install(zevpn, n,
+ false /*was_static*/);
+#endif
+ } else {
+ /* We got a "remote" neighbor notification for an entry
+ * we think is local. This can happen in a multihoming
+ * scenario - but only if the MAC is already "remote".
+ * Just mark our entry as "remote".
+ */
+ zmac = zebra_evpn_mac_lookup(zevpn, macaddr);
+ if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
+ zlog_debug(
+ "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zevpn->vni);
+ return -1;
+ }
+
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ n->r_vtep_ip = zmac->fwd_info.r_vtep_ip;
+ }
+
+ return 0;
+}
+
+/* Notify Neighbor entries to the Client, skips the GW entry */
+static void
+zebra_evpn_send_neigh_hash_entry_to_client(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_neigh_t *zn = bucket->data;
+ zebra_mac_t *zmac = NULL;
+
+ if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW))
+ return;
+
+ if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL)
+ && IS_ZEBRA_NEIGH_ACTIVE(zn)) {
+ zmac = zebra_evpn_mac_lookup(wctx->zevpn, &zn->emac);
+ if (!zmac)
+ return;
+
+ zebra_evpn_neigh_send_add_to_client(wctx->zevpn->vni, &zn->ip,
+ &zn->emac, zn->mac,
+ zn->flags, zn->loc_seq);
+ }
+}
+
+/* Iterator of a specific EVPN */
+void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn)
+{
+ struct neigh_walk_ctx wctx;
+
+ memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ wctx.zevpn = zevpn;
+
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_send_neigh_hash_entry_to_client, &wctx);
+}
+
+void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ struct neigh_walk_ctx *wctx = ctxt;
+ zebra_neigh_t *nbr;
+ zebra_evpn_t *zevpn;
+ char buf[INET6_ADDRSTRLEN];
+
+ nbr = (zebra_neigh_t *)bucket->data;
+ if (!nbr)
+ return;
+
+ zevpn = wctx->zevpn;
+
+ if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+ return;
+
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ ipaddr2str(&nbr->ip, buf, sizeof(buf));
+ zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u",
+ __func__, buf, nbr->flags, nbr->loc_seq);
+ }
+
+ UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
+ nbr->dad_count = 0;
+ nbr->detect_start_time.tv_sec = 0;
+ nbr->detect_start_time.tv_usec = 0;
+ nbr->dad_dup_detect_time = 0;
+ THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
+
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
+ zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip,
+ &nbr->emac, nbr->mac,
+ nbr->flags, nbr->loc_seq);
+ } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/);
+ }
+}
+
+/*
+ * Print a specific neighbor entry.
+ */
+void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
+{
+ struct vty *vty;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ const char *type_str;
+ const char *state_str;
+ bool flags_present = false;
+ struct zebra_vrf *zvrf = NULL;
+ struct timeval detect_start_time = {0, 0};
+ char timebuf[MONOTIME_STRLEN];
+ char thread_buf[THREAD_TIMER_STRLEN];
+
+ zvrf = zebra_vrf_get_evpn();
+ if (!zvrf)
+ return;
+
+ ipaddr2str(&n->ip, buf2, sizeof(buf2));
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1));
+ type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? "local" : "remote";
+ state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
+ vty = (struct vty *)ctxt;
+ if (json == NULL) {
+ bool sync_info = false;
+
+ vty_out(vty, "IP: %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " State: %s\n", state_str);
+ vty_out(vty, " MAC: %s\n",
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ vty_out(vty, " Sync-info:");
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) {
+ vty_out(vty, " local-inactive");
+ sync_info = true;
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) {
+ vty_out(vty, " peer-proxy");
+ sync_info = true;
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) {
+ vty_out(vty, " peer-active");
+ sync_info = true;
+ }
+ if (n->hold_timer) {
+ vty_out(vty, " (ht: %s)",
+ thread_timer_to_hhmmss(thread_buf,
+ sizeof(thread_buf),
+ n->hold_timer));
+ sync_info = true;
+ }
+ if (!sync_info)
+ vty_out(vty, " -");
+ vty_out(vty, "\n");
+ } else {
+ json_object_string_add(json, "ip", buf2);
+ json_object_string_add(json, "type", type_str);
+ json_object_string_add(json, "state", state_str);
+ json_object_string_add(json, "mac", buf1);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE))
+ json_object_boolean_true_add(json, "localInactive");
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY))
+ json_object_boolean_true_add(json, "peerProxy");
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ json_object_boolean_true_add(json, "peerActive");
+ if (n->hold_timer)
+ json_object_string_add(
+ json, "peerActiveHold",
+ thread_timer_to_hhmmss(thread_buf,
+ sizeof(thread_buf),
+ n->hold_timer));
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ if (n->mac->es) {
+ if (json)
+ json_object_string_add(json, "remoteEs",
+ n->mac->es->esi_str);
+ else
+ vty_out(vty, " Remote ES: %s\n",
+ n->mac->es->esi_str);
+ } else {
+ if (json)
+ json_object_string_add(json, "remoteVtep",
+ inet_ntoa(n->r_vtep_ip));
+ else
+ vty_out(vty, " Remote VTEP: %s\n",
+ inet_ntoa(n->r_vtep_ip));
+ }
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) {
+ if (!json) {
+ vty_out(vty, " Flags: Default-gateway");
+ flags_present = true;
+ } else
+ json_object_boolean_true_add(json, "defaultGateway");
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) {
+ if (!json) {
+ vty_out(vty,
+ flags_present ? " ,Router" : " Flags: Router");
+ flags_present = true;
+ }
+ }
+ if (json == NULL) {
+ if (flags_present)
+ vty_out(vty, "\n");
+ vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq,
+ n->rem_seq);
+
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
+ vty_out(vty, " Duplicate, detected at %s",
+ time_to_string(n->dad_dup_detect_time,
+ timebuf));
+ } else if (n->dad_count) {
+ monotime_since(&n->detect_start_time,
+ &detect_start_time);
+ if (detect_start_time.tv_sec <= zvrf->dad_time) {
+ time_to_string(n->detect_start_time.tv_sec,
+ timebuf);
+ vty_out(vty,
+ " Duplicate detection started at %s, detection count %u\n",
+ timebuf, n->dad_count);
+ }
+ }
+ } else {
+ json_object_int_add(json, "localSequence", n->loc_seq);
+ json_object_int_add(json, "remoteSequence", n->rem_seq);
+ json_object_int_add(json, "detectionCount", n->dad_count);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+ json_object_boolean_true_add(json, "isDuplicate");
+ else
+ json_object_boolean_false_add(json, "isDuplicate");
+ }
+}
+
+void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx)
+{
+ vty_out(vty, "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n");
+ vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", -wctx->addr_width,
+ "Neighbor", "Type", "Flags", "State", "MAC", "Remote ES/VTEP",
+ "Seq #'s");
+}
+
+static char *zebra_evpn_print_neigh_flags(zebra_neigh_t *n, char *flags_buf,
+ uint32_t flags_buf_sz)
+{
+ snprintf(flags_buf, flags_buf_sz, "%s%s%s",
+ (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ?
+ "P" : "",
+ (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ?
+ "X" : "",
+ (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ?
+ "I" : "");
+
+ return flags_buf;
+}
+
+/*
+ * Print neighbor hash entry - called for display of all neighbors.
+ */
+void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ struct vty *vty;
+ json_object *json_evpn = NULL, *json_row = NULL;
+ zebra_neigh_t *n;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct neigh_walk_ctx *wctx = ctxt;
+ const char *state_str;
+ char flags_buf[6];
+
+ vty = wctx->vty;
+ json_evpn = wctx->json;
+ n = (zebra_neigh_t *)bucket->data;
+
+ if (json_evpn)
+ json_row = json_object_new_object();
+
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1));
+ ipaddr2str(&n->ip, buf2, sizeof(buf2));
+ state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
+ return;
+
+ if (json_evpn == NULL) {
+ vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n",
+ -wctx->addr_width, buf2, "local",
+ zebra_evpn_print_neigh_flags(n, flags_buf,
+ sizeof(flags_buf)), state_str, buf1,
+ "", n->loc_seq, n->rem_seq);
+ } else {
+ json_object_string_add(json_row, "type", "local");
+ json_object_string_add(json_row, "state", state_str);
+ json_object_string_add(json_row, "mac", buf1);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
+ json_object_boolean_true_add(json_row,
+ "defaultGateway");
+ json_object_int_add(json_row, "localSequence",
+ n->loc_seq);
+ json_object_int_add(json_row, "remoteSequence",
+ n->rem_seq);
+ json_object_int_add(json_row, "detectionCount",
+ n->dad_count);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+ json_object_boolean_true_add(json_row,
+ "isDuplicate");
+ else
+ json_object_boolean_false_add(json_row,
+ "isDuplicate");
+ }
+ wctx->count++;
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
+ && !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))
+ return;
+
+ if (json_evpn == NULL) {
+ if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
+ && (wctx->count == 0))
+ zebra_evpn_print_neigh_hdr(vty, wctx);
+ vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n",
+ -wctx->addr_width, buf2, "remote",
+ zebra_evpn_print_neigh_flags(n, flags_buf,
+ sizeof(flags_buf)), state_str, buf1,
+ n->mac->es ? n->mac->es->esi_str
+ : inet_ntoa(n->r_vtep_ip),
+ n->loc_seq, n->rem_seq);
+ } else {
+ json_object_string_add(json_row, "type", "remote");
+ json_object_string_add(json_row, "state", state_str);
+ json_object_string_add(json_row, "mac", buf1);
+ if (n->mac->es)
+ json_object_string_add(json_row, "remoteEs",
+ n->mac->es->esi_str);
+ else
+ json_object_string_add(json_row, "remoteVtep",
+ inet_ntoa(n->r_vtep_ip));
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
+ json_object_boolean_true_add(json_row,
+ "defaultGateway");
+ json_object_int_add(json_row, "localSequence",
+ n->loc_seq);
+ json_object_int_add(json_row, "remoteSequence",
+ n->rem_seq);
+ json_object_int_add(json_row, "detectionCount",
+ n->dad_count);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+ json_object_boolean_true_add(json_row,
+ "isDuplicate");
+ else
+ json_object_boolean_false_add(json_row,
+ "isDuplicate");
+ }
+ wctx->count++;
+ }
+
+ if (json_evpn)
+ json_object_object_add(json_evpn, buf2, json_row);
+}
+
+/*
+ * Print neighbor hash entry in detail - called for display of all neighbors.
+ */
+void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt)
+{
+ struct vty *vty;
+ json_object *json_evpn = NULL, *json_row = NULL;
+ zebra_neigh_t *n;
+ char buf[INET6_ADDRSTRLEN];
+ struct neigh_walk_ctx *wctx = ctxt;
+
+ vty = wctx->vty;
+ json_evpn = wctx->json;
+ n = (zebra_neigh_t *)bucket->data;
+ if (!n)
+ return;
+
+ ipaddr2str(&n->ip, buf, sizeof(buf));
+ if (json_evpn)
+ json_row = json_object_new_object();
+
+ zebra_evpn_print_neigh(n, vty, json_row);
+
+ if (json_evpn)
+ json_object_object_add(json_evpn, buf, json_row);
+}
+
+void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt)
+{
+ zebra_neigh_t *nbr;
+
+ nbr = (zebra_neigh_t *)bucket->data;
+ if (!nbr)
+ return;
+
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+ zebra_evpn_print_neigh_hash(bucket, ctxt);
+}
+
+void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ zebra_neigh_t *nbr;
+
+ nbr = (zebra_neigh_t *)bucket->data;
+ if (!nbr)
+ return;
+
+ if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
+ zebra_evpn_print_neigh_hash_detail(bucket, ctxt);
+}
+
+void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ struct ipaddr *ipaddr, zebra_mac_t *mac,
+ struct in_addr vtep_ip, uint8_t flags,
+ uint32_t seq)
+{
+ zebra_neigh_t *n;
+ int update_neigh = 0;
+ uint32_t tmp_seq;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_mac_t *old_mac = NULL;
+ bool old_static = false;
+ bool do_dad = false;
+ bool is_dup_detect = false;
+ bool is_router;
+
+ assert(mac);
+ is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ /* Check if the remote neighbor itself is unknown or has a
+ * change. If so, create or update and then install the entry.
+ */
+ n = zebra_evpn_neigh_lookup(zevpn, ipaddr);
+ if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
+ || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)
+ || (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) != 0)
+ || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) || seq != n->rem_seq)
+ update_neigh = 1;
+
+ if (update_neigh) {
+ if (!n) {
+ n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr,
+ mac, 0);
+ if (!n) {
+ zlog_warn(
+ "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
+ ipaddr2str(ipaddr, buf1, sizeof(buf1)),
+ prefix_mac2str(&mac->macaddr, buf,
+ sizeof(buf)),
+ zevpn->vni, inet_ntoa(vtep_ip));
+ return;
+ }
+
+ } else {
+ const char *n_type;
+
+ /* When host moves but changes its (MAC,IP)
+ * binding, BGP may install a MACIP entry that
+ * corresponds to "older" location of the host
+ * in transient situations (because {IP1,M1}
+ * is a different route from {IP1,M2}). Check
+ * the sequence number and ignore this update
+ * if appropriate.
+ */
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ tmp_seq = n->loc_seq;
+ n_type = "local";
+ } else {
+ tmp_seq = n->rem_seq;
+ n_type = "remote";
+ }
+ if (seq < tmp_seq) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u",
+ zevpn->vni,
+ prefix_mac2str(&mac->macaddr,
+ buf,
+ sizeof(buf)),
+ " IP ",
+ ipaddr2str(ipaddr, buf1,
+ sizeof(buf1)),
+ n_type, tmp_seq);
+ return;
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ old_static = zebra_evpn_neigh_is_static(n);
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug(
+ "sync->remote neigh vni %u ip %s mac %s seq %d f0x%x",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, buf1,
+ sizeof(buf1)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ seq, n->flags);
+ zebra_evpn_neigh_clear_sync_info(n);
+ if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ zebra_evpn_mac_send_del_to_client(
+ zevpn->vni, &mac->macaddr,
+ mac->flags, false /*force*/);
+ }
+ if (memcmp(&n->emac, &mac->macaddr,
+ sizeof(struct ethaddr))
+ != 0) {
+ /* update neigh list for macs */
+ old_mac =
+ zebra_evpn_mac_lookup(zevpn, &n->emac);
+ if (old_mac) {
+ listnode_delete(old_mac->neigh_list, n);
+ n->mac = NULL;
+ zebra_evpn_deref_ip2mac(zevpn, old_mac);
+ }
+ n->mac = mac;
+ listnode_add_sort(mac->neigh_list, n);
+ memcpy(&n->emac, &mac->macaddr, ETH_ALEN);
+
+ /* Check Neigh's curent state is local
+ * (this is the case where neigh/host has moved
+ * from L->R) and check previous detction
+ * started via local learning.
+ *
+ * RFC-7432: A PE/VTEP that detects a MAC
+ * mobilit event via local learning starts
+ * an M-second timer.
+ * VTEP-IP or seq. change along is not
+ * considered for dup. detection.
+ *
+ * Mobilty event scenario-B IP-MAC binding
+ * changed.
+ */
+ if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+ && n->dad_count)
+ do_dad = true;
+ }
+ }
+
+ /* Set "remote" forwarding info. */
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS);
+ n->r_vtep_ip = vtep_ip;
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+
+ /* Set router flag (R-bit) to this Neighbor entry */
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG))
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+ else
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ /* Check old or new MAC detected as duplicate,
+ * inherit duplicate flag to this neigh.
+ */
+ if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_mac, mac, n)) {
+ flog_warn(
+ EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+ "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC",
+ zevpn->vni,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ ipaddr2str(&n->ip, buf1, sizeof(buf1)));
+ }
+
+ /* Check duplicate address detection for IP */
+ zebra_evpn_dup_addr_detect_for_neigh(
+ zvrf, n, n->r_vtep_ip, do_dad, &is_dup_detect, false);
+ /* Install the entry. */
+ if (!is_dup_detect)
+ zebra_evpn_rem_neigh_install(zevpn, n, old_static);
+ }
+
+ /* Update seq number. */
+ n->rem_seq = seq;
+}
+
+int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip, zebra_mac_t *mac)
+{
+ zebra_neigh_t *n;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ assert(mac);
+
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
+ if (!n) {
+ n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0);
+ if (!n) {
+ flog_err(
+ EC_ZEBRA_MAC_ADD_FAILED,
+ "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
+ ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, zevpn->vni);
+ return -1;
+ }
+ }
+
+ /* Set "local" forwarding info. */
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ memcpy(&n->emac, &mac->macaddr, ETH_ALEN);
+ n->ifindex = ifp->ifindex;
+
+ /* Only advertise in BGP if the knob is enabled */
+ if (advertise_gw_macip_enabled(zevpn)) {
+
+ SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
+ /* Set Router flag (R-bit) */
+ if (ip->ipa_type == IPADDR_V6)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
+ ifp->name, ifp->ifindex, zevpn->vni,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+
+ zebra_evpn_neigh_send_add_to_client(
+ zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq);
+ } else if (advertise_svi_macip_enabled(zevpn)) {
+
+ SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x",
+ ifp->name, ifp->ifindex, zevpn->vni,
+ prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+
+ zebra_evpn_neigh_send_add_to_client(
+ zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq);
+ }
+
+ return 0;
+}
+
+void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
+ struct zebra_vrf *zvrf, zebra_neigh_t *n,
+ zebra_mac_t *mac, struct ipaddr *ipaddr)
+{
+ char buf1[INET6_ADDRSTRLEN];
+
+ if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)
+ && CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
+ && (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) {
+ struct interface *vlan_if;
+
+ vlan_if = zevpn_map_to_svi(zevpn);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
+ __func__,
+ ipaddr2str(ipaddr, buf1, sizeof(buf1)),
+ n->flags, vlan_if ? vlan_if->name : "Unknown");
+ if (vlan_if)
+ neigh_read_specific_ip(ipaddr, vlan_if);
+ }
+
+ /* When the MAC changes for an IP, it is possible the
+ * client may update the new MAC before trying to delete the
+ * "old" neighbor (as these are two different MACIP routes).
+ * Do the delete only if the MAC matches.
+ */
+ if (!memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ zebra_evpn_sync_neigh_del(n);
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_evpn_neigh_uninstall(zevpn, n);
+ zebra_evpn_neigh_del(zevpn, n);
+ zebra_evpn_deref_ip2mac(zevpn, mac);
+ }
+ }
+}
+
+int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
+{
+ zebra_neigh_t *n;
+ zebra_mac_t *zmac;
+ bool old_bgp_ready;
+ bool new_bgp_ready;
+ char buf[INET6_ADDRSTRLEN];
+ char buf2[ETHER_ADDR_STRLEN];
+ struct zebra_vrf *zvrf;
+
+ /* If entry doesn't exist, nothing to do. */
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
+ if (!n)
+ return 0;
+
+ zmac = zebra_evpn_mac_lookup(zevpn, &n->emac);
+ if (!zmac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Trying to del a neigh %s without a mac %s on VNI %u",
+ ipaddr2str(ip, buf, sizeof(buf)),
+ prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ zevpn->vni);
+
+ return 0;
+ }
+
+ /* If it is a remote entry, the kernel has aged this out or someone has
+ * deleted it, it needs to be re-installed as Quagga is the owner.
+ */
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/);
+ return 0;
+ }
+
+ /* if this is a sync entry it cannot be dropped re-install it in
+ * the dataplane
+ */
+ old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ if (zebra_evpn_neigh_is_static(n)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, buf, sizeof(buf)),
+ prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ n->flags);
+
+ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE))
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE);
+ /* inform-bgp about change in local-activity if any */
+ new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
+ zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready,
+ new_bgp_ready);
+
+ /* re-install the entry in the kernel */
+ zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ return 0;
+ }
+
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
+ if (!zvrf) {
+ zlog_debug("%s: VNI %u vrf lookup failed.", __func__,
+ zevpn->vni);
+ return -1;
+ }
+
+ /* In case of feeze action, if local neigh is in duplicate state,
+ * Mark the Neigh as inactive before sending delete request to BGPd,
+ * If BGPd has remote entry, it will re-install
+ */
+ if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+
+ /* Remove neighbor from BGP. */
+ zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac,
+ n->flags, n->state,
+ false /* force */);
+
+ /* Delete this neighbor entry. */
+ zebra_evpn_neigh_del(zevpn, n);
+
+ /* see if the AUTO mac needs to be deleted */
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO)
+ && !listcount(zmac->neigh_list))
+ zebra_evpn_mac_del(zevpn, zmac);
+
+ return 0;
+}
diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h
new file mode 100644
index 0000000000..4b98266c86
--- /dev/null
+++ b/zebra/zebra_evpn_neigh.h
@@ -0,0 +1,294 @@
+/*
+ * Zebra EVPN Neighbor Data structures and definitions
+ * These are "internal" to this function.
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ * Copyright (C) 2020 Volta Networks.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; 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_EVPN_NEIGH_H
+#define _ZEBRA_EVPN_NEIGH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct zebra_neigh_t_ zebra_neigh_t;
+
+#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE)
+
+#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE)
+
+#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE
+
+#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE
+
+/*
+ * Neighbor hash table.
+ *
+ * This table contains the neighbors (IP to MAC bindings) pertaining to
+ * this VNI. This includes local neighbors learnt on the attached VLAN
+ * device that maps to this VNI as well as remote neighbors learnt and
+ * installed by BGP.
+ * Local neighbors will be known against the VLAN device (SVI); however,
+ * it is sufficient for zebra to maintain against the VNI. The correct
+ * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI).
+ */
+struct zebra_neigh_t_ {
+ /* IP address. */
+ struct ipaddr ip;
+
+ /* MAC address. */
+ struct ethaddr emac;
+
+ /* Back pointer to MAC. Only applicable to hosts in a L2-VNI. */
+ zebra_mac_t *mac;
+
+ /* Underlying interface. */
+ ifindex_t ifindex;
+
+ zebra_evpn_t *zevpn;
+
+ uint32_t flags;
+#define ZEBRA_NEIGH_LOCAL 0x01
+#define ZEBRA_NEIGH_REMOTE 0x02
+#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */
+#define ZEBRA_NEIGH_DEF_GW 0x08
+#define ZEBRA_NEIGH_ROUTER_FLAG 0x10
+#define ZEBRA_NEIGH_DUPLICATE 0x20
+#define ZEBRA_NEIGH_SVI_IP 0x40
+/* rxed from an ES peer */
+#define ZEBRA_NEIGH_ES_PEER_ACTIVE 0x80
+/* rxed from an ES peer as a proxy advertisement */
+#define ZEBRA_NEIGH_ES_PEER_PROXY 0x100
+/* We have not been able to independently establish that the host
+ * is local connected
+ */
+#define ZEBRA_NEIGH_LOCAL_INACTIVE 0x200
+#define ZEBRA_NEIGH_ALL_LOCAL_FLAGS \
+ (ZEBRA_NEIGH_LOCAL | ZEBRA_NEIGH_LOCAL_INACTIVE)
+#define ZEBRA_NEIGH_ALL_PEER_FLAGS \
+ (ZEBRA_NEIGH_ES_PEER_PROXY | ZEBRA_NEIGH_ES_PEER_ACTIVE)
+
+ enum zebra_neigh_state state;
+
+ /* Remote VTEP IP - applicable only for remote neighbors. */
+ struct in_addr r_vtep_ip;
+
+ /*
+ * Mobility sequence numbers associated with this entry. The rem_seq
+ * represents the sequence number from the client (BGP) for the most
+ * recent add or update of this entry while the loc_seq represents
+ * the sequence number informed (or to be informed) by zebra to BGP
+ * for this entry.
+ */
+ uint32_t rem_seq;
+ uint32_t loc_seq;
+
+ /* list of hosts pointing to this remote NH entry */
+ struct host_rb_tree_entry host_rb;
+
+ /* Duplicate ip detection */
+ uint32_t dad_count;
+
+ struct thread *dad_ip_auto_recovery_timer;
+
+ struct timeval detect_start_time;
+
+ time_t dad_dup_detect_time;
+
+ /* used for ageing out the PEER_ACTIVE flag */
+ struct thread *hold_timer;
+};
+
+/*
+ * Context for neighbor hash walk - used by callbacks.
+ */
+struct neigh_walk_ctx {
+ zebra_evpn_t *zevpn; /* VNI hash */
+ struct zebra_vrf *zvrf; /* VRF - for client notification. */
+ int uninstall; /* uninstall from kernel? */
+ int upd_client; /* uninstall from client? */
+
+ uint32_t flags;
+#define DEL_LOCAL_NEIGH 0x1
+#define DEL_REMOTE_NEIGH 0x2
+#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH)
+#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4
+#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8
+
+ struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */
+
+ struct vty *vty; /* Used by VTY handlers */
+ uint32_t count; /* Used by VTY handlers */
+ uint8_t addr_width; /* Used by VTY handlers */
+ struct json_object *json; /* Used for JSON Output */
+};
+
+/**************************** SYNC neigh handling **************************/
+static inline bool zebra_evpn_neigh_is_static(zebra_neigh_t *neigh)
+{
+ return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS);
+}
+
+static inline bool zebra_evpn_neigh_is_ready_for_bgp(zebra_neigh_t *n)
+{
+ bool mac_ready;
+ bool neigh_ready;
+
+ mac_ready = !!(n->mac->flags & ZEBRA_MAC_LOCAL);
+ neigh_ready =
+ ((n->flags & ZEBRA_NEIGH_LOCAL) && IS_ZEBRA_NEIGH_ACTIVE(n)
+ && (!(n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE)
+ || (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE)))
+ ? true
+ : false;
+
+ return mac_ready && neigh_ready;
+}
+
+static inline void zebra_evpn_neigh_stop_hold_timer(zebra_neigh_t *n)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+
+ if (!n->hold_timer)
+ return;
+
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ n->flags);
+ THREAD_OFF(n->hold_timer);
+}
+
+void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static,
+ bool new_n_static, bool defer_n_dp,
+ bool defer_mac_dp, const char *caller);
+
+static inline bool zebra_evpn_neigh_clear_sync_info(zebra_neigh_t *n)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool old_n_static = false;
+ bool new_n_static = false;
+
+ if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
+ zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear",
+ n->zevpn->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->flags);
+
+ old_n_static = zebra_evpn_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS);
+ new_n_static = zebra_evpn_neigh_is_static(n);
+ if (old_n_static != new_n_static)
+ zebra_evpn_sync_neigh_static_chg(
+ n, old_n_static, new_n_static,
+ true /*defer_dp)*/, false /*defer_mac_dp*/,
+ __func__);
+ }
+ zebra_evpn_neigh_stop_hold_timer(n);
+
+ /* if the neigh static flag changed inform that a dp
+ * re-install maybe needed
+ */
+ return old_n_static != new_n_static;
+}
+
+int remote_neigh_count(zebra_mac_t *zmac);
+
+int neigh_list_cmp(void *p1, void *p2);
+struct hash *zebra_neigh_db_create(const char *desc);
+uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn);
+void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt);
+int remote_neigh_count(zebra_mac_t *zmac);
+int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ bool was_static);
+void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt);
+int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
+ struct ethaddr *macaddr,
+ zebra_mac_t *zmac, uint32_t neigh_flags,
+ uint32_t seq);
+int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
+ struct ethaddr *macaddr, uint32_t flags,
+ int state, bool force);
+bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ struct ethaddr *macaddr, uint32_t seq);
+int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n);
+void zebra_evpn_sync_neigh_del(zebra_neigh_t *n);
+zebra_neigh_t *
+zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ uint16_t ipa_len, struct ipaddr *ipaddr,
+ uint8_t flags, uint32_t seq, esi_t *esi,
+ struct sync_mac_ip_ctx *ctx);
+void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall,
+ int upd_client, uint32_t flags);
+zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip);
+
+int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n,
+ bool was_static);
+void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac);
+void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac);
+void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac,
+ bool seq_change,
+ bool es_change);
+void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn,
+ zebra_mac_t *zmac);
+int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
+ struct ipaddr *ip, struct ethaddr *macaddr,
+ bool is_router, bool local_inactive,
+ bool dp_static);
+int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
+ struct ipaddr *ip, struct ethaddr *macaddr,
+ uint16_t state);
+void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn);
+void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
+void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx);
+void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt);
+void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket,
+ void *ctxt);
+void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
+ struct ipaddr *ipaddr, zebra_mac_t *mac,
+ struct in_addr vtep_ip, uint8_t flags,
+ uint32_t seq);
+int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
+ struct ipaddr *ip, zebra_mac_t *mac);
+void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
+ struct zebra_vrf *zvrf, zebra_neigh_t *n,
+ zebra_mac_t *mac, struct ipaddr *ipaddr);
+int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_ZEBRA_EVPN_NEIGH_H */
diff --git a/zebra/zebra_evpn_vxlan.h b/zebra/zebra_evpn_vxlan.h
new file mode 100644
index 0000000000..bf8904d492
--- /dev/null
+++ b/zebra/zebra_evpn_vxlan.h
@@ -0,0 +1,71 @@
+/*
+ * Zebra EVPN for VxLAN code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* Get the VRR interface for SVI if any */
+static inline struct interface *
+zebra_get_vrr_intf_for_svi(struct interface *ifp)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif = NULL;
+
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) {
+ zif = tmp_if->info;
+ if (!zif)
+ continue;
+
+ if (!IS_ZEBRA_IF_MACVLAN(tmp_if))
+ continue;
+
+ if (zif->link == ifp)
+ return tmp_if;
+ }
+
+ return NULL;
+}
+
+/* EVPN<=>vxlan_zif association */
+static inline void zevpn_vxlan_if_set(zebra_evpn_t *zevpn,
+ struct interface *ifp, bool set)
+{
+ struct zebra_if *zif;
+
+ if (set) {
+ if (zevpn->vxlan_if == ifp)
+ return;
+ zevpn->vxlan_if = ifp;
+ } else {
+ if (!zevpn->vxlan_if)
+ return;
+ zevpn->vxlan_if = NULL;
+ }
+
+ if (ifp)
+ zif = ifp->info;
+ else
+ zif = NULL;
+
+ zebra_evpn_vxl_evpn_set(zif, zevpn, set);
+}
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
index 47b4965396..f84c8c1fcc 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1908,8 +1908,7 @@ static inline void zfpm_init_message_format(const char *format)
return;
}
flog_warn(EC_ZEBRA_PROTOBUF_NOT_AVAILABLE,
- "FPM protobuf message format is deprecated and scheduled to be removed. "
- "Please convert to using netlink format or contact dev@lists.frrouting.org with your use case.");
+ "FPM protobuf message format is deprecated and scheduled to be removed. Please convert to using netlink format or contact dev@lists.frrouting.org with your use case.");
zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;
return;
}
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index a18885ddb7..3e11d53b16 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -279,7 +279,6 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd,
rib_dest_t *dest, struct route_entry *re)
{
struct nexthop *nexthop;
- struct zebra_vrf *zvrf;
memset(ri, 0, sizeof(*ri));
@@ -287,9 +286,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd,
ri->af = rib_dest_af(dest);
ri->nlmsg_type = cmd;
- zvrf = rib_dest_vrf(dest);
- if (zvrf)
- ri->rtm_table = zvrf->table_id;
+ ri->rtm_table = rib_table_info(rib_dest_table(dest))->table_id;
ri->rtm_protocol = RTPROT_UNSPEC;
/*
@@ -364,6 +361,7 @@ static int netlink_route_info_encode(struct netlink_route_info *ri,
struct rtattr *nest, *inner_nest;
struct rtnexthop *rtnh;
struct vxlan_encap_info_t *vxlan;
+ struct in6_addr ipv6;
struct {
struct nlmsghdr n;
@@ -423,8 +421,15 @@ static int netlink_route_info_encode(struct netlink_route_info *ri,
nhi = &ri->nhs[0];
if (nhi->gateway) {
- nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY,
- nhi->gateway, bytelen);
+ if (nhi->type == NEXTHOP_TYPE_IPV4_IFINDEX
+ && ri->af == AF_INET6) {
+ ipv4_to_ipv4_mapped_ipv6(&ipv6,
+ nhi->gateway->ipv4);
+ nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY,
+ &ipv6, bytelen);
+ } else
+ nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY,
+ nhi->gateway, bytelen);
}
if (nhi->if_index) {
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
index 4c0cc62fbf..1758c8f96a 100644
--- a/zebra/zebra_l2.c
+++ b/zebra/zebra_l2.c
@@ -43,6 +43,7 @@
#include "zebra/rt_netlink.h"
#include "zebra/zebra_l2.h"
#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn_mh.h"
/* definitions */
@@ -53,13 +54,7 @@ static void map_slaves_to_bridge(struct interface *br_if, int link)
{
struct vrf *vrf;
struct interface *ifp;
- struct zebra_vrf *zvrf;
- struct zebra_ns *zns;
- zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id);
- assert(zvrf);
- zns = zvrf->zns;
- assert(zns);
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
FOR_ALL_INTERFACES (vrf, ifp) {
struct zebra_if *zif;
@@ -78,8 +73,7 @@ static void map_slaves_to_bridge(struct interface *br_if, int link)
br_slave = &zif->brslave_info;
if (link) {
- if (br_slave->bridge_ifindex == br_if->ifindex &&
- br_slave->ns_id == zns->ns_id)
+ if (br_slave->bridge_ifindex == br_if->ifindex)
br_slave->br_if = br_if;
} else {
if (br_slave->br_if == br_if)
@@ -90,14 +84,12 @@ static void map_slaves_to_bridge(struct interface *br_if, int link)
}
/* Public functions */
-void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave,
- struct zebra_ns *zns)
+void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave)
{
struct interface *br_if;
/* TODO: Handle change of master */
- assert(zns);
- br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id),
+ br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
br_slave->bridge_ifindex);
if (br_if)
br_slave->br_if = br_if;
@@ -119,7 +111,7 @@ void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave,
bond_slave->bond_if = bond_if;
else
bond_slave->bond_if = if_create_ifindex(bond_slave->bond_ifindex,
- vrf_id, NULL);
+ vrf_id);
}
void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave)
@@ -191,6 +183,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp,
if (add) {
memcpy(&zif->l2info.vxl, vxlan_info, sizeof(*vxlan_info));
+ zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif);
zebra_vxlan_if_add(ifp);
return;
}
@@ -229,6 +222,9 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp,
return;
zif->l2info.vxl.access_vlan = access_vlan;
+
+ zebra_evpn_vl_vxl_deref(old_access_vlan, zif);
+ zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif);
zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_VLAN_CHANGE);
}
@@ -237,6 +233,12 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp,
*/
void zebra_l2_vxlanif_del(struct interface *ifp)
{
+ struct zebra_if *zif;
+
+ zif = ifp->info;
+ assert(zif);
+
+ zebra_evpn_vl_vxl_deref(zif->l2info.vxl.access_vlan, zif);
zebra_vxlan_if_del(ifp);
}
@@ -246,32 +248,23 @@ void zebra_l2_vxlanif_del(struct interface *ifp)
* from a bridge before it can be mapped to another bridge.
*/
void zebra_l2if_update_bridge_slave(struct interface *ifp,
- ifindex_t bridge_ifindex,
- ns_id_t ns_id)
+ ifindex_t bridge_ifindex)
{
struct zebra_if *zif;
ifindex_t old_bridge_ifindex;
- ns_id_t old_ns_id;
- struct zebra_vrf *zvrf;
zif = ifp->info;
assert(zif);
- zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
- if (!zvrf)
- return;
-
old_bridge_ifindex = zif->brslave_info.bridge_ifindex;
- old_ns_id = zif->brslave_info.ns_id;
- if (old_bridge_ifindex == bridge_ifindex &&
- old_ns_id == zif->brslave_info.ns_id)
+ if (old_bridge_ifindex == bridge_ifindex)
return;
- zif->brslave_info.ns_id = ns_id;
zif->brslave_info.bridge_ifindex = bridge_ifindex;
+
/* Set up or remove link with master */
if (bridge_ifindex != IFINDEX_INTERNAL) {
- zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns);
+ zebra_l2_map_slave_to_bridge(&zif->brslave_info);
/* In the case of VxLAN, invoke the handler for EVPN. */
if (zif->zif_type == ZEBRA_IF_VXLAN)
zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE);
@@ -307,3 +300,43 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex)
else if (old_bond_ifindex != IFINDEX_INTERNAL)
zebra_l2_unmap_slave_from_bond(&zif->bondslave_info);
}
+
+void zebra_vlan_bitmap_compute(struct interface *ifp,
+ uint32_t vid_start, uint16_t vid_end)
+{
+ uint32_t vid;
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ assert(zif);
+
+ for (vid = vid_start; vid <= vid_end; ++vid)
+ bf_set_bit(zif->vlan_bitmap, vid);
+}
+
+void zebra_vlan_mbr_re_eval(struct interface *ifp, bitfield_t old_vlan_bitmap)
+{
+ uint32_t vid;
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ assert(zif);
+
+ if (!bf_cmp(zif->vlan_bitmap, old_vlan_bitmap))
+ /* no change */
+ return;
+
+ bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) {
+ /* if not already set create new reference */
+ if (!bf_test_index(old_vlan_bitmap, vid))
+ zebra_evpn_vl_mbr_ref(vid, zif);
+
+ /* also clear from the old vlan bitmap */
+ bf_release_index(old_vlan_bitmap, vid);
+ }
+
+ /* any bits remaining in the old vlan bitmap are stale references */
+ bf_for_each_set_bit(old_vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) {
+ zebra_evpn_vl_mbr_deref(vid, zif);
+ }
+}
diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h
index a3c780ee09..2735d915ec 100644
--- a/zebra/zebra_l2.h
+++ b/zebra/zebra_l2.h
@@ -37,7 +37,6 @@ extern "C" {
struct zebra_l2info_brslave {
ifindex_t bridge_ifindex; /* Bridge Master */
struct interface *br_if; /* Pointer to master */
- ns_id_t ns_id; /* network namespace where bridge is */
};
/* zebra L2 interface information - bridge interface */
@@ -82,8 +81,7 @@ union zebra_l2if_info {
#define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1)
-extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave,
- struct zebra_ns *zns);
+extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave);
extern void
zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave);
extern void
@@ -103,11 +101,14 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp,
vlanid_t access_vlan);
extern void zebra_l2_vxlanif_del(struct interface *ifp);
extern void zebra_l2if_update_bridge_slave(struct interface *ifp,
- ifindex_t bridge_ifindex,
- ns_id_t ns_id);
+ ifindex_t bridge_ifindex);
extern void zebra_l2if_update_bond_slave(struct interface *ifp,
ifindex_t bond_ifindex);
+extern void zebra_vlan_bitmap_compute(struct interface *ifp,
+ uint32_t vid_start, uint16_t vid_end);
+extern void zebra_vlan_mbr_re_eval(struct interface *ifp,
+ bitfield_t vlan_bitmap);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_memory.c b/zebra/zebra_memory.c
index a9c2c5fe58..da8121774e 100644
--- a/zebra/zebra_memory.c
+++ b/zebra/zebra_memory.c
@@ -28,3 +28,5 @@
DEFINE_MGROUP(ZEBRA, "zebra")
DEFINE_MTYPE(ZEBRA, RE, "Route Entry")
DEFINE_MTYPE(ZEBRA, RIB_DEST, "RIB destination")
+DEFINE_MTYPE(ZEBRA, ZVLAN, "VLAN")
+DEFINE_MTYPE(ZEBRA, ZVLAN_BITMAP, "VLAN bitmap")
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index e741268ebb..03b8c8de1f 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -47,6 +47,7 @@
#include "zebra/zebra_memory.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_srte.h"
#include "zebra/zebra_errors.h"
DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object")
@@ -120,7 +121,8 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp,
enum lsp_types_t type);
static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf,
mpls_label_t in_label);
-static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty);
+static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty,
+ const char *indent);
static void lsp_print(struct vty *vty, zebra_lsp_t *lsp);
static void *slsp_alloc(void *p);
static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype,
@@ -232,8 +234,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
if (IS_ZEBRA_DEBUG_MPLS) {
nhlfe2str(nhlfe, buf, BUFSIZ);
zlog_debug(
- "LSP in-label %u type %d nexthop %s "
- "out-label changed",
+ "LSP in-label %u type %d nexthop %s out-label changed",
lsp->ile.in_label, lsp_type, buf);
}
@@ -254,8 +255,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
if (IS_ZEBRA_DEBUG_MPLS) {
nhlfe2str(nhlfe, buf, BUFSIZ);
zlog_debug(
- "Add LSP in-label %u type %d nexthop %s "
- "out-label %u",
+ "Add LSP in-label %u type %d nexthop %s out-label %u",
lsp->ile.in_label, lsp_type, buf,
nexthop->nh_label->label[0]);
}
@@ -928,8 +928,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
if (newbest)
nhlfe2str(newbest, buf2, sizeof(buf2));
zlog_debug(
- "Process LSP in-label %u oldbest %s newbest %s "
- "flags 0x%x ecmp# %d",
+ "Process LSP in-label %u oldbest %s newbest %s flags 0x%x ecmp# %d",
lsp->ile.in_label, oldbest ? buf : "NULL",
newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp);
}
@@ -957,9 +956,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
} else {
@@ -985,10 +981,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_removals++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
} else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) {
zebra_nhlfe_t *nhlfe;
@@ -1032,10 +1024,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
}
@@ -1506,12 +1494,23 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
{
char buf[BUFSIZ];
json_object *json_nhlfe = NULL;
+ json_object *json_backups = NULL;
+ json_object *json_label_stack;
struct nexthop *nexthop = nhlfe->nexthop;
+ int i;
json_nhlfe = json_object_new_object();
json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type));
json_object_int_add(json_nhlfe, "outLabel",
nexthop->nh_label->label[0]);
+
+ json_label_stack = json_object_new_array();
+ json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack);
+ for (i = 0; i < nexthop->nh_label->num_labels; i++)
+ json_object_array_add(
+ json_label_stack,
+ json_object_new_int(nexthop->nh_label->label[i]));
+
json_object_int_add(json_nhlfe, "distance", nhlfe->distance);
if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED))
@@ -1537,13 +1536,27 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
default:
break;
}
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nexthop->backup_num; i++) {
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nexthop->backup_idx[i]));
+ }
+
+ json_object_object_add(json_nhlfe, "backupIndex",
+ json_backups);
+ }
+
return json_nhlfe;
}
/*
* Print the NHLFE for a LSP forwarding entry.
*/
-static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
+static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty,
+ const char *indent)
{
struct nexthop *nexthop;
char buf[MPLS_LABEL_STRLEN];
@@ -1558,6 +1571,10 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
nexthop->nh_label->label,
buf, sizeof(buf), 0),
nhlfe->distance);
+
+ if (indent)
+ vty_out(vty, "%s", indent);
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
@@ -1595,31 +1612,34 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
static void lsp_print(struct vty *vty, zebra_lsp_t *lsp)
{
zebra_nhlfe_t *nhlfe, *backup;
- int i;
+ int i, j;
vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label,
CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)"
: "");
frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) {
- nhlfe_print(nhlfe, vty);
+ nhlfe_print(nhlfe, vty, NULL);
- if (nhlfe->nexthop &&
- CHECK_FLAG(nhlfe->nexthop->flags,
- NEXTHOP_FLAG_HAS_BACKUP)) {
- /* Find backup in backup list */
+ if (nhlfe->nexthop == NULL ||
+ !CHECK_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP))
+ continue;
+ /* Backup nhlfes: find backups in backup list */
+
+ for (j = 0; j < nhlfe->nexthop->backup_num; j++) {
i = 0;
backup = NULL;
frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) {
- if (i == nhlfe->nexthop->backup_idx)
+ if (i == nhlfe->nexthop->backup_idx[j])
break;
i++;
}
if (backup) {
vty_out(vty, " [backup %d]", i);
- nhlfe_print(backup, vty);
+ nhlfe_print(backup, vty, " ");
}
}
}
@@ -1643,6 +1663,19 @@ static json_object *lsp_json(zebra_lsp_t *lsp)
json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe));
json_object_object_add(json, "nexthops", json_nhlfe_list);
+ json_nhlfe_list = NULL;
+
+
+ frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) {
+ if (json_nhlfe_list == NULL)
+ json_nhlfe_list = json_object_new_array();
+
+ json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe));
+ }
+
+ if (json_nhlfe_list)
+ json_object_object_add(json, "backupNexthops", json_nhlfe_list);
+
return json;
}
@@ -1882,14 +1915,13 @@ static int mpls_processq_init(void)
}
-/* Public functions */
-
/*
* Process LSP update results from zebra dataplane.
*/
void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
{
struct zebra_vrf *zvrf;
+ mpls_label_t label;
zebra_ile_t tmp_ile;
struct hash *lsp_table;
zebra_lsp_t *lsp;
@@ -1897,6 +1929,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
struct nexthop *nexthop;
enum dplane_op_e op;
enum zebra_dplane_result status;
+ enum zebra_sr_policy_update_label_mode update_mode;
op = dplane_ctx_get_op(ctx);
status = dplane_ctx_get_status(ctx);
@@ -1907,6 +1940,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_in_label(ctx),
dplane_res2str(status));
+ label = dplane_ctx_get_in_label(ctx);
+
switch (op) {
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
@@ -1917,7 +1952,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
lsp_table = zvrf->lsp_table;
- tmp_ile.in_label = dplane_ctx_get_in_label(ctx);
+ tmp_ile.in_label = label;
lsp = hash_lookup(lsp_table, &tmp_ile);
if (lsp == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE)
@@ -1951,13 +1986,21 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
}
}
+ update_mode = (op == DPLANE_OP_LSP_INSTALL)
+ ? ZEBRA_SR_POLICY_LABEL_CREATED
+ : ZEBRA_SR_POLICY_LABEL_UPDATED;
+ zebra_sr_policy_label_update(label, update_mode);
break;
case DPLANE_OP_LSP_DELETE:
- if (status != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) {
flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE,
"LSP Deletion Failure: in-label %u",
dplane_ctx_get_in_label(ctx));
+ break;
+ }
+ zebra_sr_policy_label_update(label,
+ ZEBRA_SR_POLICY_LABEL_REMOVED);
break;
default:
@@ -2112,6 +2155,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
__func__, buf);
SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
} else {
if (is_debug)
@@ -2119,6 +2163,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
__func__, buf);
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
}
if (CHECK_FLAG(ctx_nhlfe->nexthop->flags,
@@ -2140,6 +2185,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
zlog_debug("%s: no match for lsp nhlfe %s",
__func__, buf);
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
@@ -3284,7 +3330,7 @@ lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type,
/*
* Install an LSP and forwarding entry; used primarily
- * from zapi message processing.
+ * from vrf zapi message processing.
*/
int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,
mpls_label_t in_label, uint8_t num_out_labels,
@@ -3335,8 +3381,19 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,
/* Update backup info if present */
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- nhlfe->nexthop->backup_idx = znh->backup_idx;
+ if (znh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ nhlfe_del(nhlfe);
+ return -1;
+ }
+
+ nhlfe->nexthop->backup_num = znh->backup_num;
+ memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx,
+ znh->backup_num);
SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ } else {
+ /* Ensure there's no stale backup info */
+ UNSET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nhlfe->nexthop->backup_num = 0;
}
/* Queue LSP for processing. */
@@ -3371,6 +3428,21 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,
return 0;
}
+zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label)
+{
+ struct hash *lsp_table;
+ zebra_ile_t tmp_ile;
+
+ /* Lookup table. */
+ lsp_table = zvrf->lsp_table;
+ if (!lsp_table)
+ return NULL;
+
+ /* If entry is not present, exit. */
+ tmp_ile.in_label = in_label;
+ return hash_lookup(lsp_table, &tmp_ile);
+}
+
/*
* Uninstall a particular NHLFE in the forwarding table. If this is
* the only NHLFE, the entire LSP forwarding entry has to be deleted.
@@ -3408,7 +3480,7 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,
return 0;
if (IS_ZEBRA_DEBUG_MPLS) {
- nhlfe2str(nhlfe, buf, BUFSIZ);
+ nhlfe2str(nhlfe, buf, sizeof(buf));
zlog_debug("Del LSP in-label %u type %d nexthop %s flags 0x%x",
in_label, type, buf, nhlfe->flags);
}
@@ -3639,10 +3711,9 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label,
return 0;
if (IS_ZEBRA_DEBUG_MPLS) {
- snhlfe2str(snhlfe, buf, BUFSIZ);
+ snhlfe2str(snhlfe, buf, sizeof(buf));
zlog_debug(
- "Upd static LSP in-label %u nexthop %s "
- "out-label %u (old %u)",
+ "Upd static LSP in-label %u nexthop %s out-label %u (old %u)",
in_label, buf, out_label, snhlfe->out_label);
}
snhlfe->out_label = out_label;
@@ -3653,7 +3724,7 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label,
return -1;
if (IS_ZEBRA_DEBUG_MPLS) {
- snhlfe2str(snhlfe, buf, BUFSIZ);
+ snhlfe2str(snhlfe, buf, sizeof(buf));
zlog_debug(
"Add static LSP in-label %u nexthop %s out-label %u",
in_label, buf, out_label);
@@ -3798,7 +3869,8 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf,
for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp))
json_object_object_add(
- json, label2str(lsp->ile.in_label, buf, BUFSIZ),
+ json, label2str(lsp->ile.in_label, buf,
+ sizeof(buf)),
lsp_json(lsp));
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -3853,7 +3925,7 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf,
out_label_str = mpls_label2str(
nexthop->nh_label->num_labels,
&nexthop->nh_label->label[0],
- buf, BUFSIZ, 1);
+ buf, sizeof(buf), 1);
else
out_label_str = "-";
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 07a8efeb8b..c0e58c44e3 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -33,6 +33,7 @@
#include "mpls.h"
#include "zebra/zserv.h"
#include "zebra/zebra_vrf.h"
+#include "hook.h"
#ifdef __cplusplus
extern "C" {
@@ -326,6 +327,11 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,
const union g_addr *gate, ifindex_t ifindex);
/*
+ * Lookup LSP by its input label.
+ */
+zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label);
+
+/*
* Uninstall a particular NHLFE in the forwarding table. If this is
* the only NHLFE, the entire LSP forwarding entry has to be deleted.
*/
@@ -461,6 +467,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type)
case ZEBRA_LSP_SHARP:
case ZEBRA_LSP_OSPF_SR:
case ZEBRA_LSP_ISIS_SR:
+ case ZEBRA_LSP_SRTE:
return 150;
}
@@ -492,6 +499,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type)
return ZEBRA_LSP_ISIS_SR;
case ZEBRA_ROUTE_SHARP:
return ZEBRA_LSP_SHARP;
+ case ZEBRA_ROUTE_SRTE:
+ return ZEBRA_LSP_SRTE;
default:
return ZEBRA_LSP_NONE;
}
@@ -517,6 +526,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type)
return ZEBRA_ROUTE_KERNEL;
case ZEBRA_LSP_SHARP:
return ZEBRA_ROUTE_SHARP;
+ case ZEBRA_LSP_SRTE:
+ return ZEBRA_ROUTE_SRTE;
}
/*
@@ -544,6 +555,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type)
return "SR (IS-IS)";
case ZEBRA_LSP_SHARP:
return "SHARP";
+ case ZEBRA_LSP_SRTE:
+ return "SR-TE";
case ZEBRA_LSP_NONE:
return "Unknown";
}
diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c
index c95a021639..3b2279c66c 100644
--- a/zebra/zebra_mpls_netlink.c
+++ b/zebra/zebra_mpls_netlink.c
@@ -28,13 +28,9 @@
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
-/*
- * LSP forwarding update using dataplane context information.
- */
-enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t ret = -1;
int cmd;
/* Call to netlink layer based on type of update */
@@ -48,26 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS)
zlog_debug("LSP in-label %u: update fails, no best NHLFE",
dplane_ctx_get_in_label(ctx));
- goto done;
+ return -1;
}
cmd = RTM_NEWROUTE;
} else
/* Invalid op? */
- goto done;
-
- ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt,
- sizeof(nl_pkt));
- if (ret <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return -1;
-done:
+ return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen);
+}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false);
}
/*
@@ -75,9 +66,10 @@ done:
* but note that the default has been to report 'success' for pw updates
* on unsupported platforms.
*/
-enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
}
int mpls_kernel_init(void)
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index c8a3cbbbce..b767929dc0 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -276,8 +276,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx)
&& CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)))) {
if (nhlfe->nexthop->nh_label->num_labels > 1) {
flog_warn(EC_ZEBRA_MAX_LABELS_PUSH,
- "%s: can't push %u labels at once "
- "(maximum is 1)",
+ "%s: can't push %u labels at once (maximum is 1)",
__func__,
nhlfe->nexthop->nh_label->num_labels);
continue;
diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c
index 53fe8e8e3f..1fc1faff67 100644
--- a/zebra/zebra_nb.c
+++ b/zebra/zebra_nb.c
@@ -22,40 +22,6 @@
#include "libfrr.h"
#include "zebra_nb.h"
-const char *zebra_afi_safi_value2identity(afi_t afi, safi_t safi)
-{
- if (afi == AFI_IP && safi == SAFI_UNICAST)
- return "ipv4-unicast";
- if (afi == AFI_IP6 && safi == SAFI_UNICAST)
- return "ipv6-unicast";
- if (afi == AFI_IP && safi == SAFI_MULTICAST)
- return "ipv4-multicast";
- if (afi == AFI_IP6 && safi == SAFI_MULTICAST)
- return "ipv6-multicast";
-
- return " ";
-}
-
-void zebra_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi)
-{
- if (strmatch(key, "frr-zebra:ipv4-unicast")) {
- *afi = AFI_IP;
- *safi = SAFI_UNICAST;
- } else if (strmatch(key, "frr-zebra:ipv6-unicast")) {
- *afi = AFI_IP6;
- *safi = SAFI_UNICAST;
- } else if (strmatch(key, "frr-zebra:ipv4-multicast")) {
- *afi = AFI_IP;
- *safi = SAFI_MULTICAST;
- } else if (strmatch(key, "frr-zebra:ipv6-multicast")) {
- *afi = AFI_IP6;
- *safi = SAFI_MULTICAST;
- } else {
- *afi = AFI_UNSPEC;
- *safi = SAFI_UNSPEC;
- }
-}
-
/* clang-format off */
const struct frr_yang_module_info frr_zebra_info = {
.name = "frr-zebra",
@@ -126,27 +92,6 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping",
- .cbs = {
- .create = zebra_vrf_vni_mapping_create,
- .destroy = zebra_vrf_vni_mapping_destroy,
- }
- },
- {
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping/vni-id",
- .cbs = {
- .modify = zebra_vrf_vni_mapping_vni_id_modify,
- .destroy = zebra_vrf_vni_mapping_vni_id_destroy,
- }
- },
- {
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping/prefix-only",
- .cbs = {
- .create = zebra_vrf_vni_mapping_prefix_only_create,
- .destroy = zebra_vrf_vni_mapping_prefix_only_destroy,
- }
- },
- {
.xpath = "/frr-zebra:zebra/debugs/debug-events",
.cbs = {
.modify = zebra_debugs_debug_events_modify,
@@ -608,6 +553,12 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color",
+ .cbs = {
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem,
+ }
+ },
+ {
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry",
.cbs = {
.get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next,
@@ -670,6 +621,19 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id",
+ .cbs = {
+ .modify = lib_vrf_zebra_l3vni_id_modify,
+ .destroy = lib_vrf_zebra_l3vni_id_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only",
+ .cbs = {
+ .modify = lib_vrf_zebra_prefix_only_modify,
+ }
+ },
+ {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length",
.cbs = {
.modify = lib_route_map_entry_match_condition_ipv4_prefix_length_modify,
diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h
index a9e7fd5fb0..e68b819767 100644
--- a/zebra/zebra_nb.h
+++ b/zebra/zebra_nb.h
@@ -26,10 +26,6 @@ extern "C" {
extern const struct frr_yang_module_info frr_zebra_info;
-/* helper functions */
-const char *zebra_afi_safi_value2identity(afi_t afi, safi_t safi);
-void zebra_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi);
-
/* prototypes */
int get_route_information_rpc(struct nb_cb_rpc_args *args);
int get_v6_mroute_info_rpc(struct nb_cb_rpc_args *args);
@@ -61,12 +57,6 @@ int zebra_import_kernel_table_route_map_destroy(
int zebra_allow_external_route_update_create(struct nb_cb_create_args *args);
int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args);
int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args);
-int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args);
-int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args);
-int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args);
-int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args);
-int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args);
-int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args);
int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args);
int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args);
int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args);
@@ -285,6 +275,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem(
struct yang_data *
lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem(
struct nb_cb_get_elem_args *args);
+struct yang_data *
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem(
+ struct nb_cb_get_elem_args *args);
const void *
lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next(
struct nb_cb_get_next_args *args);
@@ -320,6 +313,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem(
struct yang_data *
lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem(
struct nb_cb_get_elem_args *args);
+int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args);
+int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args);
+int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index 948ef51320..b4ed910b4d 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -31,6 +31,8 @@
#include "zebra/connected.h"
#include "zebra/zebra_router.h"
#include "zebra/debug.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_vxlan.h"
/*
* XPath: /frr-zebra:zebra/mcast-rpf-lookup
@@ -273,99 +275,6 @@ int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args)
}
/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping
- */
-int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping/vni-id
- */
-int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping/prefix-only
- */
-int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-zebra:zebra/debugs/debug-events
*/
int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args)
@@ -1242,7 +1151,7 @@ int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args)
table_id = zvrf->table_id;
afi_safi_name = yang_dnode_get_string(args->dnode, "./afi-safi-name");
- zebra_afi_safi_identity2value(afi_safi_name, &afi, &safi);
+ yang_afi_safi_identity2value(afi_safi_name, &afi, &safi);
zrt = zebra_router_find_zrt(zvrf, table_id, afi, safi);
@@ -1277,6 +1186,161 @@ int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+/*
+ * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id
+ */
+int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ vni_t vni = 0;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_evpn = NULL;
+ char err[ERR_STR_SZ];
+ bool pfx_only = false;
+ const struct lyd_node *pn_dnode;
+ const char *vrfname;
+
+ switch (args->event) {
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ return NB_OK;
+ case NB_EV_VALIDATE:
+ zvrf_evpn = zebra_vrf_get_evpn();
+ if (!zvrf_evpn) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "evpn vrf is not present.");
+ return NB_ERR_VALIDATION;
+ }
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+ /* Get vrf info from parent node, reject configuration
+ * if zebra vrf already mapped to different vni id.
+ */
+ pn_dnode = yang_dnode_get_parent(args->dnode, "vrf");
+ if (pn_dnode) {
+ vrfname = yang_dnode_get_string(pn_dnode, "./name");
+ zvrf = zebra_vrf_lookup_by_name(vrfname);
+ if (!zvrf) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "zebra vrf info not found for vrf:%s.",
+ vrfname);
+ return NB_ERR_VALIDATION;
+ }
+ if (zvrf->l3vni && zvrf->l3vni != vni) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "vni %u cannot be configured as vni %u is already configured under the vrf",
+ vni, zvrf->l3vni);
+ return NB_ERR_VALIDATION;
+ }
+ }
+
+ /* Check if this VNI is already present in the system */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "VNI %u is already configured as L3-VNI", vni);
+ return NB_ERR_VALIDATION;
+ }
+
+ break;
+ case NB_EV_APPLY:
+
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ zvrf = zebra_vrf_lookup_by_name(vrf->name);
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+ /* Note: This covers lib_vrf_zebra_prefix_only_modify() config
+ * along with l3vni config
+ */
+ pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only");
+
+ if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ pfx_only ? 1 : 0, 1)
+ != 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "vrf vni %u mapping failed with error: %s",
+ vni, err);
+ return NB_ERR;
+ }
+
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(vrf);
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ vni_t vni = 0;
+ char err[ERR_STR_SZ];
+ uint8_t filter = 0;
+
+ switch (args->event) {
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_VALIDATE:
+ return NB_OK;
+ case NB_EV_APPLY:
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ zvrf = zebra_vrf_lookup_by_name(vrf->name);
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+
+ if (!zl3vni_lookup(vni))
+ return NB_OK;
+
+ if (zvrf->l3vni != vni) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "vrf %s has different vni %u mapped",
+ vrf->name, zvrf->l3vni);
+ return NB_ERR;
+ }
+
+ if (is_l3vni_for_prefix_routes_only(zvrf->l3vni))
+ filter = 1;
+
+ if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ filter, 0)
+ != 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "vrf vni %u unmapping failed with error: %s",
+ vni, err);
+ return NB_ERR;
+ }
+
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!zebra_vrf_has_config(zvrf))
+ vrf_reset_user_cfged(vrf);
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only
+ */
+int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* TODO: implement me. */
+ break;
+ }
+
+ return NB_OK;
+}
/*
* XPath:
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
index 4bca3b36d6..21c89f64ed 100644
--- a/zebra/zebra_nb_state.c
+++ b/zebra/zebra_nb_state.c
@@ -185,10 +185,9 @@ int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args)
args->keys->num = 2;
- snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%s:%s",
- "frr-zebra",
- zebra_afi_safi_value2identity(zrt->afi, zrt->safi));
- snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32,
+ snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%s",
+ yang_afi_safi_value2identity(zrt->afi, zrt->safi));
+ snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%u",
zrt->tableid);
return NB_OK;
@@ -205,7 +204,7 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args)
zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
- zebra_afi_safi_identity2value(args->keys->key[0], &afi, &safi);
+ yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi);
table_id = yang_str2uint32(args->keys->key[1]);
/* table_id 0 assume vrf's table_id. */
if (!table_id)
@@ -526,54 +525,57 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys(
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
- args->keys->num = 3;
+ args->keys->num = 4;
strlcpy(args->keys->key[0], yang_nexthop_type2str(nexthop->type),
sizeof(args->keys->key[0]));
+ snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32,
+ nexthop->vrf_id);
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]),
+ snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]),
"%pI4", &nexthop->gate.ipv4);
if (nexthop->ifindex)
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
else
/* no ifindex */
- strlcpy(args->keys->key[2], " ",
- sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ",
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
- snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]),
+ snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]),
"%pI6", &nexthop->gate.ipv6);
if (nexthop->ifindex)
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
else
/* no ifindex */
- strlcpy(args->keys->key[2], " ",
- sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ",
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_IFINDEX:
- strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1]));
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex, nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_BLACKHOLE:
/* Gateway IP */
- strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1]));
- strlcpy(args->keys->key[2], " ", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ", sizeof(args->keys->key[3]));
break;
default:
break;
@@ -806,6 +808,22 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem(
/*
* XPath:
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color
+ */
+struct yang_data *
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ struct nexthop *nexthop = (struct nexthop *)args->list_entry;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE))
+ return yang_data_new_uint32(args->xpath, nexthop->srte_color);
+
+ return NULL;
+}
+
+/*
+ * XPath:
* /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry
*/
const void *
diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c
index 79121bb086..8de4daf439 100644
--- a/zebra/zebra_netns_id.c
+++ b/zebra/zebra_netns_id.c
@@ -159,34 +159,27 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
return ns_id;
}
-/* fd_param = -1 is ignored.
- * netnspath set to null is ignored.
- * one of the 2 params is mandatory. netnspath is looked in priority
- */
-ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
+ns_id_t zebra_ns_id_get(const char *netnspath)
{
int ns_id = -1;
struct sockaddr_nl snl;
- int fd = -1, sock, ret;
+ int fd, sock, ret;
unsigned int seq;
ns_id_t return_nsid = NS_UNKNOWN;
/* netns path check */
- if (!netnspath && fd_param == -1)
+ if (!netnspath)
return NS_UNKNOWN;
- if (netnspath) {
- fd = open(netnspath, O_RDONLY);
- if (fd == -1)
- return NS_UNKNOWN;
- } else if (fd_param != -1)
- fd = fd_param;
+ fd = open(netnspath, O_RDONLY);
+ if (fd == -1)
+ return NS_UNKNOWN;
+
/* netlink socket */
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
sock, safe_strerror(errno));
- if (fd_param == -1)
- close(fd);
+ close(fd);
return NS_UNKNOWN;
}
memset(&snl, 0, sizeof(snl));
@@ -199,8 +192,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
"netlink( %u) socket() bind error: %s", sock,
safe_strerror(errno));
close(sock);
- if (fd_param == -1)
- close(fd);
+ close(fd);
return NS_UNKNOWN;
}
@@ -222,8 +214,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
ret = send_receive(sock, nlh, seq, buf);
if (ret < 0) {
close(sock);
- if (fd_param == -1)
- close(fd);
+ close(fd);
return NS_UNKNOWN;
}
nlh = (struct nlmsghdr *)buf;
@@ -267,8 +258,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
"netlink( %u) recvfrom() error 2 when reading: %s",
fd, safe_strerror(errno));
close(sock);
- if (fd_param == -1)
- close(fd);
+ close(fd);
if (errno == ENOTSUP) {
zlog_debug("NEWNSID locally generated");
return zebra_ns_id_get_fallback(netnspath);
@@ -289,8 +279,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
ret = send_receive(sock, nlh, seq, buf);
if (ret < 0) {
close(sock);
- if (fd_param == -1)
- close(fd);
+ close(fd);
return NS_UNKNOWN;
}
nlh = (struct nlmsghdr *)buf;
@@ -321,18 +310,16 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
} while (len != 0 && ret == 0);
}
- if (fd_param == -1)
- close(fd);
+ close(fd);
close(sock);
return return_nsid;
}
#else
-ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused)))
+ns_id_t zebra_ns_id_get(const char *netnspath)
{
return zebra_ns_id_get_fallback(netnspath);
}
-
#endif /* ! defined(HAVE_NETLINK) */
#ifdef HAVE_NETNS
@@ -368,7 +355,7 @@ ns_id_t zebra_ns_id_get_default(void)
return NS_DEFAULT_INTERNAL;
}
close(fd);
- return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1);
+ return zebra_ns_id_get((char *)NS_DEFAULT_NAME);
#else /* HAVE_NETNS */
return NS_DEFAULT_INTERNAL;
#endif /* !HAVE_NETNS */
diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h
index dd9eab18e0..7a5f6851f4 100644
--- a/zebra/zebra_netns_id.h
+++ b/zebra/zebra_netns_id.h
@@ -24,7 +24,7 @@
extern "C" {
#endif
-extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd);
+extern ns_id_t zebra_ns_id_get(const char *netnspath);
extern ns_id_t zebra_ns_id_get_default(void);
#ifdef __cplusplus
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
index 72e4fd0055..ec7681bf23 100644
--- a/zebra/zebra_netns_notify.c
+++ b/zebra/zebra_netns_notify.c
@@ -72,14 +72,13 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)
char *netnspath = ns_netns_pathname(NULL, name);
struct vrf *vrf;
int ret;
- ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN;
- struct ns *default_ns;
+ ns_id_t ns_id, ns_id_external;
if (netnspath == NULL)
return;
frr_with_privs(&zserv_privs) {
- ns_id = zebra_ns_id_get(netnspath, -1);
+ ns_id = zebra_ns_id_get(netnspath);
}
if (ns_id == NS_UNKNOWN)
return;
@@ -98,21 +97,9 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)
ns_map_nsid_with_external(ns_id, false);
return;
}
-
- default_ns = ns_get_default();
-
- /* force kernel ns_id creation in that new vrf */
- frr_with_privs(&zserv_privs) {
- ns_switch_to_netns(netnspath);
- ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd);
- ns_switchback_to_initial();
- }
-
frr_with_privs(&zserv_privs) {
ret = vrf_netns_handler_create(NULL, vrf, netnspath,
- ns_id_external,
- ns_id,
- ns_id_relative);
+ ns_id_external, ns_id);
}
if (ret != CMD_SUCCESS) {
flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED,
@@ -185,7 +172,7 @@ static bool zebra_ns_notify_is_default_netns(const char *name)
{
struct stat default_netns_stat;
struct stat st;
- char netnspath[64];
+ char netnspath[PATH_MAX];
if (zebra_ns_notify_self_identify(&default_netns_stat))
return false;
@@ -230,14 +217,12 @@ static int zebra_ns_ready_read(struct thread *t)
/* check default name is not already set */
if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) {
- zlog_warn("NS notify : NS %s is already default VRF."
- "Cancel VRF Creation", basename(netnspath));
+ zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath));
return zebra_ns_continue_read(zns_info, 1);
}
if (zebra_ns_notify_is_default_netns(basename(netnspath))) {
zlog_warn(
- "NS notify : NS %s is default VRF."
- " Updating VRF Name", basename(netnspath));
+ "NS notify : NS %s is default VRF. Updating VRF Name", basename(netnspath));
vrf_set_default_name(basename(netnspath), false);
return zebra_ns_continue_read(zns_info, 1);
}
@@ -333,14 +318,12 @@ void zebra_ns_notify_parse(void)
}
/* check default name is not already set */
if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) {
- zlog_warn("NS notify : NS %s is already default VRF."
- "Cancel VRF Creation", dent->d_name);
+ zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name);
continue;
}
if (zebra_ns_notify_is_default_netns(dent->d_name)) {
zlog_warn(
- "NS notify : NS %s is default VRF."
- " Updating VRF Name", dent->d_name);
+ "NS notify : NS %s is default VRF. Updating VRF Name", dent->d_name);
vrf_set_default_name(dent->d_name, false);
continue;
}
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 9bfd7aacb7..b8faaa43fd 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -28,6 +28,7 @@
#include "lib/mpls.h"
#include "lib/jhash.h"
#include "lib/debug.h"
+#include "lib/lib_errors.h"
#include "zebra/connected.h"
#include "zebra/debug.h"
@@ -36,6 +37,7 @@
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_memory.h"
+#include "zebra/zebra_srte.h"
#include "zebra/zserv.h"
#include "zebra/rt.h"
#include "zebra_errors.h"
@@ -1620,7 +1622,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
}
static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
- struct nexthop *nexthop)
+ struct nexthop *nexthop,
+ struct zebra_sr_policy *policy)
{
struct nexthop *resolved_hop;
uint8_t num_labels = 0;
@@ -1684,7 +1687,21 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
/* Copy labels of the resolved route and the parent resolving to it */
- if (newhop->nh_label) {
+ if (policy) {
+ int i = 0;
+
+ /*
+ * Don't push the first SID if the corresponding action in the
+ * LFIB is POP.
+ */
+ if (!newhop->nh_label || !newhop->nh_label->num_labels
+ || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL)
+ i = 1;
+
+ for (; i < policy->segment_list.label_num; i++)
+ labels[num_labels++] = policy->segment_list.labels[i];
+ label_type = policy->segment_list.type;
+ } else if (newhop->nh_label) {
for (i = 0; i < newhop->nh_label->num_labels; i++) {
/* Be a bit picky about overrunning the local array */
if (num_labels >= MPLS_MAX_LABELS) {
@@ -1771,10 +1788,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
struct route_node *rn;
struct route_entry *match = NULL;
int resolved;
+ zebra_nhlfe_t *nhlfe;
struct nexthop *newhop;
struct interface *ifp;
rib_dest_t *dest;
struct zebra_vrf *zvrf;
+ struct in_addr local_ipv4;
+ struct in_addr *ipv4;
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
|| nexthop->type == NEXTHOP_TYPE_IPV6)
@@ -1835,13 +1855,63 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
return 0;
}
+ /* Validation for ipv4 mapped ipv6 nexthop. */
+ if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) {
+ afi = AFI_IP;
+ ipv4 = &local_ipv4;
+ ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4);
+ } else {
+ ipv4 = &nexthop->gate.ipv4;
+ }
+
+ if (nexthop->srte_color) {
+ struct ipaddr endpoint = {0};
+ struct zebra_sr_policy *policy;
+
+ switch (afi) {
+ case AFI_IP:
+ endpoint.ipa_type = IPADDR_V4;
+ endpoint.ipaddr_v4 = *ipv4;
+ break;
+ case AFI_IP6:
+ endpoint.ipa_type = IPADDR_V6;
+ endpoint.ipaddr_v6 = nexthop->gate.ipv6;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown address-family: %u", __func__,
+ afi);
+ exit(1);
+ }
+
+ policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint);
+ if (policy && policy->status == ZEBRA_SR_POLICY_UP) {
+ resolved = 0;
+ frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list,
+ nhlfe) {
+ if (!CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_DELETED))
+ continue;
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ nexthop_set_resolved(afi, nhlfe->nexthop,
+ nexthop, policy);
+ resolved = 1;
+ }
+ if (resolved)
+ return 1;
+ }
+ }
+
/* Make lookup prefix. */
memset(&p, 0, sizeof(struct prefix));
switch (afi) {
case AFI_IP:
p.family = AF_INET;
p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.u.prefix4 = nexthop->gate.ipv4;
+ p.u.prefix4 = *ipv4;
break;
case AFI_IP6:
p.family = AF_INET6;
@@ -1951,8 +2021,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
goto done_with_match;
}
- /* Examine installed nexthops */
- nhg = &match->nhe->nhg;
+ /* Examine installed nexthops; note that there
+ * may not be any installed primary nexthops if
+ * only backups are installed.
+ */
+ nhg = rib_get_fib_nhg(match);
for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
if (!nexthop_valid_resolve(nexthop, newhop))
continue;
@@ -1964,7 +2037,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
SET_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
+ nexthop_set_resolved(afi, newhop, nexthop,
+ NULL);
resolved = 1;
}
@@ -1973,8 +2047,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
* dedicated fib list.
*/
nhg = rib_get_fib_backup_nhg(match);
- if (nhg == NULL ||
- nhg == zebra_nhg_get_backup_nhg(match->nhe))
+ if (nhg == NULL || nhg->nexthop == NULL)
goto done_with_match;
for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
@@ -1988,7 +2061,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
SET_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
+ nexthop_set_resolved(afi, newhop, nexthop,
+ NULL);
resolved = 1;
}
done_with_match:
@@ -2589,6 +2663,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_RULE_ADD:
case DPLANE_OP_RULE_DELETE:
case DPLANE_OP_RULE_UPDATE:
+ case DPLANE_OP_NEIGH_DISCOVER:
case DPLANE_OP_NONE:
break;
}
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 6462daf687..4e51437337 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -153,25 +153,20 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete)
/* During zebra shutdown, do partial cleanup while the async dataplane
* is still running.
*/
-int zebra_ns_early_shutdown(struct ns *ns,
- void *param_in __attribute__((unused)),
- void **param_out __attribute__((unused)))
+int zebra_ns_early_shutdown(struct ns *ns)
{
struct zebra_ns *zns = ns->info;
if (zns == NULL)
return 0;
- zebra_ns_disable_internal(zns, false);
- return NS_WALK_CONTINUE;
+ return zebra_ns_disable_internal(zns, false);
}
/* During zebra shutdown, do final cleanup
* after all dataplane work is complete.
*/
-int zebra_ns_final_shutdown(struct ns *ns,
- void *param_in __attribute__((unused)),
- void **param_out __attribute__((unused)))
+int zebra_ns_final_shutdown(struct ns *ns)
{
struct zebra_ns *zns = ns->info;
@@ -180,7 +175,7 @@ int zebra_ns_final_shutdown(struct ns *ns,
kernel_terminate(zns, true);
- return NS_WALK_CONTINUE;
+ return 0;
}
int zebra_ns_init(const char *optional_default_name)
@@ -188,16 +183,12 @@ int zebra_ns_init(const char *optional_default_name)
struct ns *default_ns;
ns_id_t ns_id;
ns_id_t ns_id_external;
- struct ns *ns;
frr_with_privs(&zserv_privs) {
ns_id = zebra_ns_id_get_default();
}
ns_id_external = ns_map_nsid_with_external(ns_id, true);
ns_init_management(ns_id_external, ns_id);
- ns = ns_get_default();
- if (ns)
- ns->relative_default_ns = ns_id;
default_ns = ns_lookup(ns_get_default_id());
if (!default_ns) {
diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h
index f7d1f40782..dc79a83db0 100644
--- a/zebra/zebra_ns.h
+++ b/zebra/zebra_ns.h
@@ -67,12 +67,9 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
int zebra_ns_init(const char *optional_default_name);
int zebra_ns_enable(ns_id_t ns_id, void **info);
int zebra_ns_disabled(struct ns *ns);
-int zebra_ns_early_shutdown(struct ns *ns,
- void *param_in __attribute__((unused)),
- void **param_out __attribute__((unused)));
-int zebra_ns_final_shutdown(struct ns *ns,
- void *param_in __attribute__((unused)),
- void **param_out __attribute__((unused)));
+int zebra_ns_early_shutdown(struct ns *ns);
+int zebra_ns_final_shutdown(struct ns *ns);
+
int zebra_ns_config_write(struct vty *vty, struct ns *ns);
#ifdef __cplusplus
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index c5a7795273..1bc8d893bc 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -336,14 +336,20 @@ bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
return true;
}
-void zebra_pbr_iptable_free(void *arg)
+/* this function gives option to flush plugin memory contexts
+ * with all parameter. set it to true to flush all
+ * set it to false to flush only passed arg argument
+ */
+static void _zebra_pbr_iptable_free_all(void *arg, bool all)
{
struct zebra_pbr_iptable *iptable;
struct listnode *node, *nnode;
char *name;
iptable = (struct zebra_pbr_iptable *)arg;
- hook_call(zebra_pbr_iptable_update, 0, iptable);
+
+ if (all)
+ hook_call(zebra_pbr_iptable_update, 0, iptable);
if (iptable->interface_name_list) {
for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node,
@@ -356,6 +362,11 @@ void zebra_pbr_iptable_free(void *arg)
XFREE(MTYPE_TMP, iptable);
}
+void zebra_pbr_iptable_free(void *arg)
+{
+ _zebra_pbr_iptable_free_all(arg, false);
+}
+
uint32_t zebra_pbr_iptable_hash_key(const void *arg)
{
const struct zebra_pbr_iptable *iptable = arg;
@@ -529,7 +540,7 @@ static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data)
if (iptable->sock == *sock) {
if (hash_release(zrouter.iptable_hash, iptable))
- zebra_pbr_iptable_free(iptable);
+ _zebra_pbr_iptable_free_all(iptable, true);
else
hook_call(zebra_pbr_iptable_update, 0, iptable);
}
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index 4bc0f40037..888d2fcfa0 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -54,6 +54,8 @@ struct zebra_pbr_rule {
(r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT)
#define IS_RULE_FILTERING_ON_DST_PORT(r) \
(r->rule.filter.filter_bm & PBR_FILTER_DST_PORT)
+#define IS_RULE_FILTERING_ON_DSFIELD(r) \
+ (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD)
#define IS_RULE_FILTERING_ON_FWMARK(r) \
(r->rule.filter.filter_bm & PBR_FILTER_FWMARK)
@@ -170,13 +172,6 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable);
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable);
/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-extern enum zebra_dplane_result
-kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
-
-/*
* Get to know existing PBR rules in the kernel - typically called at startup.
*/
extern void kernel_read_pbr_rules(struct zebra_ns *zns);
diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c
index 88ea2b87b1..9a3b567b5a 100644
--- a/zebra/zebra_ptm.c
+++ b/zebra/zebra_ptm.c
@@ -434,8 +434,7 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp,
if (ifp) {
zlog_debug(
- "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s"
- " %s event",
+ "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s %s event",
inet_ntop(dp->family, &dp->u.prefix, buf[0],
INET6_ADDRSTRLEN),
dp->prefixlen, ifp->name,
@@ -444,8 +443,7 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp,
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
zlog_debug(
- "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d "
- "with src %s/%d and vrf %s(%u) %s event",
+ "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d with src %s/%d and vrf %s(%u) %s event",
inet_ntop(dp->family, &dp->u.prefix, buf[0],
INET6_ADDRSTRLEN),
dp->prefixlen,
@@ -502,8 +500,7 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt,
if (IS_ZEBRA_DEBUG_EVENT)
zlog_debug(
- "%s: Recv Port [%s] bfd status [%s] vrf [%s]"
- " peer [%s] local [%s]",
+ "%s: Recv Port [%s] bfd status [%s] vrf [%s] peer [%s] local [%s]",
__func__, ifp ? ifp->name : "N/A", bfdst_str, vrf_str,
dest_str, src_str);
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index 2328ab650a..cdcca1e930 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -215,8 +215,7 @@ void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus)
{
if (IS_ZEBRA_DEBUG_PW)
zlog_debug(
- "%u: failed installing pseudowire %s, "
- "scheduling retry in %u seconds",
+ "%u: failed installing pseudowire %s, scheduling retry in %u seconds",
pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL);
/* schedule to retry later */
@@ -518,6 +517,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty)
struct zebra_pw *pw;
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
zvrf = vrf_info_lookup(VRF_DEFAULT);
if (!zvrf)
@@ -545,22 +545,41 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty)
vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid);
vty_out(vty, " Status: %s \n",
(zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
- ? "Up"
- : "Down");
+ ? "Up"
+ : "Down");
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
&pw->nexthop, NULL);
- if (re) {
- for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {
- snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
- nexthop);
- vty_out(vty, " Next Hop: %s\n", buf_nh);
- if (nexthop->nh_label)
- vty_out(vty, " Next Hop label: %u\n",
- nexthop->nh_label->label[0]);
- else
- vty_out(vty, " Next Hop label: %s\n",
- "-");
- }
+ if (re == NULL)
+ continue;
+
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
+ nexthop);
+ vty_out(vty, " Next Hop: %s\n", buf_nh);
+ if (nexthop->nh_label)
+ vty_out(vty, " Next Hop label: %u\n",
+ nexthop->nh_label->label[0]);
+ else
+ vty_out(vty, " Next Hop label: %s\n",
+ "-");
+ }
+
+ /* Include any installed backups */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg == NULL)
+ continue;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
+ nexthop);
+ vty_out(vty, " Next Hop: %s\n", buf_nh);
+ if (nexthop->nh_label)
+ vty_out(vty, " Next Hop label: %u\n",
+ nexthop->nh_label->label[0]);
+ else
+ vty_out(vty, " Next Hop label: %s\n",
+ "-");
}
}
}
@@ -569,6 +588,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
{
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
char buf_nbr[INET6_ADDRSTRLEN];
char buf_nh[100];
json_object *json_pw = NULL;
@@ -603,23 +623,48 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
: "Down");
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
&pw->nexthop, NULL);
- if (re) {
- for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {
- json_nexthop = json_object_new_object();
- snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
- json_object_string_add(json_nexthop, "nexthop", buf_nh);
- if (nexthop->nh_label)
- json_object_int_add(
- json_nexthop, "nhLabel",
- nexthop->nh_label->label[0]);
- else
- json_object_string_add(json_nexthop, "nhLabel",
- "-");
+ if (re == NULL)
+ goto done;
+
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ json_nexthop = json_object_new_object();
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
+ json_object_string_add(json_nexthop, "nexthop", buf_nh);
+ if (nexthop->nh_label)
+ json_object_int_add(
+ json_nexthop, "nhLabel",
+ nexthop->nh_label->label[0]);
+ else
+ json_object_string_add(json_nexthop, "nhLabel",
+ "-");
- json_object_array_add(json_nexthops, json_nexthop);
- }
- json_object_object_add(json_pw, "nexthops", json_nexthops);
+ json_object_array_add(json_nexthops, json_nexthop);
+ }
+
+ /* Include installed backup nexthops also */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg == NULL)
+ goto done;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ json_nexthop = json_object_new_object();
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
+ json_object_string_add(json_nexthop, "nexthop", buf_nh);
+ if (nexthop->nh_label)
+ json_object_int_add(
+ json_nexthop, "nhLabel",
+ nexthop->nh_label->label[0]);
+ else
+ json_object_string_add(json_nexthop, "nhLabel",
+ "-");
+
+ json_object_array_add(json_nexthops, json_nexthop);
}
+
+done:
+
+ json_object_object_add(json_pw, "nexthops", json_nexthops);
json_object_array_add(json_pws, json_pw);
}
@@ -680,8 +725,7 @@ static int zebra_pw_config(struct vty *vty)
pw->local_label, pw->remote_label);
else
vty_out(vty,
- " ! Incomplete config, specify the static "
- "MPLS labels\n");
+ " ! Incomplete config, specify the static MPLS labels\n");
if (pw->af != AF_UNSPEC) {
char buf[INET6_ADDRSTRLEN];
@@ -689,8 +733,7 @@ static int zebra_pw_config(struct vty *vty)
vty_out(vty, " neighbor %s\n", buf);
} else
vty_out(vty,
- " ! Incomplete config, specify a neighbor "
- "address\n");
+ " ! Incomplete config, specify a neighbor address\n");
if (!(pw->flags & F_PSEUDOWIRE_CWORD))
vty_out(vty, " control-word exclude\n");
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 67b3812ed3..d1d56f2cdb 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -106,7 +106,8 @@ static const struct {
[ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5},
[ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5},
[ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3},
- [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}
+ [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5},
+ [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 5},
/* Any new route type added to zebra, should be mirrored here */
/* no entry/default: 150 */
@@ -472,6 +473,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
SET_FLAG(old->status, ROUTE_ENTRY_QUEUED);
/* Free old FIB nexthop group */
+ UNSET_FLAG(old->status, ROUTE_ENTRY_USE_FIB_NHG);
if (old->fib_ng.nexthop) {
nexthops_free(old->fib_ng.nexthop);
old->fib_ng.nexthop = NULL;
@@ -495,9 +497,6 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
if (zvrf)
zvrf->installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
@@ -542,9 +541,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
if (zvrf)
zvrf->removals++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
@@ -574,6 +570,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
nexthops_free(re->fib_ng.nexthop);
re->fib_ng.nexthop = NULL;
}
+ UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -1081,8 +1078,7 @@ static void rib_process(struct route_node *rn)
if (re != old_selected) {
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug(
- "%s: %s(%u):%s: imported via import-table but denied "
- "by the ip protocol table route-map",
+ "%s: %s(%u):%s: imported via import-table but denied by the ip protocol table route-map",
__func__,
VRF_LOGNAME(vrf),
vrf_id, buf);
@@ -1376,7 +1372,7 @@ static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg,
continue;
/* Check for a FIB nexthop corresponding to the RIB nexthop */
- if (nexthop_same(ctx_nexthop, nexthop) == false) {
+ if (!nexthop_same(ctx_nexthop, nexthop)) {
/* If the FIB doesn't know about the nexthop,
* it's not installed
*/
@@ -1491,7 +1487,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
VRF_LOGNAME(vrf), re->vrf_id, dest_str);
goto check_backups;
- } else if (re->fib_ng.nexthop) {
+ } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) {
/*
* Free stale fib list and move on to check the rib nhg.
*/
@@ -1502,6 +1498,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
nexthops_free(re->fib_ng.nexthop);
re->fib_ng.nexthop = NULL;
+ UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
+
/* Note that the installed nexthops have changed */
changed_p = true;
} else {
@@ -1547,20 +1545,15 @@ no_nexthops:
*/
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug(
- "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg",
+ "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg%s",
VRF_LOGNAME(vrf), re->vrf_id, dest_str,
- (changed_p ? "true" : "false"));
+ (changed_p ? "true" : "false"),
+ ctxnhg->nexthop != NULL ? "" : " (empty)");
+ /* Set the flag about the dedicated fib list */
+ SET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
if (ctxnhg->nexthop)
copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL);
- else {
- /* Bit of a special case when the fib has _no_ installed
- * nexthops.
- */
- nexthop = nexthop_new();
- nexthop->type = NEXTHOP_TYPE_IPV4;
- _nexthop_add(&(re->fib_ng.nexthop), nexthop);
- }
check_backups:
@@ -1612,7 +1605,7 @@ check_backups:
}
/*
- * If a FIB backup nexthop set exists: attach a copy
+ * If a FIB backup nexthop set exists, attach a copy
* to the route if any backup is installed
*/
if (ctxnhg && ctxnhg->nexthop) {
@@ -1937,7 +1930,7 @@ static int rib_count_installed_nh(struct route_entry *re)
/* The meaningful flag depends on where the installed
* nexthops reside.
*/
- if (nhg == &(re->fib_backup_ng)) {
+ if (nhg == &(re->fib_ng)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
count++;
} else {
@@ -1946,9 +1939,12 @@ static int rib_count_installed_nh(struct route_entry *re)
}
}
- for (ALL_NEXTHOPS_PTR(rib_get_fib_backup_nhg(re), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
- count++;
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ count++;
+ }
}
return count;
@@ -2630,6 +2626,8 @@ static void _route_entry_dump_nh(const struct route_entry *re,
char nhname[PREFIX_STRLEN];
char backup_str[50];
char wgt_str[50];
+ char temp_str[10];
+ int i;
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
@@ -2655,8 +2653,12 @@ static void _route_entry_dump_nh(const struct route_entry *re,
backup_str[0] = '\0';
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- snprintf(backup_str, sizeof(backup_str), "backup %d,",
- (int)nexthop->backup_idx);
+ snprintf(backup_str, sizeof(backup_str), "backup ");
+ for (i = 0; i < nexthop->backup_num; i++) {
+ snprintf(temp_str, sizeof(temp_str), "%d, ",
+ nexthop->backup_idx[i]);
+ strlcat(backup_str, temp_str, sizeof(backup_str));
+ }
}
wgt_str[0] = '\0';
@@ -3149,8 +3151,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
if (nh)
rnode_debug(
rn, vrf_id,
- "via %s ifindex %d type %d "
- "doesn't exist in rib",
+ "via %s ifindex %d type %d doesn't exist in rib",
inet_ntop(afi2family(afi),
&nh->gate, buf2,
sizeof(buf2)),
@@ -3763,6 +3764,7 @@ static int rib_process_dplane_results(struct thread *thread)
case DPLANE_OP_NEIGH_DELETE:
case DPLANE_OP_VTEP_ADD:
case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NEIGH_DISCOVER:
case DPLANE_OP_NONE:
/* Don't expect this: just return the struct? */
dplane_ctx_fini(&ctx);
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index d1a5cf2a9d..cdc00f6026 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -46,6 +46,7 @@
#include "zebra/debug.h"
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
+#include "zebra/zebra_srte.h"
#include "zebra/interface.h"
#include "zebra/zebra_memory.h"
#include "zebra/zebra_errors.h"
@@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re,
static void copy_state(struct rnh *rnh, const struct route_entry *re,
struct route_node *rn);
static int compare_state(struct route_entry *r1, struct route_entry *r2);
-static int send_client(struct rnh *rnh, struct zserv *client,
- enum rnh_type type, vrf_id_t vrf_id);
static void print_rnh(struct route_node *rn, struct vty *vty);
static int zebra_client_cleanup_rnh(struct zserv *client);
@@ -305,7 +304,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
* We always need to respond with known information,
* currently multiple daemons expect this behavior
*/
- send_client(rnh, client, type, vrf_id);
+ zebra_send_rnh_update(rnh, client, type, vrf_id, 0);
}
void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
@@ -531,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi,
}
/* state changed, notify clients */
for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
- send_client(rnh, client,
- RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client,
+ RNH_IMPORT_CHECK_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
}
}
@@ -594,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi,
zebra_route_string(client->proto));
}
- send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
if (re)
@@ -1040,22 +1041,10 @@ static bool compare_valid_nexthops(struct route_entry *r1,
* backups will be in the 'fib' list.
*/
nhg1 = rib_get_fib_backup_nhg(r1);
- if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe))
- nhg1 = NULL;
-
nhg2 = rib_get_fib_backup_nhg(r2);
- if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe))
- nhg2 = NULL;
-
- if (nhg1)
- nh1 = nhg1->nexthop;
- else
- nh1 = NULL;
- if (nhg2)
- nh2 = nhg2->nexthop;
- else
- nh2 = NULL;
+ nh1 = nhg1->nexthop;
+ nh2 = nhg2->nexthop;
while (1) {
/* Find each backup list's next valid nexthop */
@@ -1119,8 +1108,9 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)
return 0;
}
-static int send_client(struct rnh *rnh, struct zserv *client,
- enum rnh_type type, vrf_id_t vrf_id)
+int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
+ enum rnh_type type, vrf_id_t vrf_id,
+ uint32_t srte_color)
{
struct stream *s = NULL;
struct route_entry *re;
@@ -1129,6 +1119,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,
struct nexthop *nh;
struct route_node *rn;
int ret;
+ uint32_t message = 0;
int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE
: ZEBRA_NEXTHOP_UPDATE;
@@ -1140,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client,
zclient_create_header(s, cmd, vrf_id);
+ /* Message flags. */
+ if (srte_color)
+ SET_FLAG(message, ZAPI_MESSAGE_SRTE);
+ stream_putl(s, message);
+
stream_putw(s, rn->p.family);
switch (rn->p.family) {
case AF_INET:
@@ -1156,6 +1152,9 @@ static int send_client(struct rnh *rnh, struct zserv *client,
__func__, rn->p.family);
goto failure;
}
+ if (srte_color)
+ stream_putl(s, srte_color);
+
if (re) {
struct zapi_nexthop znh;
struct nexthop_group *nhg;
@@ -1172,7 +1171,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,
for (ALL_NEXTHOPS_PTR(nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
- ret = zapi_nexthop_encode(s, &znh, 0/*flags*/);
+ ret = zapi_nexthop_encode(s, &znh, 0, message);
if (ret < 0)
goto failure;
@@ -1180,15 +1179,16 @@ static int send_client(struct rnh *rnh, struct zserv *client,
}
nhg = rib_get_fib_backup_nhg(re);
- if (nhg == zebra_nhg_get_backup_nhg(re->nhe))
- nhg = NULL;
-
if (nhg) {
for (ALL_NEXTHOPS_PTR(nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
- zapi_nexthop_encode(s, &znh,
- 0 /* flags */);
+ ret = zapi_nexthop_encode(
+ s, &znh, 0 /* flags */,
+ 0 /* message */);
+ if (ret < 0)
+ goto failure;
+
num++;
}
}
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index e744504920..ba12b1738f 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -50,6 +50,9 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid,
extern void zebra_free_rnh(struct rnh *rnh);
extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
enum rnh_type type, vrf_id_t vrfid);
+extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
+ enum rnh_type type, vrf_id_t vrf_id,
+ uint32_t srte_color);
extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *);
extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *);
extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
index 9b2a58fd17..8155f9acfe 100644
--- a/zebra/zebra_routemap.c
+++ b/zebra/zebra_routemap.c
@@ -351,7 +351,7 @@ static int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype,
return CMD_SUCCESS;
}
-DEFPY(
+DEFPY_YANG(
match_ip_address_prefix_len, match_ip_address_prefix_len_cmd,
"match ip address prefix-len (0-32)$length",
MATCH_STR
@@ -371,7 +371,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_address_prefix_len, no_match_ip_address_prefix_len_cmd,
"no match ip address prefix-len [(0-32)]",
NO_STR
@@ -388,7 +388,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_address_prefix_len, match_ipv6_address_prefix_len_cmd,
"match ipv6 address prefix-len (0-128)$length",
MATCH_STR
@@ -408,7 +408,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_address_prefix_len, no_match_ipv6_address_prefix_len_cmd,
"no match ipv6 address prefix-len [(0-128)]",
NO_STR
@@ -425,7 +425,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_nexthop_prefix_len, match_ip_nexthop_prefix_len_cmd,
"match ip next-hop prefix-len (0-32)$length",
MATCH_STR
@@ -446,7 +446,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_nexthop_prefix_len, no_match_ip_nexthop_prefix_len_cmd,
"no match ip next-hop prefix-len [(0-32)]",
NO_STR
@@ -464,7 +464,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_source_protocol, match_source_protocol_cmd,
"match source-protocol " FRR_REDIST_STR_ZEBRA "$proto",
MATCH_STR
@@ -482,7 +482,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_source_protocol, no_match_source_protocol_cmd,
"no match source-protocol [" FRR_REDIST_STR_ZEBRA "]",
NO_STR
@@ -497,7 +497,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_source_instance, match_source_instance_cmd,
"match source-instance (0-255)$instance",
MATCH_STR
@@ -515,7 +515,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_source_instance, no_match_source_instance_cmd,
"no match source-instance [(0-255)]",
NO_STR MATCH_STR
@@ -531,7 +531,7 @@ DEFPY(
/* set functions */
-DEFPY(
+DEFPY_YANG(
set_src, set_src_cmd,
"set src <A.B.C.D$addrv4|X:X::X:X$addrv6>",
SET_STR
@@ -558,7 +558,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_src, no_set_src_cmd,
"no set src [<A.B.C.D|X:X::X:X>]",
NO_STR
@@ -605,7 +605,7 @@ DEFUN (no_zebra_route_map_timer,
return (CMD_SUCCESS);
}
-DEFPY (ip_protocol,
+DEFPY_YANG (ip_protocol,
ip_protocol_cmd,
"ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA
" $proto route-map ROUTE-MAP$rmap",
@@ -639,7 +639,7 @@ DEFPY (ip_protocol,
return ret;
}
-DEFPY (no_ip_protocol,
+DEFPY_YANG (no_ip_protocol,
no_ip_protocol_cmd,
"no ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA
" $proto [route-map ROUTE-MAP$rmap]",
@@ -673,7 +673,7 @@ DEFPY (no_ip_protocol,
return ret;
}
-DEFPY (show_ip_protocol,
+DEFPY_YANG (show_ip_protocol,
show_ip_protocol_cmd,
"show ip protocol [vrf <NAME$vrf_name|all$vrf_all>]",
SHOW_STR
@@ -686,7 +686,7 @@ DEFPY (show_ip_protocol,
return ret;
}
-DEFPY (ipv6_protocol,
+DEFPY_YANG (ipv6_protocol,
ipv6_protocol_cmd,
"ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA
" $proto route-map ROUTE-MAP$rmap",
@@ -720,7 +720,7 @@ DEFPY (ipv6_protocol,
return ret;
}
-DEFPY (no_ipv6_protocol,
+DEFPY_YANG (no_ipv6_protocol,
no_ipv6_protocol_cmd,
"no ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA
" $proto [route-map ROUTE-MAP$rmap]",
@@ -754,7 +754,7 @@ DEFPY (no_ipv6_protocol,
return ret;
}
-DEFPY (show_ipv6_protocol,
+DEFPY_YANG (show_ipv6_protocol,
show_ipv6_protocol_cmd,
"show ipv6 protocol [vrf <NAME$vrf_name|all$vrf_all>]",
SHOW_STR
@@ -767,7 +767,7 @@ DEFPY (show_ipv6_protocol,
return ret;
}
-DEFPY (ip_protocol_nht_rmap,
+DEFPY_YANG (ip_protocol_nht_rmap,
ip_protocol_nht_rmap_cmd,
"ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA
" $proto route-map ROUTE-MAP$rmap",
@@ -802,7 +802,7 @@ DEFPY (ip_protocol_nht_rmap,
return ret;
}
-DEFPY (no_ip_protocol_nht_rmap,
+DEFPY_YANG (no_ip_protocol_nht_rmap,
no_ip_protocol_nht_rmap_cmd,
"no ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA
" $proto route-map [ROUTE-MAP$rmap]",
@@ -836,7 +836,7 @@ DEFPY (no_ip_protocol_nht_rmap,
return ret;
}
-DEFPY (show_ip_protocol_nht,
+DEFPY_YANG (show_ip_protocol_nht,
show_ip_protocol_nht_cmd,
"show ip nht route-map [vrf <NAME$vrf_name|all$vrf_all>]",
SHOW_STR
@@ -850,7 +850,7 @@ DEFPY (show_ip_protocol_nht,
return ret;
}
-DEFPY (ipv6_protocol_nht_rmap,
+DEFPY_YANG (ipv6_protocol_nht_rmap,
ipv6_protocol_nht_rmap_cmd,
"ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA
" $proto route-map ROUTE-MAP$rmap",
@@ -884,7 +884,7 @@ DEFPY (ipv6_protocol_nht_rmap,
return ret;
}
-DEFPY (no_ipv6_protocol_nht_rmap,
+DEFPY_YANG (no_ipv6_protocol_nht_rmap,
no_ipv6_protocol_nht_rmap_cmd,
"no ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA
" $proto [route-map ROUTE-MAP$rmap]",
@@ -918,7 +918,7 @@ DEFPY (no_ipv6_protocol_nht_rmap,
return ret;
}
-DEFPY (show_ipv6_protocol_nht,
+DEFPY_YANG (show_ipv6_protocol_nht,
show_ipv6_protocol_nht_cmd,
"show ipv6 nht route-map [vrf <NAME$vrf_name|all$vrf_all>]",
SHOW_STR
diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c
index 61fef8779f..66f2924555 100644
--- a/zebra/zebra_router.c
+++ b/zebra/zebra_router.c
@@ -133,6 +133,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf,
info->zvrf = zvrf;
info->afi = afi;
info->safi = safi;
+ info->table_id = tableid;
route_table_set_info(zrt->table, info);
zrt->table->cleanup = zebra_rtable_node_cleanup;
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 863c5fa71c..f73a8f2d59 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -125,6 +125,12 @@ struct zebra_router {
/* L3-VNI hash table (for EVPN). Only in default instance */
struct hash *l3vni_table;
+ /* Tables and other global info maintained for EVPN multihoming */
+ struct zebra_evpn_mh_info *mh_info;
+
+ /* EVPN MH broadcast domains indexed by the VID */
+ struct hash *evpn_vlan_table;
+
struct hash *rules_hash;
struct hash *ipset_hash;
diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c
new file mode 100644
index 0000000000..d6043534e3
--- /dev/null
+++ b/zebra/zebra_srte.c
@@ -0,0 +1,378 @@
+/* Zebra SR-TE code
+ * Copyright (C) 2020 NetDEF, 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 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
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/lib_errors.h"
+
+#include "zebra/zebra_srte.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/zapi_msg.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy")
+
+static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy);
+
+/* Generate rb-tree of SR Policy instances. */
+static inline int
+zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a,
+ const struct zebra_sr_policy *b)
+{
+ return sr_policy_compare(&a->endpoint, &b->endpoint, a->color,
+ b->color);
+}
+RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry,
+ zebra_sr_policy_instance_compare)
+
+struct zebra_sr_policy_instance_head zebra_sr_policy_instances =
+ RB_INITIALIZER(&zebra_sr_policy_instances);
+
+struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color,
+ struct ipaddr *endpoint, char *name)
+{
+ struct zebra_sr_policy *policy;
+
+ policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy));
+ policy->color = color;
+ policy->endpoint = *endpoint;
+ strlcpy(policy->name, name, sizeof(policy->name));
+ policy->status = ZEBRA_SR_POLICY_DOWN;
+ RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
+ policy);
+
+ return policy;
+}
+
+void zebra_sr_policy_del(struct zebra_sr_policy *policy)
+{
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_deactivate(policy);
+ RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
+ policy);
+ XFREE(MTYPE_ZEBRA_SR_POLICY, policy);
+}
+
+struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color,
+ struct ipaddr *endpoint)
+{
+ struct zebra_sr_policy policy = {};
+
+ policy.color = color;
+ policy.endpoint = *endpoint;
+ return RB_FIND(zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances, &policy);
+}
+
+struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name)
+{
+ struct zebra_sr_policy *policy;
+
+ // TODO: create index for policy names
+ RB_FOREACH (policy, zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances) {
+ if (strcmp(policy->name, name) == 0)
+ return policy;
+ }
+
+ return NULL;
+}
+
+static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy,
+ struct zserv *client)
+{
+ const zebra_nhlfe_t *nhlfe;
+ struct stream *s;
+ uint32_t message = 0;
+ unsigned long nump = 0;
+ uint8_t num;
+ struct zapi_nexthop znh;
+ int ret;
+
+ /* Get output stream. */
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf));
+
+ /* Message flags. */
+ SET_FLAG(message, ZAPI_MESSAGE_SRTE);
+ stream_putl(s, message);
+
+ switch (policy->endpoint.ipa_type) {
+ case IPADDR_V4:
+ stream_putw(s, AF_INET);
+ stream_putc(s, IPV4_MAX_BITLEN);
+ stream_put_in_addr(s, &policy->endpoint.ipaddr_v4);
+ break;
+ case IPADDR_V6:
+ stream_putw(s, AF_INET6);
+ stream_putc(s, IPV6_MAX_BITLEN);
+ stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN);
+ break;
+ default:
+ flog_warn(EC_LIB_DEVELOPMENT,
+ "%s: unknown policy endpoint address family: %u",
+ __func__, policy->endpoint.ipa_type);
+ exit(1);
+ }
+ stream_putl(s, policy->color);
+
+ num = 0;
+ frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) {
+ if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
+ continue;
+
+ if (num == 0) {
+ stream_putc(s, re_type_from_lsp_type(nhlfe->type));
+ stream_putw(s, 0); /* instance - not available */
+ stream_putc(s, nhlfe->distance);
+ stream_putl(s, 0); /* metric - not available */
+ nump = stream_get_endp(s);
+ stream_putc(s, 0);
+ }
+
+ zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop);
+ ret = zapi_nexthop_encode(s, &znh, 0, message);
+ if (ret < 0)
+ goto failure;
+
+ num++;
+ }
+ stream_putc_at(s, nump, num);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ client->nh_last_upd_time = monotime(NULL);
+ client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE;
+ return zserv_send_message(client, s);
+
+failure:
+
+ stream_free(s);
+ return -1;
+}
+
+static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy)
+{
+ struct rnh *rnh;
+ struct prefix p = {};
+ struct zebra_vrf *zvrf;
+ struct listnode *node;
+ struct zserv *client;
+
+ zvrf = policy->zvrf;
+ switch (policy->endpoint.ipa_type) {
+ case IPADDR_V4:
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = policy->endpoint.ipaddr_v4;
+ break;
+ case IPADDR_V6:
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = policy->endpoint.ipaddr_v6;
+ break;
+ default:
+ flog_warn(EC_LIB_DEVELOPMENT,
+ "%s: unknown policy endpoint address family: %u",
+ __func__, policy->endpoint.ipa_type);
+ exit(1);
+ }
+
+ rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE);
+ if (!rnh)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_notify_update_client(policy, client);
+ else
+ /* Fallback to the IGP shortest path. */
+ zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE,
+ zvrf_id(zvrf), policy->color);
+ }
+}
+
+static void zebra_sr_policy_activate(struct zebra_sr_policy *policy,
+ zebra_lsp_t *lsp)
+{
+ policy->status = ZEBRA_SR_POLICY_UP;
+ policy->lsp = lsp;
+ (void)zebra_sr_policy_bsid_install(policy);
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_UP);
+ zebra_sr_policy_notify_update(policy);
+}
+
+static void zebra_sr_policy_update(struct zebra_sr_policy *policy,
+ zebra_lsp_t *lsp,
+ struct zapi_srte_tunnel *old_tunnel)
+{
+ bool bsid_changed;
+ bool segment_list_changed;
+
+ policy->lsp = lsp;
+
+ bsid_changed =
+ policy->segment_list.local_label != old_tunnel->local_label;
+ segment_list_changed =
+ policy->segment_list.label_num != old_tunnel->label_num
+ || memcmp(policy->segment_list.labels, old_tunnel->labels,
+ sizeof(mpls_label_t)
+ * policy->segment_list.label_num);
+
+ /* Re-install label stack if necessary. */
+ if (bsid_changed || segment_list_changed) {
+ zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label);
+ (void)zebra_sr_policy_bsid_install(policy);
+ }
+
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_UP);
+
+ /* Handle segment-list update. */
+ if (segment_list_changed)
+ zebra_sr_policy_notify_update(policy);
+}
+
+static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy)
+{
+ policy->status = ZEBRA_SR_POLICY_DOWN;
+ policy->lsp = NULL;
+ zebra_sr_policy_bsid_uninstall(policy,
+ policy->segment_list.local_label);
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_DOWN);
+ zebra_sr_policy_notify_update(policy);
+}
+
+int zebra_sr_policy_validate(struct zebra_sr_policy *policy,
+ struct zapi_srte_tunnel *new_tunnel)
+{
+ struct zapi_srte_tunnel old_tunnel = policy->segment_list;
+ zebra_lsp_t *lsp;
+
+ if (new_tunnel)
+ policy->segment_list = *new_tunnel;
+
+ /* Try to resolve the Binding-SID nexthops. */
+ lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]);
+ if (!lsp || !lsp->best_nhlfe
+ || lsp->addr_family != ipaddr_family(&policy->endpoint)) {
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_deactivate(policy);
+ return -1;
+ }
+
+ /* First label was resolved successfully. */
+ if (policy->status == ZEBRA_SR_POLICY_DOWN)
+ zebra_sr_policy_activate(policy, lsp);
+ else
+ zebra_sr_policy_update(policy, lsp, &old_tunnel);
+
+ return 0;
+}
+
+int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy)
+{
+ struct zapi_srte_tunnel *zt = &policy->segment_list;
+ zebra_nhlfe_t *nhlfe;
+
+ if (zt->local_label == MPLS_LABEL_NONE)
+ return 0;
+
+ frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) {
+ uint8_t num_out_labels;
+ mpls_label_t *out_labels;
+ mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL;
+
+ if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
+ continue;
+
+ /*
+ * Don't push the first SID if the corresponding action in the
+ * LFIB is POP.
+ */
+ if (!nhlfe->nexthop->nh_label
+ || !nhlfe->nexthop->nh_label->num_labels
+ || nhlfe->nexthop->nh_label->label[0]
+ == MPLS_LABEL_IMPLICIT_NULL) {
+ if (zt->label_num > 1) {
+ num_out_labels = zt->label_num - 1;
+ out_labels = &zt->labels[1];
+ } else {
+ num_out_labels = 1;
+ out_labels = &null_label;
+ }
+ } else {
+ num_out_labels = zt->label_num;
+ out_labels = zt->labels;
+ }
+
+ if (mpls_lsp_install(
+ policy->zvrf, zt->type, zt->local_label,
+ num_out_labels, out_labels, nhlfe->nexthop->type,
+ &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex)
+ < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy,
+ mpls_label_t old_bsid)
+{
+ struct zapi_srte_tunnel *zt = &policy->segment_list;
+
+ mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid);
+}
+
+int zebra_sr_policy_label_update(mpls_label_t label,
+ enum zebra_sr_policy_update_label_mode mode)
+{
+ struct zebra_sr_policy *policy;
+
+ RB_FOREACH (policy, zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances) {
+ mpls_label_t next_hop_label;
+
+ next_hop_label = policy->segment_list.labels[0];
+ if (next_hop_label != label)
+ continue;
+
+ switch (mode) {
+ case ZEBRA_SR_POLICY_LABEL_CREATED:
+ case ZEBRA_SR_POLICY_LABEL_UPDATED:
+ case ZEBRA_SR_POLICY_LABEL_REMOVED:
+ zebra_sr_policy_validate(policy, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void zebra_srte_init(void)
+{
+}
diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h
new file mode 100644
index 0000000000..e5239b7b7b
--- /dev/null
+++ b/zebra/zebra_srte.h
@@ -0,0 +1,74 @@
+/* Zebra's client header.
+ * Copyright (C) 2020 Netdef, 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 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
+ */
+
+#ifndef _ZEBRA_SRTE_H
+#define _ZEBRA_SRTE_H
+
+#include "zebra/zebra_mpls.h"
+
+#include "lib/zclient.h"
+#include "lib/srte.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum zebra_sr_policy_update_label_mode {
+ ZEBRA_SR_POLICY_LABEL_CREATED = 1,
+ ZEBRA_SR_POLICY_LABEL_UPDATED = 2,
+ ZEBRA_SR_POLICY_LABEL_REMOVED = 3,
+};
+
+struct zebra_sr_policy {
+ RB_ENTRY(zebra_sr_policy) entry;
+ uint32_t color;
+ struct ipaddr endpoint;
+ char name[SRTE_POLICY_NAME_MAX_LENGTH];
+ enum zebra_sr_policy_status status;
+ struct zapi_srte_tunnel segment_list;
+ zebra_lsp_t *lsp;
+ struct zebra_vrf *zvrf;
+};
+RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy);
+RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry,
+ zebra_sr_policy_instance_compare)
+
+extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances;
+
+struct zebra_sr_policy *
+zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name);
+void zebra_sr_policy_del(struct zebra_sr_policy *policy);
+struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color,
+ struct ipaddr *endpoint);
+struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name);
+int zebra_sr_policy_validate(struct zebra_sr_policy *policy,
+ struct zapi_srte_tunnel *new_tunnel);
+int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy);
+void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy,
+ mpls_label_t old_bsid);
+void zebra_srte_init(void);
+int zebra_sr_policy_label_update(mpls_label_t label,
+ enum zebra_sr_policy_update_label_mode mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_SRTE_H */
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index ee1e251a69..d102b02a21 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -544,6 +544,7 @@ static int vrf_config_write(struct vty *vty)
zebra_routemap_config_write_protocol(vty, zvrf);
+ router_id_write(vty, zvrf);
if (zvrf_id(zvrf) != VRF_DEFAULT)
vty_endframe(vty, " exit-vrf\n!\n");
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index d262faa070..910d192317 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -92,6 +92,11 @@ struct zebra_vrf {
struct list *rid_all_sorted_list;
struct list *rid_lo_sorted_list;
struct prefix rid_user_assigned;
+ struct list _rid6_all_sorted_list;
+ struct list _rid6_lo_sorted_list;
+ struct list *rid6_all_sorted_list;
+ struct list *rid6_lo_sorted_list;
+ struct prefix rid6_user_assigned;
/*
* Back pointer to the owning namespace.
@@ -125,9 +130,9 @@ struct zebra_vrf {
#define MPLS_FLAG_SCHEDULE_LSPS (1 << 0)
/*
- * VNI hash table (for EVPN). Only in the EVPN instance.
+ * EVPN hash table. Only in the EVPN instance.
*/
- struct hash *vni_table;
+ struct hash *evpn_table;
/*
* Whether EVPN is enabled or not. Only in the EVPN instance.
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 1da2660509..b6d0b26125 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -44,6 +44,7 @@
#include "zebra/zebra_routemap.h"
#include "lib/json.h"
#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn_mh.h"
#ifndef VTYSH_EXTRACT_PL
#include "zebra/zebra_vty_clippy.c"
#endif
@@ -55,6 +56,8 @@
#include "zebra/zebra_nhg.h"
#include "zebra/interface.h"
#include "northbound_cli.h"
+#include "zebra/zebra_nb.h"
+#include "zebra/kernel_netlink.h"
extern int allow_delete;
@@ -71,6 +74,12 @@ static void vty_show_ip_route_summary(struct vty *vty,
static void vty_show_ip_route_summary_prefix(struct vty *vty,
struct route_table *table,
bool use_json);
+/* Helper api to format a nexthop in the 'detailed' output path. */
+static void show_nexthop_detail_helper(struct vty *vty,
+ const struct route_entry *re,
+ const struct nexthop *nexthop,
+ bool is_backup);
+
DEFUN (ip_multicast_mode,
ip_multicast_mode_cmd,
@@ -166,11 +175,24 @@ DEFUN (show_ip_rpf_addr,
}
static char re_status_output_char(const struct route_entry *re,
- const struct nexthop *nhop)
+ const struct nexthop *nhop,
+ bool is_fib)
{
if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
- if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) &&
- !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
+ bool star_p = false;
+
+ if (nhop &&
+ !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) &&
+ !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) {
+ /* More-specific test for 'fib' output */
+ if (is_fib) {
+ star_p = !!CHECK_FLAG(nhop->flags,
+ NEXTHOP_FLAG_FIB);
+ } else
+ star_p = true;
+ }
+
+ if (star_p)
return '*';
else
return ' ';
@@ -190,19 +212,51 @@ static char re_status_output_char(const struct route_entry *re,
}
/*
- * TODO -- Show backup nexthop info
+ * Show backup nexthop info, in the 'detailed' output path
*/
static void show_nh_backup_helper(struct vty *vty,
- const struct nhg_hash_entry *nhe,
+ const struct route_entry *re,
const struct nexthop *nexthop)
{
+ const struct nexthop *start, *backup, *temp;
+ int i, idx;
+
/* Double-check that there _is_ a backup */
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) ||
+ re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL ||
+ re->nhe->backup_info->nhe->nhg.nexthop == NULL)
return;
- /* Locate the backup nexthop */
+ /* Locate the backup nexthop(s) */
+ start = re->nhe->backup_info->nhe->nhg.nexthop;
+ for (i = 0; i < nexthop->backup_num; i++) {
+ /* Format the backup(s) (indented) */
+ backup = start;
+ for (idx = 0; idx < nexthop->backup_idx[i]; idx++) {
+ backup = backup->next;
+ if (backup == NULL)
+ break;
+ }
- /* Format the backup (indented) */
+ /* It's possible for backups to be recursive too,
+ * so walk the recursive resolution list if present.
+ */
+ temp = backup;
+ while (backup) {
+ vty_out(vty, " ");
+ show_nexthop_detail_helper(vty, re, backup,
+ true /*backup*/);
+ vty_out(vty, "\n");
+
+ if (backup->resolved && temp == backup)
+ backup = backup->resolved;
+ else
+ backup = nexthop_next(backup);
+
+ if (backup == temp->next)
+ break;
+ }
+ }
}
@@ -212,14 +266,20 @@ static void show_nh_backup_helper(struct vty *vty,
*/
static void show_nexthop_detail_helper(struct vty *vty,
const struct route_entry *re,
- const struct nexthop *nexthop)
+ const struct nexthop *nexthop,
+ bool is_backup)
{
char addrstr[32];
char buf[MPLS_LABEL_STRLEN];
+ int i;
- vty_out(vty, " %c%s",
- re_status_output_char(re, nexthop),
- nexthop->rparent ? " " : "");
+ if (is_backup)
+ vty_out(vty, " b%s",
+ nexthop->rparent ? " " : "");
+ else
+ vty_out(vty, " %c%s",
+ re_status_output_char(re, nexthop, false),
+ nexthop->rparent ? " " : "");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -333,6 +393,13 @@ static void show_nexthop_detail_helper(struct vty *vty,
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
+
+ for (i = 1; i < nexthop->backup_num; i++)
+ vty_out(vty, ",%d", nexthop->backup_idx[i]);
+ }
}
/* New RIB. Detailed information for IPv4 route. */
@@ -403,12 +470,13 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
/* Use helper to format each nexthop */
- show_nexthop_detail_helper(vty, re, nexthop);
+ show_nexthop_detail_helper(vty, re, nexthop,
+ false /*not backup*/);
vty_out(vty, "\n");
- /* Include backup info, if present */
+ /* Include backup(s), if present */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- show_nh_backup_helper(vty, re->nhe, nexthop);
+ show_nh_backup_helper(vty, re, nexthop);
}
vty_out(vty, "\n");
}
@@ -422,6 +490,7 @@ static void show_route_nexthop_helper(struct vty *vty,
const struct nexthop *nexthop)
{
char buf[MPLS_LABEL_STRLEN];
+ int i;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -494,6 +563,10 @@ static void show_route_nexthop_helper(struct vty *vty,
if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
sizeof(buf)))
vty_out(vty, ", src %s", buf);
+ /* SR-TE information */
+ if (nexthop->srte_color)
+ vty_out(vty, ", SR-TE color %u",
+ nexthop->srte_color);
}
break;
case NEXTHOP_TYPE_IPV6:
@@ -519,8 +592,12 @@ static void show_route_nexthop_helper(struct vty *vty,
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, ", backup %d", nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
+
+ for (i = 1; i < nexthop->backup_num; i++)
+ vty_out(vty, ",%d", nexthop->backup_idx[i]);
+ }
}
/*
@@ -534,6 +611,8 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
char buf[SRCDEST2STR_BUFFER];
struct vrf *vrf = NULL;
json_object *json_labels = NULL;
+ json_object *json_backups = NULL;
+ int i;
json_object_int_add(json_nexthop, "flags",
nexthop->flags);
@@ -645,9 +724,17 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
json_object_boolean_true_add(json_nexthop,
"recursive");
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(json_nexthop, "backupIndex",
- nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nexthop->backup_num; i++) {
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nexthop->backup_idx[i]));
+ }
+
+ json_object_object_add(json_nexthop, "backupIndex",
+ json_backups);
+ }
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -699,24 +786,28 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
json_object_int_add(json_nexthop, "weight",
nexthop->weight);
+ if (nexthop->srte_color)
+ json_object_int_add(json_nexthop, "srteColor",
+ nexthop->srte_color);
}
static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
struct route_entry *re, json_object *json,
bool is_fib)
{
- struct nexthop *nexthop;
+ const struct nexthop *nexthop;
int len = 0;
char buf[SRCDEST2STR_BUFFER];
json_object *json_nexthops = NULL;
json_object *json_nexthop = NULL;
json_object *json_route = NULL;
time_t uptime;
- struct vrf *vrf = NULL;
- rib_dest_t *dest = rib_dest_from_rnode(rn);
- struct nexthop_group *nhg;
+ const struct vrf *vrf = NULL;
+ const rib_dest_t *dest = rib_dest_from_rnode(rn);
+ const struct nexthop_group *nhg;
char up_str[MONOTIME_STRLEN];
- bool first_p;
+ bool first_p = true;
+ bool nhg_from_backup = false;
uptime = monotime(NULL);
uptime -= re->uptime;
@@ -791,9 +882,11 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
json_nexthop = json_object_new_object();
+ show_nexthop_json_helper(json_nexthop,
+ nexthop, re);
- show_nexthop_json_helper(json_nexthop, nexthop, re);
- json_object_array_add(json_nexthops, json_nexthop);
+ json_object_array_add(json_nexthops,
+ json_nexthop);
}
json_object_object_add(json_route, "nexthops", json_nexthops);
@@ -804,7 +897,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
else
nhg = zebra_nhg_get_backup_nhg(re->nhe);
- if (nhg) {
+ if (nhg && nhg->nexthop) {
json_nexthops = json_object_new_array();
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
@@ -824,42 +917,62 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
return;
}
+ /* Prefix information, and first nexthop. If we're showing 'fib',
+ * and there are no installed primary nexthops, see if there are any
+ * backup nexthops and start with those.
+ */
+ if (is_fib && nhg->nexthop == NULL) {
+ nhg = rib_get_fib_backup_nhg(re);
+ nhg_from_backup = true;
+ }
+
+ len = vty_out(vty, "%c", zebra_route_char(re->type));
+ if (re->instance)
+ len += vty_out(vty, "[%d]", re->instance);
+ if (nhg_from_backup && nhg->nexthop) {
+ len += vty_out(
+ vty, "%cb%c %s",
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
+ re_status_output_char(re, nhg->nexthop, is_fib),
+ srcdest_rnode2str(rn, buf, sizeof(buf)));
+ } else {
+ len += vty_out(
+ vty, "%c%c %s",
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
+ re_status_output_char(re, nhg->nexthop, is_fib),
+ srcdest_rnode2str(rn, buf, sizeof(buf)));
+ }
+
+ /* Distance and metric display. */
+ if (((re->type == ZEBRA_ROUTE_CONNECT) &&
+ (re->distance || re->metric)) ||
+ (re->type != ZEBRA_ROUTE_CONNECT))
+ len += vty_out(vty, " [%u/%u]", re->distance,
+ re->metric);
+
/* Nexthop information. */
- first_p = true;
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
if (first_p) {
first_p = false;
-
- /* Prefix information. */
- len = vty_out(vty, "%c", zebra_route_char(re->type));
- if (re->instance)
- len += vty_out(vty, "[%d]", re->instance);
- len += vty_out(
- vty, "%c%c %s",
- CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)
- ? '>'
- : ' ',
- re_status_output_char(re, nexthop),
- srcdest_rnode2str(rn, buf, sizeof(buf)));
-
- /* Distance and metric display. */
- if (((re->type == ZEBRA_ROUTE_CONNECT) &&
- (re->distance || re->metric)) ||
- (re->type != ZEBRA_ROUTE_CONNECT))
- len += vty_out(vty, " [%u/%u]", re->distance,
- re->metric);
+ } else if (nhg_from_backup) {
+ vty_out(vty, " b%c%*c",
+ re_status_output_char(re, nexthop, is_fib),
+ len - 3 + (2 * nexthop_level(nexthop)), ' ');
} else {
vty_out(vty, " %c%*c",
- re_status_output_char(re, nexthop),
+ re_status_output_char(re, nexthop, is_fib),
len - 3 + (2 * nexthop_level(nexthop)), ' ');
}
show_route_nexthop_helper(vty, re, nexthop);
-
vty_out(vty, ", %s\n", up_str);
}
- /* Check for backup info if present */
+ /* If we only had backup nexthops, we're done */
+ if (nhg_from_backup)
+ return;
+
+ /* Check for backup nexthop info if present */
if (is_fib)
nhg = rib_get_fib_backup_nhg(re);
else
@@ -1206,7 +1319,7 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
if (CHECK_FLAG(nexthop->flags,
NEXTHOP_FLAG_HAS_BACKUP))
vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
+ nexthop->backup_idx[0]);
vty_out(vty, "\n");
continue;
@@ -1214,22 +1327,13 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
/* TODO -- print more useful backup info */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- struct nexthop *backup;
int i;
- i = 0;
- for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) {
- if (i == nexthop->backup_idx)
- break;
- i++;
- }
+ vty_out(vty, "[backup");
+ for (i = 0; i < nexthop->backup_num; i++)
+ vty_out(vty, " %d", nexthop->backup_idx[i]);
- /* TODO */
- if (backup)
- vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
- else
- vty_out(vty, " [backup INVALID]");
+ vty_out(vty, "]");
}
vty_out(vty, "\n");
@@ -2304,12 +2408,9 @@ DEFUN (vrf_vni_mapping,
"VNI-ID\n"
"prefix-routes-only\n")
{
- int ret = 0;
int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
- vni_t vni = strtoul(argv[1]->arg, NULL, 10);
- char err[ERR_STR_SZ];
assert(vrf);
assert(zvrf);
@@ -2317,14 +2418,15 @@ DEFUN (vrf_vni_mapping,
if (argc == 3)
filter = 1;
- /* Mark as having FRR configuration */
- vrf_set_user_cfged(vrf);
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
- filter, 1);
- if (ret != 0) {
- vty_out(vty, "%s\n", err);
- return CMD_WARNING;
- }
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY,
+ argv[1]->arg);
+
+ if (filter)
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
+ NB_OP_MODIFY, "true");
+
+ nb_cli_apply_changes(vty, NULL);
return CMD_SUCCESS;
}
@@ -2337,12 +2439,10 @@ DEFUN (no_vrf_vni_mapping,
"VNI-ID\n"
"prefix-routes-only\n")
{
- int ret = 0;
int filter = 0;
- char err[ERR_STR_SZ];
- vni_t vni = strtoul(argv[2]->arg, NULL, 10);
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ vni_t vni = strtoul(argv[2]->arg, NULL, 10);
assert(vrf);
assert(zvrf);
@@ -2350,16 +2450,22 @@ DEFUN (no_vrf_vni_mapping,
if (argc == 4)
filter = 1;
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err,
- ERR_STR_SZ, filter, 0);
- if (ret != 0) {
- vty_out(vty, "%s\n", err);
+ if (zvrf->l3vni != vni) {
+ vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni,
+ zvrf->vrf->name);
return CMD_WARNING;
}
- /* If no other FRR config for this VRF, mark accordingly. */
- if (!zebra_vrf_has_config(zvrf))
- vrf_reset_user_cfged(vrf);
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY,
+ argv[2]->arg);
+
+ if (filter)
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
+ NB_OP_DESTROY, "true");
+
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL);
+
+ nb_cli_apply_changes(vty, NULL);
return CMD_SUCCESS;
}
@@ -2419,6 +2525,81 @@ DEFUN (show_evpn_global,
return CMD_SUCCESS;
}
+DEFPY(show_evpn_es,
+ show_evpn_es_cmd,
+ "show evpn es [NAME$esi_str] [json$json] [detail$detail]",
+ SHOW_STR
+ "EVPN\n"
+ "Ethernet Segment\n"
+ "ES ID\n"
+ JSON_STR
+ "Detailed information\n")
+{
+ esi_t esi;
+ bool uj = !!json;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ zebra_evpn_es_show_esi(vty, uj, &esi);
+ } else {
+ if (detail)
+ zebra_evpn_es_show_detail(vty, uj);
+ else
+ zebra_evpn_es_show(vty, uj);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_evpn_es_evi,
+ show_evpn_es_evi_cmd,
+ "show evpn es-evi [vni (1-16777215)$vni] [json$json] [detail$detail]",
+ SHOW_STR
+ "EVPN\n"
+ "Ethernet Segment per EVI\n"
+ "VxLAN Network Identifier\n"
+ "VNI\n"
+ JSON_STR
+ "Detailed information\n")
+{
+ bool uj = !!json;
+ bool ud = !!detail;
+
+ if (vni)
+ zebra_evpn_es_evi_show_vni(vty, uj, vni, ud);
+ else
+ zebra_evpn_es_evi_show(vty, uj, ud);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_evpn_access_vlan,
+ show_evpn_access_vlan_cmd,
+ "show evpn access-vlan [(1-4094)$vid] [json$json] [detail$detail]",
+ SHOW_STR
+ "EVPN\n"
+ "Access VLANs\n"
+ "VLAN ID\n"
+ JSON_STR
+ "Detailed information\n")
+{
+ bool uj = !!json;
+
+ if (vid) {
+ zebra_evpn_acc_vl_show_vid(vty, uj, vid);
+ } else {
+ if (detail)
+ zebra_evpn_acc_vl_show_detail(vty, uj);
+ else
+ zebra_evpn_acc_vl_show(vty, uj);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_vni,
show_evpn_vni_cmd,
"show evpn vni [json]",
@@ -3217,6 +3398,11 @@ static int config_write_protocol(struct vty *vty)
if (!zebra_nhg_kernel_nexthops_enabled())
vty_out(vty, "no zebra nexthop kernel enable\n");
+#ifdef HAVE_NETLINK
+ /* Include netlink info */
+ netlink_config_write_helper(vty);
+#endif /* HAVE_NETLINK */
+
return 1;
}
@@ -3236,8 +3422,7 @@ DEFUN (show_zebra,
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
struct zebra_vrf *zvrf = vrf->info;
- vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64
- " %10" PRIu64 " %10" PRIu64 "\n",
+ vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64" %10" PRIu64 " %10" PRIu64 "\n",
vrf->name, zvrf->installs, zvrf->removals,
zvrf->neigh_updates, zvrf->lsp_installs,
zvrf->lsp_removals);
@@ -3469,9 +3654,6 @@ static int config_write_table(struct vty *vty)
/* IPForwarding configuration write function. */
static int config_write_forwarding(struct vty *vty)
{
- /* FIXME: Find better place for that. */
- router_id_write(vty);
-
if (!ipforward())
vty_out(vty, "no ip forwarding\n");
if (!ipforward_ipv6())
@@ -3543,6 +3725,44 @@ DEFUN_HIDDEN (show_frr,
return CMD_SUCCESS;
}
+#ifdef HAVE_NETLINK
+DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf,
+ zebra_kernel_netlink_batch_tx_buf_cmd,
+ "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)",
+ ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ uint32_t bufsize = 0, threshold = 0;
+
+ bufsize = strtoul(argv[4]->arg, NULL, 10);
+ threshold = strtoul(argv[5]->arg, NULL, 10);
+
+ netlink_set_batch_buffer_size(bufsize, threshold, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
+ no_zebra_kernel_netlink_batch_tx_buf_cmd,
+ "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]",
+ NO_STR ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ netlink_set_batch_buffer_size(0, 0, false);
+
+ return CMD_SUCCESS;
+}
+
+#endif /* HAVE_NETLINK */
+
/* IP node for static routes. */
static int zebra_ip_config(struct vty *vty);
static struct cmd_node ip_node = {
@@ -3641,6 +3861,9 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_evpn_vni_cmd);
install_element(VIEW_NODE, &show_evpn_vni_detail_cmd);
install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_es_cmd);
+ install_element(VIEW_NODE, &show_evpn_es_evi_cmd);
+ install_element(VIEW_NODE, &show_evpn_access_vlan_cmd);
install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd);
install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd);
install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd);
@@ -3677,5 +3900,10 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);
+#ifdef HAVE_NETLINK
+ install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);
+ install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd);
+#endif /* HAVE_NETLINK */
+
install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd);
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 998c035656..13a58bc34a 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -49,75 +49,31 @@
#include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_evpn.h"
+#include "zebra/zebra_evpn_mac.h"
+#include "zebra/zebra_evpn_neigh.h"
#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_evpn_mh.h"
+#include "zebra/zebra_evpn_vxlan.h"
#include "zebra/zebra_router.h"
DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix");
-DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash");
-DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
-DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
-DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
+DEFINE_MTYPE_STATIC(ZEBRA, L3VNI_MAC, "EVPN L3VNI MAC");
+DEFINE_MTYPE_STATIC(ZEBRA, L3NEIGH, "EVPN Neighbor");
DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group");
DEFINE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason), (rmac, zl3vni, delete, reason))
-/* definitions */
-/* PMSI strings. */
-#define VXLAN_FLOOD_STR_NO_INFO "-"
-#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO
-static const struct message zvtep_flood_str[] = {
- {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO},
- {VXLAN_FLOOD_PIM_SM, "PIM-SM"},
- {VXLAN_FLOOD_HEAD_END_REPL, "HER"},
- {0}
-};
-
-
/* static function declarations */
-static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
- uint16_t cmd);
-static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
-static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt);
-static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt);
-static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket,
- void **args);
+static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket,
+ void **args);
static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
json_object *json);
static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
json_object *json);
-static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json);
-static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt);
-static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt);
-static void zvni_print(zebra_vni_t *zvni, void **ctxt);
-static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]);
-
-static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
- struct ipaddr *ip, uint8_t flags,
- uint32_t seq, int state, uint16_t cmd);
-static unsigned int neigh_hash_keymake(const void *p);
-static void *zvni_neigh_alloc(void *p);
-static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
- struct ethaddr *mac);
-static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n);
-static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
- uint32_t flags);
-static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip);
-static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr,
- uint8_t flags, uint32_t seq);
-static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr,
- uint8_t flags, int state);
-static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
-static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n);
-static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n);
-static zebra_vni_t *zvni_from_svi(struct interface *ifp,
- struct interface *br_if);
-static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if);
-static struct interface *zvni_map_to_macvlan(struct interface *br_if,
- struct interface *svi_if);
+static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt);
/* l3-vni next-hop neigh related APIs */
static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
@@ -142,75 +98,13 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
/* l3-vni related APIs*/
-static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
static void *zl3vni_alloc(void *p);
static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
static int zl3vni_del(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
-static unsigned int mac_hash_keymake(const void *p);
-static bool mac_cmp(const void *p1, const void *p2);
-static void *zvni_mac_alloc(void *p);
-static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr);
-static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac);
-static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
- uint32_t flags);
-static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr);
-static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
- uint8_t flags, uint32_t seq);
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr);
-static zebra_vni_t *zvni_map_vlan(struct interface *ifp,
- struct interface *br_if, vlanid_t vid);
-static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac);
-static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac);
-static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt);
-
-static unsigned int vni_hash_keymake(const void *p);
-static void *zvni_alloc(void *p);
-static zebra_vni_t *zvni_lookup(vni_t vni);
-static zebra_vni_t *zvni_add(vni_t vni);
-static int zvni_del(zebra_vni_t *zvni);
-static int zvni_send_add_to_client(zebra_vni_t *zvni);
-static int zvni_send_del_to_client(vni_t vni);
-static void zvni_build_hash_table(void);
-static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep);
-static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip);
-static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip,
- int flood_control);
-static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep);
-static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall);
-static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep);
-static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip);
-static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni);
-static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni);
-static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
- struct ethaddr *macaddr, struct ipaddr *ip);
-static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
- struct ipaddr *ip);
-struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp);
-static int advertise_gw_macip_enabled(zebra_vni_t *zvni);
-static int advertise_svi_macip_enabled(zebra_vni_t *zvni);
-static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf,
- zebra_mac_t *old_zmac,
- zebra_mac_t *new_zmac,
- zebra_neigh_t *nbr);
-static int remote_neigh_count(zebra_mac_t *zmac);
-static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac);
-static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t);
-static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t);
-static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf,
- zebra_neigh_t *nbr,
- struct in_addr vtep_ip,
- bool do_dad,
- bool *is_dup_detect,
- bool is_local);
-static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
- zebra_mac_t *mac,
- struct in_addr vtep_ip,
- bool do_dad,
- bool *is_dup_detect,
- bool is_local);
+static void zevpn_build_hash_table(void);
static unsigned int zebra_vxlan_sg_hash_key_make(const void *p);
static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2);
static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf,
@@ -223,9 +117,6 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip,
struct in_addr mcast_grp);
static void zebra_vxlan_sg_cleanup(struct hash_bucket *bucket, void *arg);
-static void zvni_send_mac_to_client(zebra_vni_t *zvn);
-static void zvni_send_neigh_to_client(zebra_vni_t *zvni);
-
/* Private functions */
static int host_rb_entry_compare(const struct host_rb_entry *hle1,
const struct host_rb_entry *hle2)
@@ -273,677 +164,14 @@ static uint32_t rb_host_count(struct host_rb_tree_entry *hrbe)
}
/*
- * Return number of valid MACs in a VNI's MAC hash table - all
- * remote MACs and non-internal (auto) local MACs count.
- */
-static uint32_t num_valid_macs(zebra_vni_t *zvni)
-{
- unsigned int i;
- uint32_t num_macs = 0;
- struct hash *hash;
- struct hash_bucket *hb;
- zebra_mac_t *mac;
-
- hash = zvni->mac_table;
- if (!hash)
- return num_macs;
- for (i = 0; i < hash->size; i++) {
- for (hb = hash->index[i]; hb; hb = hb->next) {
- mac = (zebra_mac_t *)hb->data;
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
- || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
- || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
- num_macs++;
- }
- }
-
- return num_macs;
-}
-
-static uint32_t num_dup_detected_macs(zebra_vni_t *zvni)
-{
- unsigned int i;
- uint32_t num_macs = 0;
- struct hash *hash;
- struct hash_bucket *hb;
- zebra_mac_t *mac;
-
- hash = zvni->mac_table;
- if (!hash)
- return num_macs;
- for (i = 0; i < hash->size; i++) {
- for (hb = hash->index[i]; hb; hb = hb->next) {
- mac = (zebra_mac_t *)hb->data;
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- num_macs++;
- }
- }
-
- return num_macs;
-}
-
-static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni)
-{
- unsigned int i;
- uint32_t num_neighs = 0;
- struct hash *hash;
- struct hash_bucket *hb;
- zebra_neigh_t *nbr;
-
- hash = zvni->neigh_table;
- if (!hash)
- return num_neighs;
- for (i = 0; i < hash->size; i++) {
- for (hb = hash->index[i]; hb; hb = hb->next) {
- nbr = (zebra_neigh_t *)hb->data;
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
- num_neighs++;
- }
- }
-
- return num_neighs;
-}
-
-static int advertise_gw_macip_enabled(zebra_vni_t *zvni)
-{
- struct zebra_vrf *zvrf;
-
- zvrf = zebra_vrf_get_evpn();
- if (zvrf && zvrf->advertise_gw_macip)
- return 1;
-
- if (zvni && zvni->advertise_gw_macip)
- return 1;
-
- return 0;
-}
-
-static int advertise_svi_macip_enabled(zebra_vni_t *zvni)
-{
- struct zebra_vrf *zvrf;
-
- zvrf = zebra_vrf_get_evpn();
- if (zvrf && zvrf->advertise_svi_macip)
- return 1;
-
- if (zvni && zvni->advertise_svi_macip)
- return 1;
-
- return 0;
-}
-
-/* As part Duplicate Address Detection (DAD) for IP mobility
- * MAC binding changes, ensure to inherit duplicate flag
- * from MAC.
+ * Print neighbors for all EVPN.
*/
-static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf,
- zebra_mac_t *old_zmac,
- zebra_mac_t *new_zmac,
- zebra_neigh_t *nbr)
-{
- bool is_old_mac_dup = false;
- bool is_new_mac_dup = false;
-
- if (!zvrf->dup_addr_detect)
- return 0;
- /* Check old or new MAC is detected as duplicate
- * mark this neigh as duplicate
- */
- if (old_zmac)
- is_old_mac_dup = CHECK_FLAG(old_zmac->flags,
- ZEBRA_MAC_DUPLICATE);
- if (new_zmac)
- is_new_mac_dup = CHECK_FLAG(new_zmac->flags,
- ZEBRA_MAC_DUPLICATE);
- /* Old and/or new MAC can be in duplicate state,
- * based on that IP/Neigh Inherits the flag.
- * If New MAC is marked duplicate, inherit to the IP.
- * If old MAC is duplicate but new MAC is not, clear
- * duplicate flag for IP and reset detection params
- * and let IP DAD retrigger.
- */
- if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
- SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
- /* Capture Duplicate detection time */
- nbr->dad_dup_detect_time = monotime(NULL);
- /* Mark neigh inactive */
- ZEBRA_NEIGH_SET_INACTIVE(nbr);
-
- return 1;
- } else if (is_old_mac_dup && !is_new_mac_dup) {
- UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
- nbr->dad_count = 0;
- nbr->detect_start_time.tv_sec = 0;
- nbr->detect_start_time.tv_usec = 0;
- }
- return 0;
-}
-
-static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
- zebra_mac_t *mac,
- struct in_addr vtep_ip,
- bool do_dad,
- bool *is_dup_detect,
- bool is_local)
-{
- zebra_neigh_t *nbr;
- struct listnode *node = NULL;
- struct timeval elapsed = {0, 0};
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
- bool reset_params = false;
-
- if (!(zvrf->dup_addr_detect && do_dad))
- return;
-
- /* MAC is detected as duplicate,
- * Local MAC event -> hold on advertising to BGP.
- * Remote MAC event -> hold on installing it.
- */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count,
- zvrf->dad_freeze_time);
-
- /* For duplicate MAC do not update
- * client but update neigh due to
- * this MAC update.
- */
- if (zvrf->dad_freeze)
- *is_dup_detect = true;
-
- return;
- }
-
- /* Check if detection time (M-secs) expired.
- * Reset learn count and detection start time.
- */
- monotime_since(&mac->detect_start_time, &elapsed);
- reset_params = (elapsed.tv_sec > zvrf->dad_time);
- if (is_local && !reset_params) {
- /* RFC-7432: A PE/VTEP that detects a MAC mobility
- * event via LOCAL learning starts an M-second timer.
- *
- * NOTE: This is the START of the probe with count is
- * 0 during LOCAL learn event.
- * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time)
- */
- reset_params = !mac->dad_count;
- }
-
- if (reset_params) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count);
-
- mac->dad_count = 0;
- /* Start dup. addr detection (DAD) start time,
- * ONLY during LOCAL learn.
- */
- if (is_local)
- monotime(&mac->detect_start_time);
-
- } else if (!is_local) {
- /* For REMOTE MAC, increment detection count
- * ONLY while in probe window, once window passed,
- * next local learn event should trigger DAD.
- */
- mac->dad_count++;
- }
-
- /* For LOCAL MAC learn event, once count is reset above via either
- * initial/start detection time or passed the probe time, the count
- * needs to be incremented.
- */
- if (is_local)
- mac->dad_count++;
-
- if (mac->dad_count >= zvrf->dad_max_moves) {
- flog_warn(EC_ZEBRA_DUP_MAC_DETECTED,
- "VNI %u: MAC %s detected as duplicate during %s VTEP %s",
- mac->zvni->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- is_local ? "local update, last" :
- "remote update, from", inet_ntoa(vtep_ip));
-
- SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
-
- /* Capture Duplicate detection time */
- mac->dad_dup_detect_time = monotime(NULL);
-
- /* Mark all IPs/Neighs as duplicate
- * associcated with this MAC
- */
- for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
-
- /* Ony Mark IPs which are Local */
- if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
- continue;
-
- SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
-
- nbr->dad_dup_detect_time = monotime(NULL);
-
- flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC",
- mac->zvni->vni,
- prefix_mac2str(&mac->macaddr,
- buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
- is_local ? "local" : "remote");
- }
-
- /* Start auto recovery timer for this MAC */
- THREAD_OFF(mac->dad_mac_auto_recovery_timer);
- if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start",
- __func__,
- prefix_mac2str(&mac->macaddr, buf,
- sizeof(buf)),
- mac->flags, zvrf->dad_freeze_time);
-
- thread_add_timer(zrouter.master,
- zebra_vxlan_dad_mac_auto_recovery_exp,
- mac, zvrf->dad_freeze_time,
- &mac->dad_mac_auto_recovery_timer);
- }
-
- /* In case of local update, do not inform to client (BGPd),
- * upd_neigh for neigh sequence change.
- */
- if (zvrf->dad_freeze)
- *is_dup_detect = true;
- }
-}
-
-static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf,
- zebra_neigh_t *nbr,
- struct in_addr vtep_ip,
- bool do_dad,
- bool *is_dup_detect,
- bool is_local)
-{
-
- struct timeval elapsed = {0, 0};
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
- bool reset_params = false;
-
- if (!zvrf->dup_addr_detect)
- return;
-
- /* IP is detected as duplicate or inherit dup
- * state, hold on to install as remote entry
- * only if freeze is enabled.
- */
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u",
- __func__,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
- nbr->flags, nbr->dad_count,
- zvrf->dad_freeze_time);
-
- if (zvrf->dad_freeze)
- *is_dup_detect = true;
-
- /* warn-only action, neigh will be installed.
- * freeze action, it wil not be installed.
- */
- return;
- }
-
- if (!do_dad)
- return;
-
- /* Check if detection time (M-secs) expired.
- * Reset learn count and detection start time.
- * During remote mac add, count should already be 1
- * via local learning.
- */
- monotime_since(&nbr->detect_start_time, &elapsed);
- reset_params = (elapsed.tv_sec > zvrf->dad_time);
-
- if (is_local && !reset_params) {
- /* RFC-7432: A PE/VTEP that detects a MAC mobility
- * event via LOCAL learning starts an M-second timer.
- *
- * NOTE: This is the START of the probe with count is
- * 0 during LOCAL learn event.
- */
- reset_params = !nbr->dad_count;
- }
-
- if (reset_params) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u",
- __func__,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
- nbr->flags, nbr->dad_count);
- /* Reset learn count but do not start detection
- * during REMOTE learn event.
- */
- nbr->dad_count = 0;
- /* Start dup. addr detection (DAD) start time,
- * ONLY during LOCAL learn.
- */
- if (is_local)
- monotime(&nbr->detect_start_time);
-
- } else if (!is_local) {
- /* For REMOTE IP/Neigh, increment detection count
- * ONLY while in probe window, once window passed,
- * next local learn event should trigger DAD.
- */
- nbr->dad_count++;
- }
-
- /* For LOCAL IP/Neigh learn event, once count is reset above via either
- * initial/start detection time or passed the probe time, the count
- * needs to be incremented.
- */
- if (is_local)
- nbr->dad_count++;
-
- if (nbr->dad_count >= zvrf->dad_max_moves) {
- flog_warn(EC_ZEBRA_DUP_IP_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s",
- nbr->zvni->vni,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
- is_local ? "local update, last" :
- "remote update, from",
- inet_ntoa(vtep_ip));
-
- SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
-
- /* Capture Duplicate detection time */
- nbr->dad_dup_detect_time = monotime(NULL);
-
- /* Start auto recovery timer for this IP */
- THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
- if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start",
- __func__,
- prefix_mac2str(&nbr->emac, buf,
- sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1,
- sizeof(buf1)),
- nbr->flags, zvrf->dad_freeze_time);
-
- thread_add_timer(zrouter.master,
- zebra_vxlan_dad_ip_auto_recovery_exp,
- nbr, zvrf->dad_freeze_time,
- &nbr->dad_ip_auto_recovery_timer);
- }
- if (zvrf->dad_freeze)
- *is_dup_detect = true;
- }
-}
-
-/*
- * Helper function to determine maximum width of neighbor IP address for
- * display - just because we're dealing with IPv6 addresses that can
- * widely vary.
- */
-static void zvni_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_neigh_t *n;
- char buf[INET6_ADDRSTRLEN];
- struct neigh_walk_ctx *wctx = ctxt;
- int width;
-
- n = (zebra_neigh_t *)bucket->data;
-
- ipaddr2str(&n->ip, buf, sizeof(buf));
- width = strlen(buf);
- if (width > wctx->addr_width)
- wctx->addr_width = width;
-
-}
-
-/*
- * Print a specific neighbor entry.
- */
-static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
-{
- struct vty *vty;
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- const char *type_str;
- const char *state_str;
- bool flags_present = false;
- struct zebra_vrf *zvrf = NULL;
- struct timeval detect_start_time = {0, 0};
- char timebuf[MONOTIME_STRLEN];
-
- zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
- ipaddr2str(&n->ip, buf2, sizeof(buf2));
- prefix_mac2str(&n->emac, buf1, sizeof(buf1));
- type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ?
- "local" : "remote";
- state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
- vty = (struct vty *)ctxt;
- if (json == NULL) {
- vty_out(vty, "IP: %s\n",
- ipaddr2str(&n->ip, buf2, sizeof(buf2)));
- vty_out(vty, " Type: %s\n", type_str);
- vty_out(vty, " State: %s\n", state_str);
- vty_out(vty, " MAC: %s\n",
- prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
- } else {
- json_object_string_add(json, "ip", buf2);
- json_object_string_add(json, "type", type_str);
- json_object_string_add(json, "state", state_str);
- json_object_string_add(json, "mac", buf1);
- }
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
- if (json == NULL) {
- vty_out(vty, " Remote VTEP: %s\n",
- inet_ntoa(n->r_vtep_ip));
- } else
- json_object_string_add(json, "remoteVtep",
- inet_ntoa(n->r_vtep_ip));
- }
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) {
- if (!json) {
- vty_out(vty, " Flags: Default-gateway");
- flags_present = true;
- } else
- json_object_boolean_true_add(json, "defaultGateway");
- }
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) {
- if (!json) {
- vty_out(vty,
- flags_present ? " ,Router" : " Flags: Router");
- flags_present = true;
- }
- }
- if (json == NULL) {
- if (flags_present)
- vty_out(vty, "\n");
- vty_out(vty, " Local Seq: %u Remote Seq: %u\n",
- n->loc_seq, n->rem_seq);
-
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) {
- vty_out(vty, " Duplicate, detected at %s",
- time_to_string(n->dad_dup_detect_time,
- timebuf));
- } else if (n->dad_count) {
- monotime_since(&n->detect_start_time,
- &detect_start_time);
- if (detect_start_time.tv_sec <= zvrf->dad_time) {
- time_to_string(n->detect_start_time.tv_sec,
- timebuf);
- vty_out(vty,
- " Duplicate detection started at %s, detection count %u\n",
- timebuf, n->dad_count);
- }
- }
- } else {
- json_object_int_add(json, "localSequence", n->loc_seq);
- json_object_int_add(json, "remoteSequence", n->rem_seq);
- json_object_int_add(json, "detectionCount",
- n->dad_count);
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
- json_object_boolean_true_add(json, "isDuplicate");
- else
- json_object_boolean_false_add(json, "isDuplicate");
-
-
- }
-}
-
-/*
- * Print neighbor hash entry - called for display of all neighbors.
- */
-static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt)
-{
- struct vty *vty;
- json_object *json_vni = NULL, *json_row = NULL;
- zebra_neigh_t *n;
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- struct neigh_walk_ctx *wctx = ctxt;
- const char *state_str;
-
- vty = wctx->vty;
- json_vni = wctx->json;
- n = (zebra_neigh_t *)bucket->data;
-
- if (json_vni)
- json_row = json_object_new_object();
-
- prefix_mac2str(&n->emac, buf1, sizeof(buf1));
- ipaddr2str(&n->ip, buf2, sizeof(buf2));
- state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive";
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)
- return;
-
- if (json_vni == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n",
- -wctx->addr_width, buf2, "local",
- state_str, buf1, n->loc_seq, n->rem_seq);
- } else {
- json_object_string_add(json_row, "type", "local");
- json_object_string_add(json_row, "state", state_str);
- json_object_string_add(json_row, "mac", buf1);
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
- json_object_boolean_true_add(
- json_row, "defaultGateway");
- json_object_int_add(json_row, "localSequence",
- n->loc_seq);
- json_object_int_add(json_row, "remoteSequence",
- n->rem_seq);
- json_object_int_add(json_row, "detectionCount",
- n->dad_count);
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
- json_object_boolean_true_add(json_row,
- "isDuplicate");
- else
- json_object_boolean_false_add(json_row,
- "isDuplicate");
- }
- wctx->count++;
- } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
- if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
- !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))
- return;
-
- if (json_vni == NULL) {
- if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
- (wctx->count == 0))
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n",
- -wctx->addr_width, "Neighbor", "Type",
- "State", "MAC", "Remote VTEP",
- "Seq #'s");
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n",
- -wctx->addr_width, buf2, "remote", state_str,
- buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq);
- } else {
- json_object_string_add(json_row, "type", "remote");
- json_object_string_add(json_row, "state", state_str);
- json_object_string_add(json_row, "mac", buf1);
- json_object_string_add(json_row, "remoteVtep",
- inet_ntoa(n->r_vtep_ip));
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW))
- json_object_boolean_true_add(json_row,
- "defaultGateway");
- json_object_int_add(json_row, "localSequence",
- n->loc_seq);
- json_object_int_add(json_row, "remoteSequence",
- n->rem_seq);
- json_object_int_add(json_row, "detectionCount",
- n->dad_count);
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
- json_object_boolean_true_add(json_row,
- "isDuplicate");
- else
- json_object_boolean_false_add(json_row,
- "isDuplicate");
- }
- wctx->count++;
- }
-
- if (json_vni)
- json_object_object_add(json_vni, buf2, json_row);
-}
-
-/*
- * Print neighbor hash entry in detail - called for display of all neighbors.
- */
-static void zvni_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt)
-{
- struct vty *vty;
- json_object *json_vni = NULL, *json_row = NULL;
- zebra_neigh_t *n;
- char buf[INET6_ADDRSTRLEN];
- struct neigh_walk_ctx *wctx = ctxt;
-
- vty = wctx->vty;
- json_vni = wctx->json;
- n = (zebra_neigh_t *)bucket->data;
- if (!n)
- return;
-
- ipaddr2str(&n->ip, buf, sizeof(buf));
- if (json_vni)
- json_row = json_object_new_object();
-
- zvni_print_neigh(n, vty, json_row);
-
- if (json_vni)
- json_object_object_add(json_vni, buf, json_row);
-}
-
-/*
- * Print neighbors for all VNI.
- */
-static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket,
+static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket,
void **args)
{
struct vty *vty;
- json_object *json = NULL, *json_vni = NULL;
- zebra_vni_t *zvni;
+ json_object *json = NULL, *json_evpn = NULL;
+ zebra_evpn_t *zevpn;
uint32_t num_neigh;
struct neigh_walk_ctx wctx;
char vni_str[VNI_STR_LEN];
@@ -953,26 +181,26 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket,
json = (json_object *)args[1];
print_dup = (uint32_t)(uintptr_t)args[2];
- zvni = (zebra_vni_t *)bucket->data;
+ zevpn = (zebra_evpn_t *)bucket->data;
- num_neigh = hashcount(zvni->neigh_table);
+ num_neigh = hashcount(zevpn->neigh_table);
if (print_dup)
- num_neigh = num_dup_detected_neighs(zvni);
+ num_neigh = num_dup_detected_neighs(zevpn);
if (json == NULL) {
vty_out(vty,
"\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
- zvni->vni, num_neigh);
+ zevpn->vni, num_neigh);
} else {
- json_vni = json_object_new_object();
- json_object_int_add(json_vni, "numArpNd", num_neigh);
- snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni);
+ json_evpn = json_object_new_object();
+ json_object_int_add(json_evpn, "numArpNd", num_neigh);
+ snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
}
if (!num_neigh) {
if (json)
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
return;
}
@@ -981,61 +209,36 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket,
* the maximum width.
*/
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
- wctx.json = json_vni;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+ wctx.json = json_evpn;
+ hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width,
+ &wctx);
+
+ if (json == NULL)
+ zebra_evpn_print_neigh_hdr(vty, &wctx);
- if (json == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n",
- -wctx.addr_width, "IP", "Type",
- "State", "MAC", "Remote VTEP", "Seq #'s");
- }
if (print_dup)
- hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash,
- &wctx);
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_print_dad_neigh_hash, &wctx);
else
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash,
+ &wctx);
if (json)
- json_object_object_add(json, vni_str, json_vni);
-}
-
-static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_neigh_t *nbr;
-
- nbr = (zebra_neigh_t *)bucket->data;
- if (!nbr)
- return;
-
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
- zvni_print_neigh_hash(bucket, ctxt);
-}
-
-static void zvni_print_dad_neigh_hash_detail(struct hash_bucket *bucket,
- void *ctxt)
-{
- zebra_neigh_t *nbr;
-
- nbr = (zebra_neigh_t *)bucket->data;
- if (!nbr)
- return;
-
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
- zvni_print_neigh_hash_detail(bucket, ctxt);
+ json_object_object_add(json, vni_str, json_evpn);
}
/*
- * Print neighbors for all VNIs in detail.
+ * Print neighbors for all EVPNs in detail.
*/
-static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket,
+static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket,
void **args)
{
struct vty *vty;
- json_object *json = NULL, *json_vni = NULL;
- zebra_vni_t *zvni;
+ json_object *json = NULL, *json_evpn = NULL;
+ zebra_evpn_t *zevpn;
uint32_t num_neigh;
struct neigh_walk_ctx wctx;
char vni_str[VNI_STR_LEN];
@@ -1045,47 +248,47 @@ static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket,
json = (json_object *)args[1];
print_dup = (uint32_t)(uintptr_t)args[2];
- zvni = (zebra_vni_t *)bucket->data;
- if (!zvni) {
+ zevpn = (zebra_evpn_t *)bucket->data;
+ if (!zevpn) {
if (json)
vty_out(vty, "{}\n");
return;
}
- num_neigh = hashcount(zvni->neigh_table);
+ num_neigh = hashcount(zevpn->neigh_table);
- if (print_dup && num_dup_detected_neighs(zvni) == 0)
+ if (print_dup && num_dup_detected_neighs(zevpn) == 0)
return;
if (json == NULL) {
vty_out(vty,
"\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
- zvni->vni, num_neigh);
+ zevpn->vni, num_neigh);
} else {
- json_vni = json_object_new_object();
- json_object_int_add(json_vni, "numArpNd", num_neigh);
- snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni);
+ json_evpn = json_object_new_object();
+ json_object_int_add(json_evpn, "numArpNd", num_neigh);
+ snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
}
if (!num_neigh) {
if (json)
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
return;
}
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
- wctx.json = json_vni;
+ wctx.json = json_evpn;
if (print_dup)
- hash_iterate(zvni->neigh_table,
- zvni_print_dad_neigh_hash_detail, &wctx);
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_print_dad_neigh_hash_detail, &wctx);
else
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail,
- &wctx);
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_print_neigh_hash_detail, &wctx);
if (json)
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
}
/* print a specific next hop for an l3vni */
@@ -1165,352 +368,14 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
}
/*
- * Print a specific MAC entry.
- */
-static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json)
-{
- struct vty *vty;
- zebra_neigh_t *n = NULL;
- struct listnode *node = NULL;
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- struct zebra_vrf *zvrf;
- struct timeval detect_start_time = {0, 0};
- char timebuf[MONOTIME_STRLEN];
-
- zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
- vty = (struct vty *)ctxt;
- prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
-
- if (json) {
- json_object *json_mac = json_object_new_object();
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
- struct zebra_ns *zns;
- struct interface *ifp;
- ifindex_t ifindex;
-
- ifindex = mac->fwd_info.local.ifindex;
- zns = zebra_ns_lookup(mac->fwd_info.local.ns_id);
- ifp = if_lookup_by_index_per_ns(zns, ifindex);
- if (!ifp)
- return;
- json_object_string_add(json_mac, "type", "local");
- json_object_string_add(json_mac, "intf", ifp->name);
- json_object_int_add(json_mac, "ifindex", ifindex);
- if (mac->fwd_info.local.vid)
- json_object_int_add(json_mac, "vlan",
- mac->fwd_info.local.vid);
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- json_object_string_add(json_mac, "type", "remote");
- json_object_string_add(
- json_mac, "remoteVtep",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
- json_object_string_add(json_mac, "type", "auto");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
- json_object_boolean_true_add(json_mac, "stickyMac");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
- json_object_boolean_true_add(json_mac,
- "defaultGateway");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
- json_object_boolean_true_add(json_mac,
- "remoteGatewayMac");
-
- json_object_int_add(json_mac, "localSequence", mac->loc_seq);
- json_object_int_add(json_mac, "remoteSequence", mac->rem_seq);
-
- json_object_int_add(json_mac, "detectionCount", mac->dad_count);
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- json_object_boolean_true_add(json_mac, "isDuplicate");
- else
- json_object_boolean_false_add(json_mac, "isDuplicate");
-
- /* print all the associated neigh */
- if (!listcount(mac->neigh_list))
- json_object_string_add(json_mac, "neighbors", "none");
- else {
- json_object *json_active_nbrs = json_object_new_array();
- json_object *json_inactive_nbrs =
- json_object_new_array();
- json_object *json_nbrs = json_object_new_object();
-
- for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
- if (IS_ZEBRA_NEIGH_ACTIVE(n))
- json_object_array_add(
- json_active_nbrs,
- json_object_new_string(
- ipaddr2str(
- &n->ip, buf2,
- sizeof(buf2))));
- else
- json_object_array_add(
- json_inactive_nbrs,
- json_object_new_string(
- ipaddr2str(
- &n->ip, buf2,
- sizeof(buf2))));
- }
-
- json_object_object_add(json_nbrs, "active",
- json_active_nbrs);
- json_object_object_add(json_nbrs, "inactive",
- json_inactive_nbrs);
- json_object_object_add(json_mac, "neighbors",
- json_nbrs);
- }
-
- json_object_object_add(json, buf1, json_mac);
- } else {
- vty_out(vty, "MAC: %s\n", buf1);
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
- struct zebra_ns *zns;
- struct interface *ifp;
- ifindex_t ifindex;
-
- ifindex = mac->fwd_info.local.ifindex;
- zns = zebra_ns_lookup(mac->fwd_info.local.ns_id);
- ifp = if_lookup_by_index_per_ns(zns, ifindex);
- if (!ifp)
- return;
- vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
- if (mac->fwd_info.local.vid)
- vty_out(vty, " VLAN: %u",
- mac->fwd_info.local.vid);
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- vty_out(vty, " Remote VTEP: %s",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
- vty_out(vty, " Auto Mac ");
- }
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
- vty_out(vty, " Sticky Mac ");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
- vty_out(vty, " Default-gateway Mac ");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW))
- vty_out(vty, " Remote-gateway Mac ");
-
- vty_out(vty, "\n");
- vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq,
- mac->rem_seq);
- vty_out(vty, "\n");
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
- vty_out(vty, " Duplicate, detected at %s",
- time_to_string(mac->dad_dup_detect_time,
- timebuf));
- } else if (mac->dad_count) {
- monotime_since(&mac->detect_start_time,
- &detect_start_time);
- if (detect_start_time.tv_sec <= zvrf->dad_time) {
- time_to_string(mac->detect_start_time.tv_sec,
- timebuf);
- vty_out(vty,
- " Duplicate detection started at %s, detection count %u\n",
- timebuf, mac->dad_count);
- }
- }
-
- /* print all the associated neigh */
- vty_out(vty, " Neighbors:\n");
- if (!listcount(mac->neigh_list))
- vty_out(vty, " No Neighbors\n");
- else {
- for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
- vty_out(vty, " %s %s\n",
- ipaddr2str(&n->ip, buf2, sizeof(buf2)),
- (IS_ZEBRA_NEIGH_ACTIVE(n)
- ? "Active"
- : "Inactive"));
- }
- }
-
- vty_out(vty, "\n");
- }
-}
-
-/*
- * Print MAC hash entry - called for display of all MACs.
+ * Print MACs for all EVPNs.
*/
-static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt)
+static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt)
{
struct vty *vty;
- json_object *json_mac_hdr = NULL, *json_mac = NULL;
- zebra_mac_t *mac;
- char buf1[ETHER_ADDR_STRLEN];
- struct mac_walk_ctx *wctx = ctxt;
-
- vty = wctx->vty;
- json_mac_hdr = wctx->json;
- mac = (zebra_mac_t *)bucket->data;
-
- prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
-
- if (json_mac_hdr)
- json_mac = json_object_new_object();
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
- struct zebra_ns *zns;
- ifindex_t ifindex;
- struct interface *ifp;
- vlanid_t vid;
-
- if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)
- return;
-
- zns = zebra_ns_lookup(mac->fwd_info.local.ns_id);
- ifindex = mac->fwd_info.local.ifindex;
- ifp = if_lookup_by_index_per_ns(zns, ifindex);
- if (!ifp) // unexpected
- return;
- vid = mac->fwd_info.local.vid;
- if (json_mac_hdr == NULL)
- vty_out(vty, "%-17s %-6s %-21s", buf1, "local",
- ifp->name);
- else {
- json_object_string_add(json_mac, "type", "local");
- json_object_string_add(json_mac, "intf", ifp->name);
- }
- if (vid) {
- if (json_mac_hdr == NULL)
- vty_out(vty, " %-5u", vid);
- else
- json_object_int_add(json_mac, "vlan", vid);
- } else /* No vid? fill out the space */
- if (json_mac_hdr == NULL)
- vty_out(vty, " %-5s", "");
- if (json_mac_hdr == NULL) {
- vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq);
- vty_out(vty, "\n");
- } else {
- json_object_int_add(json_mac, "localSequence",
- mac->loc_seq);
- json_object_int_add(json_mac, "remoteSequence",
- mac->rem_seq);
- json_object_int_add(json_mac, "detectionCount",
- mac->dad_count);
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- json_object_boolean_true_add(json_mac,
- "isDuplicate");
- else
- json_object_boolean_false_add(json_mac,
- "isDuplicate");
- json_object_object_add(json_mac_hdr, buf1, json_mac);
- }
-
- wctx->count++;
-
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
-
- if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
- !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
- &wctx->r_vtep_ip))
- return;
-
- if (json_mac_hdr == NULL) {
- if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
- (wctx->count == 0)) {
- vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni);
- vty_out(vty, "%-17s %-6s %-21s %-5s %s\n",
- "MAC", "Type", "Intf/Remote VTEP",
- "VLAN", "Seq #'s");
- }
- vty_out(vty, "%-17s %-6s %-21s %-5s %u/%u\n", buf1,
- "remote", inet_ntoa(mac->fwd_info.r_vtep_ip),
- "", mac->loc_seq, mac->rem_seq);
- } else {
- json_object_string_add(json_mac, "type", "remote");
- json_object_string_add(json_mac, "remoteVtep",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
- json_object_object_add(json_mac_hdr, buf1, json_mac);
- json_object_int_add(json_mac, "localSequence",
- mac->loc_seq);
- json_object_int_add(json_mac, "remoteSequence",
- mac->rem_seq);
- json_object_int_add(json_mac, "detectionCount",
- mac->dad_count);
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- json_object_boolean_true_add(json_mac,
- "isDuplicate");
- else
- json_object_boolean_false_add(json_mac,
- "isDuplicate");
-
- }
-
- wctx->count++;
- }
-}
-
-/* Print Duplicate MAC */
-static void zvni_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_mac_t *mac;
-
- mac = (zebra_mac_t *)bucket->data;
- if (!mac)
- return;
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- zvni_print_mac_hash(bucket, ctxt);
-}
-
-/*
- * Print MAC hash entry in detail - called for display of all MACs.
- */
-static void zvni_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt)
-{
- struct vty *vty;
- json_object *json_mac_hdr = NULL;
- zebra_mac_t *mac;
- struct mac_walk_ctx *wctx = ctxt;
- char buf1[ETHER_ADDR_STRLEN];
-
- vty = wctx->vty;
- json_mac_hdr = wctx->json;
- mac = (zebra_mac_t *)bucket->data;
- if (!mac)
- return;
-
- wctx->count++;
- prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
-
- zvni_print_mac(mac, vty, json_mac_hdr);
-}
-
-/* Print Duplicate MAC in detail */
-static void zvni_print_dad_mac_hash_detail(struct hash_bucket *bucket,
- void *ctxt)
-{
- zebra_mac_t *mac;
-
- mac = (zebra_mac_t *)bucket->data;
- if (!mac)
- return;
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- zvni_print_mac_hash_detail(bucket, ctxt);
-}
-
-/*
- * Print MACs for all VNI.
- */
-static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt)
-{
- struct vty *vty;
- json_object *json = NULL, *json_vni = NULL;
+ json_object *json = NULL, *json_evpn = NULL;
json_object *json_mac = NULL;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_macs;
struct mac_walk_ctx *wctx = ctxt;
char vni_str[VNI_STR_LEN];
@@ -1518,70 +383,74 @@ static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt)
vty = wctx->vty;
json = wctx->json;
- zvni = (zebra_vni_t *)bucket->data;
- wctx->zvni = zvni;
+ zevpn = (zebra_evpn_t *)bucket->data;
+ wctx->zevpn = zevpn;
/*We are iterating over a new VNI, set the count to 0*/
wctx->count = 0;
- num_macs = num_valid_macs(zvni);
+ num_macs = num_valid_macs(zevpn);
if (!num_macs)
return;
if (wctx->print_dup)
- num_macs = num_dup_detected_macs(zvni);
+ num_macs = num_dup_detected_macs(zevpn);
if (json) {
- json_vni = json_object_new_object();
+ json_evpn = json_object_new_object();
json_mac = json_object_new_object();
- snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni);
+ snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
}
if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
if (json == NULL) {
vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
- zvni->vni, num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC",
- "Type", "Intf/Remote VTEP", "VLAN", "Seq #'s");
+ zevpn->vni, num_macs);
+ vty_out(vty,
+ "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n");
+ vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC",
+ "Type", "Flags", "Intf/Remote ES/VTEP",
+ "VLAN", "Seq #'s");
} else
- json_object_int_add(json_vni, "numMacs", num_macs);
+ json_object_int_add(json_evpn, "numMacs", num_macs);
}
if (!num_macs) {
if (json) {
- json_object_int_add(json_vni, "numMacs", num_macs);
- json_object_object_add(json, vni_str, json_vni);
+ json_object_int_add(json_evpn, "numMacs", num_macs);
+ json_object_object_add(json, vni_str, json_evpn);
}
return;
}
- /* assign per-vni to wctx->json object to fill macs
- * under the vni. Re-assign primary json object to fill
- * next vni information.
+ /* assign per-evpn to wctx->json object to fill macs
+ * under the evpn. Re-assign primary json object to fill
+ * next evpn information.
*/
wctx->json = json_mac;
if (wctx->print_dup)
- hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash,
+ wctx);
else
- hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, wctx);
wctx->json = json;
if (json) {
if (wctx->count)
- json_object_object_add(json_vni, "macs", json_mac);
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json_evpn, "macs", json_mac);
+ json_object_object_add(json, vni_str, json_evpn);
}
}
/*
- * Print MACs in detail for all VNI.
+ * Print MACs in detail for all EVPNs.
*/
-static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket,
+static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket,
void *ctxt)
{
struct vty *vty;
- json_object *json = NULL, *json_vni = NULL;
+ json_object *json = NULL, *json_evpn = NULL;
json_object *json_mac = NULL;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_macs;
struct mac_walk_ctx *wctx = ctxt;
char vni_str[VNI_STR_LEN];
@@ -1589,52 +458,53 @@ static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket,
vty = wctx->vty;
json = wctx->json;
- zvni = (zebra_vni_t *)bucket->data;
- if (!zvni) {
+ zevpn = (zebra_evpn_t *)bucket->data;
+ if (!zevpn) {
if (json)
vty_out(vty, "{}\n");
return;
}
- wctx->zvni = zvni;
+ wctx->zevpn = zevpn;
- /*We are iterating over a new VNI, set the count to 0*/
+ /*We are iterating over a new EVPN, set the count to 0*/
wctx->count = 0;
- num_macs = num_valid_macs(zvni);
+ num_macs = num_valid_macs(zevpn);
if (!num_macs)
return;
- if (wctx->print_dup && (num_dup_detected_macs(zvni) == 0))
+ if (wctx->print_dup && (num_dup_detected_macs(zevpn) == 0))
return;
if (json) {
- json_vni = json_object_new_object();
+ json_evpn = json_object_new_object();
json_mac = json_object_new_object();
- snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni);
+ snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni);
}
if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
if (json == NULL) {
vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
- zvni->vni, num_macs);
+ zevpn->vni, num_macs);
} else
- json_object_int_add(json_vni, "numMacs", num_macs);
+ json_object_int_add(json_evpn, "numMacs", num_macs);
}
- /* assign per-vni to wctx->json object to fill macs
- * under the vni. Re-assign primary json object to fill
- * next vni information.
+ /* assign per-evpn to wctx->json object to fill macs
+ * under the evpn. Re-assign primary json object to fill
+ * next evpn information.
*/
wctx->json = json_mac;
if (wctx->print_dup)
- hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash_detail,
- wctx);
+ hash_iterate(zevpn->mac_table,
+ zebra_evpn_print_dad_mac_hash_detail, wctx);
else
- hash_iterate(zvni->mac_table, zvni_print_mac_hash_detail, wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash_detail,
+ wctx);
wctx->json = json;
if (json) {
if (wctx->count)
- json_object_object_add(json_vni, "macs", json_mac);
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json_evpn, "macs", json_mac);
+ json_object_object_add(json, vni_str, json_evpn);
}
}
@@ -1642,7 +512,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx)
{
struct nh_walk_ctx *wctx = NULL;
struct vty *vty = NULL;
- struct json_object *json_vni = NULL;
+ struct json_object *json_evpn = NULL;
struct json_object *json_nh = NULL;
zebra_neigh_t *n = NULL;
char buf1[ETHER_ADDR_STRLEN];
@@ -1650,12 +520,12 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx)
wctx = (struct nh_walk_ctx *)ctx;
vty = wctx->vty;
- json_vni = wctx->json;
- if (json_vni)
+ json_evpn = wctx->json;
+ if (json_evpn)
json_nh = json_object_new_object();
n = (zebra_neigh_t *)bucket->data;
- if (!json_vni) {
+ if (!json_evpn) {
vty_out(vty, "%-15s %-17s\n",
ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
@@ -1665,7 +535,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx)
json_object_string_add(
json_nh, "routerMac",
prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
- json_object_object_add(json_vni,
+ json_object_object_add(json_evpn,
ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
json_nh);
}
@@ -1676,7 +546,7 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
{
struct vty *vty = NULL;
json_object *json = NULL;
- json_object *json_vni = NULL;
+ json_object *json_evpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
uint32_t num_nh = 0;
struct nh_walk_ctx wctx;
@@ -1692,22 +562,22 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket,
return;
if (json) {
- json_vni = json_object_new_object();
- snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni);
+ json_evpn = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
}
if (json == NULL) {
vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh);
vty_out(vty, "%-15s %-17s\n", "IP", "RMAC");
} else
- json_object_int_add(json_vni, "numNextHops", num_nh);
+ json_object_int_add(json_evpn, "numNextHops", num_nh);
memset(&wctx, 0, sizeof(struct nh_walk_ctx));
wctx.vty = vty;
- wctx.json = json_vni;
+ wctx.json = json_evpn;
hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
if (json)
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
}
static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
@@ -1715,7 +585,7 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
{
struct vty *vty = NULL;
json_object *json = NULL;
- json_object *json_vni = NULL;
+ json_object *json_evpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
uint32_t num_rmacs;
struct rmac_walk_ctx wctx;
@@ -1731,15 +601,15 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
return;
if (json) {
- json_vni = json_object_new_object();
- snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni);
+ json_evpn = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
}
if (json == NULL) {
vty_out(vty, "\nVNI %u #RMACs %u\n\n", zl3vni->vni, num_rmacs);
vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP");
} else
- json_object_int_add(json_vni, "numRmacs", num_rmacs);
+ json_object_int_add(json_evpn, "numRmacs", num_rmacs);
/* assign per-vni to wctx->json object to fill macs
* under the vni. Re-assign primary json object to fill
@@ -1747,10 +617,10 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket,
*/
memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
wctx.vty = vty;
- wctx.json = json_vni;
+ wctx.json = json_evpn;
hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
if (json)
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
}
static void zl3vni_print_rmac_hash(struct hash_bucket *bucket, void *ctx)
@@ -1791,8 +661,8 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
char buf[ETHER_ADDR_STRLEN];
struct vty *vty = NULL;
json_object *json = NULL;
- zebra_vni_t *zvni = NULL;
- json_object *json_vni_list = NULL;
+ zebra_evpn_t *zevpn = NULL;
+ json_object *json_evpn_list = NULL;
struct listnode *node = NULL, *nnode = NULL;
vty = ctx[0];
@@ -1817,11 +687,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
vty_out(vty, " Router MAC: %s\n",
zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
vty_out(vty, " L2 VNIs: ");
- for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni))
- vty_out(vty, "%u ", zvni->vni);
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn))
+ vty_out(vty, "%u ", zevpn->vni);
vty_out(vty, "\n");
} else {
- json_vni_list = json_object_new_array();
+ json_evpn_list = json_object_new_array();
json_object_int_add(json, "vni", zl3vni->vni);
json_object_string_add(json, "type", "L3");
json_object_string_add(json, "localVtepIp",
@@ -1843,106 +713,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)
? "prefix-routes-only"
: "none");
- for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
- json_object_array_add(json_vni_list,
- json_object_new_int(zvni->vni));
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) {
+ json_object_array_add(json_evpn_list,
+ json_object_new_int(zevpn->vni));
}
- json_object_object_add(json, "l2Vnis", json_vni_list);
- }
-}
-
-/*
- * Print a specific VNI entry.
- */
-static void zvni_print(zebra_vni_t *zvni, void **ctxt)
-{
- struct vty *vty;
- zebra_vtep_t *zvtep;
- uint32_t num_macs;
- uint32_t num_neigh;
- json_object *json = NULL;
- json_object *json_vtep_list = NULL;
- json_object *json_ip_str = NULL;
-
- vty = ctxt[0];
- json = ctxt[1];
-
- if (json == NULL) {
- vty_out(vty, "VNI: %u\n", zvni->vni);
- vty_out(vty, " Type: %s\n", "L2");
- vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zvni->vrf_id));
- } else {
- json_object_int_add(json, "vni", zvni->vni);
- json_object_string_add(json, "type", "L2");
- json_object_string_add(json, "vrf",
- vrf_id_to_name(zvni->vrf_id));
- }
-
- if (!zvni->vxlan_if) { // unexpected
- if (json == NULL)
- vty_out(vty, " VxLAN interface: unknown\n");
- return;
- }
- num_macs = num_valid_macs(zvni);
- num_neigh = hashcount(zvni->neigh_table);
- if (json == NULL) {
- vty_out(vty, " VxLAN interface: %s\n", zvni->vxlan_if->name);
- vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex);
- vty_out(vty, " Local VTEP IP: %s\n",
- inet_ntoa(zvni->local_vtep_ip));
- vty_out(vty, " Mcast group: %s\n",
- inet_ntoa(zvni->mcast_grp));
- } else {
- json_object_string_add(json, "vxlanInterface",
- zvni->vxlan_if->name);
- json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex);
- json_object_string_add(json, "vtepIp",
- inet_ntoa(zvni->local_vtep_ip));
- json_object_string_add(json, "mcastGroup",
- inet_ntoa(zvni->mcast_grp));
- json_object_string_add(json, "advertiseGatewayMacip",
- zvni->advertise_gw_macip ? "Yes" : "No");
- json_object_int_add(json, "numMacs", num_macs);
- json_object_int_add(json, "numArpNd", num_neigh);
- }
- if (!zvni->vteps) {
- if (json == NULL)
- vty_out(vty, " No remote VTEPs known for this VNI\n");
- } else {
- if (json == NULL)
- vty_out(vty, " Remote VTEPs for this VNI:\n");
- else
- json_vtep_list = json_object_new_array();
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
- const char *flood_str = lookup_msg(zvtep_flood_str,
- zvtep->flood_control,
- VXLAN_FLOOD_STR_DEFAULT);
-
- if (json == NULL) {
- vty_out(vty, " %s flood: %s\n",
- inet_ntoa(zvtep->vtep_ip),
- flood_str);
- } else {
- json_ip_str = json_object_new_string(
- inet_ntoa(zvtep->vtep_ip));
- json_object_array_add(json_vtep_list,
- json_ip_str);
- }
- }
- if (json)
- json_object_object_add(json, "numRemoteVteps",
- json_vtep_list);
- }
- if (json == NULL) {
- vty_out(vty,
- " Number of MACs (local and remote) known for this VNI: %u\n",
- num_macs);
- vty_out(vty,
- " Number of ARPs (IPv4 and IPv6, local and remote) "
- "known for this VNI: %u\n",
- num_neigh);
- vty_out(vty, " Advertise-gw-macip: %s\n",
- zvni->advertise_gw_macip ? "Yes" : "No");
+ json_object_object_add(json, "l2Vnis", json_evpn_list);
}
}
@@ -1951,7 +726,7 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[])
{
struct vty *vty = NULL;
json_object *json = NULL;
- json_object *json_vni = NULL;
+ json_object *json_evpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
vty = (struct vty *)ctx[0];
@@ -1968,31 +743,23 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[])
} else {
char vni_str[VNI_STR_LEN];
- snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni);
- json_vni = json_object_new_object();
- json_object_int_add(json_vni, "vni", zl3vni->vni);
- json_object_string_add(json_vni, "vxlanIf",
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ json_evpn = json_object_new_object();
+ json_object_int_add(json_evpn, "vni", zl3vni->vni);
+ json_object_string_add(json_evpn, "vxlanIf",
zl3vni_vxlan_if_name(zl3vni));
- json_object_int_add(json_vni, "numMacs",
+ json_object_int_add(json_evpn, "numMacs",
hashcount(zl3vni->rmac_table));
- json_object_int_add(json_vni, "numArpNd",
+ json_object_int_add(json_evpn, "numArpNd",
hashcount(zl3vni->nh_table));
- json_object_string_add(json_vni, "numRemoteVteps", "n/a");
- json_object_string_add(json_vni, "type", "L3");
- json_object_string_add(json_vni, "tenantVrf",
+ json_object_string_add(json_evpn, "numRemoteVteps", "n/a");
+ json_object_string_add(json_evpn, "type", "L3");
+ json_object_string_add(json_evpn, "tenantVrf",
zl3vni_vrf_name(zl3vni));
- json_object_object_add(json, vni_str, json_vni);
+ json_object_object_add(json, vni_str, json_evpn);
}
}
-/* Private Structure to pass callback data for hash iterator */
-struct zvni_evpn_show {
- struct vty *vty;
- json_object *json;
- struct zebra_vrf *zvrf;
- bool use_json;
-};
-
/* print a L3 VNI hash entry in detail*/
static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data)
{
@@ -2000,7 +767,7 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data)
zebra_l3vni_t *zl3vni = NULL;
json_object *json_array = NULL;
bool use_json = false;
- struct zvni_evpn_show *zes = data;
+ struct zebra_evpn_show *zes = data;
vty = zes->vty;
json_array = zes->json;
@@ -2015,1749 +782,6 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data)
vty_out(vty, "\n");
}
-
-/*
- * Print a VNI hash entry - called for display of all VNIs.
- */
-static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[])
-{
- struct vty *vty;
- zebra_vni_t *zvni;
- zebra_vtep_t *zvtep;
- uint32_t num_vteps = 0;
- uint32_t num_macs = 0;
- uint32_t num_neigh = 0;
- json_object *json = NULL;
- json_object *json_vni = NULL;
- json_object *json_ip_str = NULL;
- json_object *json_vtep_list = NULL;
-
- vty = ctxt[0];
- json = ctxt[1];
-
- zvni = (zebra_vni_t *)bucket->data;
-
- zvtep = zvni->vteps;
- while (zvtep) {
- num_vteps++;
- zvtep = zvtep->next;
- }
-
- num_macs = num_valid_macs(zvni);
- num_neigh = hashcount(zvni->neigh_table);
- if (json == NULL)
- vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n",
- zvni->vni, "L2",
- zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
- num_macs, num_neigh, num_vteps,
- vrf_id_to_name(zvni->vrf_id));
- else {
- char vni_str[VNI_STR_LEN];
- snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni);
- json_vni = json_object_new_object();
- json_object_int_add(json_vni, "vni", zvni->vni);
- json_object_string_add(json_vni, "type", "L2");
- json_object_string_add(json_vni, "vxlanIf",
- zvni->vxlan_if ? zvni->vxlan_if->name
- : "unknown");
- json_object_int_add(json_vni, "numMacs", num_macs);
- json_object_int_add(json_vni, "numArpNd", num_neigh);
- json_object_int_add(json_vni, "numRemoteVteps", num_vteps);
- json_object_string_add(json_vni, "tenantVrf",
- vrf_id_to_name(zvni->vrf_id));
- if (num_vteps) {
- json_vtep_list = json_object_new_array();
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
- json_ip_str = json_object_new_string(
- inet_ntoa(zvtep->vtep_ip));
- json_object_array_add(json_vtep_list,
- json_ip_str);
- }
- json_object_object_add(json_vni, "remoteVteps",
- json_vtep_list);
- }
- json_object_object_add(json, vni_str, json_vni);
- }
-}
-
-/*
- * Print a VNI hash entry in detail - called for display of all VNIs.
- */
-static void zvni_print_hash_detail(struct hash_bucket *bucket, void *data)
-{
- struct vty *vty;
- zebra_vni_t *zvni;
- json_object *json_array = NULL;
- bool use_json = false;
- struct zvni_evpn_show *zes = data;
-
- vty = zes->vty;
- json_array = zes->json;
- use_json = zes->use_json;
-
- zvni = (zebra_vni_t *)bucket->data;
-
- zebra_vxlan_print_vni(vty, zes->zvrf, zvni->vni, use_json, json_array);
-
- if (!use_json)
- vty_out(vty, "\n");
-}
-
-/*
- * Inform BGP about local MACIP.
- */
-static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
- struct ipaddr *ip, uint8_t flags,
- uint32_t seq, int state, uint16_t cmd)
-{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- int ipa_len;
- struct zserv *client = NULL;
- struct stream *s = NULL;
-
- client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
- /* BGP may not be running. */
- if (!client)
- return 0;
-
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
-
- zclient_create_header(s, cmd, zebra_vrf_get_evpn_id());
- stream_putl(s, vni);
- stream_put(s, macaddr->octet, ETH_ALEN);
- if (ip) {
- ipa_len = 0;
- if (IS_IPADDR_V4(ip))
- ipa_len = IPV4_MAX_BYTELEN;
- else if (IS_IPADDR_V6(ip))
- ipa_len = IPV6_MAX_BYTELEN;
-
- stream_putl(s, ipa_len); /* IP address length */
- if (ipa_len)
- stream_put(s, &ip->ip.addr, ipa_len); /* IP address */
- } else
- stream_putl(s, 0); /* Just MAC. */
-
- if (cmd == ZEBRA_MACIP_ADD) {
- stream_putc(s, flags); /* sticky mac/gateway mac */
- stream_putl(s, seq); /* sequence number */
- } else {
- stream_putl(s, state); /* state - active/inactive */
- }
-
-
- /* Write packet size. */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Send MACIP %s flags 0x%x MAC %s IP %s seq %u L2-VNI %u to %s",
- (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni,
- zebra_route_string(client->proto));
-
- if (cmd == ZEBRA_MACIP_ADD)
- client->macipadd_cnt++;
- else
- client->macipdel_cnt++;
-
- return zserv_send_message(client, s);
-}
-
-/*
- * Make hash key for neighbors.
- */
-static unsigned int neigh_hash_keymake(const void *p)
-{
- const zebra_neigh_t *n = p;
- const struct ipaddr *ip = &n->ip;
-
- if (IS_IPADDR_V4(ip))
- return jhash_1word(ip->ipaddr_v4.s_addr, 0);
-
- return jhash2(ip->ipaddr_v6.s6_addr32,
- array_size(ip->ipaddr_v6.s6_addr32), 0);
-}
-
-/*
- * Compare two neighbor hash structures.
- */
-static bool neigh_cmp(const void *p1, const void *p2)
-{
- const zebra_neigh_t *n1 = p1;
- const zebra_neigh_t *n2 = p2;
-
- if (n1 == NULL && n2 == NULL)
- return true;
-
- if (n1 == NULL || n2 == NULL)
- return false;
-
- return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0);
-}
-
-static int neigh_list_cmp(void *p1, void *p2)
-{
- const zebra_neigh_t *n1 = p1;
- const zebra_neigh_t *n2 = p2;
-
- return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr));
-}
-
-/*
- * Callback to allocate neighbor hash entry.
- */
-static void *zvni_neigh_alloc(void *p)
-{
- const zebra_neigh_t *tmp_n = p;
- zebra_neigh_t *n;
-
- n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
- *n = *tmp_n;
-
- return ((void *)n);
-}
-
-/*
- * Add neighbor entry.
- */
-static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
- struct ethaddr *mac)
-{
- zebra_neigh_t tmp_n;
- zebra_neigh_t *n = NULL;
- zebra_mac_t *zmac = NULL;
-
- memset(&tmp_n, 0, sizeof(zebra_neigh_t));
- memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
- n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc);
- assert(n);
-
- memcpy(&n->emac, mac, ETH_ALEN);
- n->state = ZEBRA_NEIGH_INACTIVE;
- n->zvni = zvni;
- n->dad_ip_auto_recovery_timer = NULL;
-
- /* Associate the neigh to mac */
- zmac = zvni_mac_lookup(zvni, mac);
- if (zmac)
- listnode_add_sort(zmac->neigh_list, n);
-
- return n;
-}
-
-/*
- * Delete neighbor entry.
- */
-static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n)
-{
- zebra_neigh_t *tmp_n;
- zebra_mac_t *zmac = NULL;
-
- zmac = zvni_mac_lookup(zvni, &n->emac);
- if (zmac)
- listnode_delete(zmac->neigh_list, n);
-
- /* Cancel auto recovery */
- THREAD_OFF(n->dad_ip_auto_recovery_timer);
-
- /* Free the VNI hash entry and allocated memory. */
- tmp_n = hash_release(zvni->neigh_table, n);
- XFREE(MTYPE_NEIGH, tmp_n);
-
- return 0;
-}
-
-/*
- * Free neighbor hash entry (callback)
- */
-static void zvni_neigh_del_hash_entry(struct hash_bucket *bucket, void *arg)
-{
- struct neigh_walk_ctx *wctx = arg;
- zebra_neigh_t *n = bucket->data;
-
- if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL))
- || ((wctx->flags & DEL_REMOTE_NEIGH)
- && (n->flags & ZEBRA_NEIGH_REMOTE))
- || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP)
- && (n->flags & ZEBRA_NEIGH_REMOTE)
- && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) {
- if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL))
- zvni_neigh_send_del_to_client(wctx->zvni->vni, &n->ip,
- &n->emac, 0, n->state);
-
- if (wctx->uninstall)
- zvni_neigh_uninstall(wctx->zvni, n);
-
- zvni_neigh_del(wctx->zvni, n);
- }
-
- return;
-}
-
-/*
- * Delete all neighbor entries for this VNI.
- */
-static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
- uint32_t flags)
-{
- struct neigh_walk_ctx wctx;
-
- if (!zvni->neigh_table)
- return;
-
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
- wctx.uninstall = uninstall;
- wctx.upd_client = upd_client;
- wctx.flags = flags;
-
- hash_iterate(zvni->neigh_table, zvni_neigh_del_hash_entry, &wctx);
-}
-
-/*
- * Look up neighbor hash entry.
- */
-static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip)
-{
- zebra_neigh_t tmp;
- zebra_neigh_t *n;
-
- memset(&tmp, 0, sizeof(tmp));
- memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
- n = hash_lookup(zvni->neigh_table, &tmp);
-
- return n;
-}
-
-/*
- * Process all neighbors associated with a MAC upon the MAC being learnt
- * locally or undergoing any other change (such as sequence number).
- */
-static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni,
- zebra_mac_t *zmac,
- bool seq_change)
-{
- zebra_neigh_t *n = NULL;
- struct listnode *node = NULL;
- struct zebra_vrf *zvrf = NULL;
- char buf[ETHER_ADDR_STRLEN];
-
- zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on local MAC %s %s, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- seq_change ? "CHANGE" : "ADD", zvni->vni);
-
- /* Walk all neighbors and mark any inactive local neighbors as
- * active and/or update sequence number upon a move, and inform BGP.
- * The action for remote neighbors is TBD.
- * NOTE: We can't simply uninstall remote neighbors as the kernel may
- * accidentally end up deleting a just-learnt local neighbor.
- */
- for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change) {
- ZEBRA_NEIGH_SET_ACTIVE(n);
- n->loc_seq = zmac->loc_seq;
- if (!(zvrf->dup_addr_detect &&
- zvrf->dad_freeze && !!CHECK_FLAG(n->flags,
- ZEBRA_NEIGH_DUPLICATE)))
- zvni_neigh_send_add_to_client(
- zvni->vni, &n->ip, &n->emac,
- n->flags, n->loc_seq);
- }
- }
- }
-}
-
-/*
- * Process all neighbors associated with a local MAC upon the MAC being
- * deleted.
- */
-static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni,
- zebra_mac_t *zmac)
-{
- zebra_neigh_t *n = NULL;
- struct listnode *node = NULL;
- char buf[ETHER_ADDR_STRLEN];
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- zvni->vni);
-
- /* Walk all local neighbors and mark as inactive and inform
- * BGP, if needed.
- * TBD: There is currently no handling for remote neighbors. We
- * don't expect them to exist, if they do, do we install the MAC
- * as a remote MAC and the neighbor as remote?
- */
- for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
- ZEBRA_NEIGH_SET_INACTIVE(n);
- n->loc_seq = 0;
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
- &n->emac, 0, ZEBRA_NEIGH_ACTIVE);
- }
- }
- }
-}
-
-/*
- * Process all neighbors associated with a MAC upon the MAC being remotely
- * learnt.
- */
-static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni,
- zebra_mac_t *zmac)
-{
- zebra_neigh_t *n = NULL;
- struct listnode *node = NULL;
- char buf[ETHER_ADDR_STRLEN];
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- zvni->vni);
-
- /* Walk all local neighbors and mark as inactive and inform
- * BGP, if needed.
- */
- for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
- ZEBRA_NEIGH_SET_INACTIVE(n);
- n->loc_seq = 0;
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
- &n->emac, 0, ZEBRA_NEIGH_ACTIVE);
- }
- }
- }
-}
-
-/*
- * Process all neighbors associated with a remote MAC upon the MAC being
- * deleted.
- */
-static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni,
- zebra_mac_t *zmac)
-{
- /* NOTE: Currently a NO-OP. */
-}
-
-static void zvni_probe_neigh_on_mac_add(zebra_vni_t *zvni, zebra_mac_t *zmac)
-{
- zebra_neigh_t *nbr = NULL;
- struct listnode *node = NULL;
-
- for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, nbr)) {
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) &&
- IS_ZEBRA_NEIGH_INACTIVE(nbr))
- zvni_neigh_probe(zvni, nbr);
- }
-}
-
-/*
- * Inform BGP about local neighbor addition.
- */
-static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr,
- uint8_t neigh_flags,
- uint32_t seq)
-{
- uint8_t flags = 0;
-
- if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW))
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
- /* Set router flag (R-bit) based on local neigh entry add */
- if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG))
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
- if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP))
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP);
-
- return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
- seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD);
-}
-
-/*
- * Inform BGP about local neighbor deletion.
- */
-static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
- struct ethaddr *macaddr, uint8_t flags,
- int state)
-{
- return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
- 0, state, ZEBRA_MACIP_DEL);
-}
-
-/*
- * Install remote neighbor into the kernel.
- */
-static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n)
-{
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl;
- struct interface *vlan_if;
- int flags;
- int ret = 0;
-
- if (!(n->flags & ZEBRA_NEIGH_REMOTE))
- return 0;
-
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
- vxl = &zif->l2info.vxl;
-
- vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
- if (!vlan_if)
- return -1;
-
- flags = DPLANE_NTF_EXT_LEARNED;
- if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
- flags |= DPLANE_NTF_ROUTER;
- ZEBRA_NEIGH_SET_ACTIVE(n);
-
- dplane_neigh_add(vlan_if, &n->ip, &n->emac, flags);
-
- return ret;
-}
-
-/*
- * Uninstall remote neighbor from the kernel.
- */
-static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n)
-{
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl;
- struct interface *vlan_if;
-
- if (!(n->flags & ZEBRA_NEIGH_REMOTE))
- return 0;
-
- if (!zvni->vxlan_if) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
- zvni->vni, zvni);
- return -1;
- }
-
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
- vxl = &zif->l2info.vxl;
- vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
- if (!vlan_if)
- return -1;
-
- ZEBRA_NEIGH_SET_INACTIVE(n);
- n->loc_seq = 0;
-
- dplane_neigh_delete(vlan_if, &n->ip);
-
- return 0;
-}
-
-/*
- * Probe neighbor from the kernel.
- */
-static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n)
-{
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl;
- struct interface *vlan_if;
-
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
- vxl = &zif->l2info.vxl;
-
- vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
- if (!vlan_if)
- return -1;
-
- dplane_neigh_update(vlan_if, &n->ip, &n->emac);
-
- return 0;
-}
-
-/*
- * Install neighbor hash entry - called upon access VLAN change.
- */
-static void zvni_install_neigh_hash(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_neigh_t *n;
- struct neigh_walk_ctx *wctx = ctxt;
-
- n = (zebra_neigh_t *)bucket->data;
-
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
- zvni_neigh_install(wctx->zvni, n);
-}
-
-/* Get the VRR interface for SVI if any */
-struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp)
-{
- struct zebra_vrf *zvrf = NULL;
- struct interface *tmp_if = NULL;
- struct zebra_if *zif = NULL;
-
- zvrf = vrf_info_lookup(ifp->vrf_id);
- assert(zvrf);
-
- FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) {
- zif = tmp_if->info;
- if (!zif)
- continue;
-
- if (!IS_ZEBRA_IF_MACVLAN(tmp_if))
- continue;
-
- if (zif->link == ifp)
- return tmp_if;
- }
-
- return NULL;
-}
-
-static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
-{
- struct listnode *cnode = NULL, *cnnode = NULL;
- struct connected *c = NULL;
- struct ethaddr macaddr;
-
- memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
-
- for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
- struct ipaddr ip;
-
- memset(&ip, 0, sizeof(struct ipaddr));
- if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
- continue;
-
- if (c->address->family == AF_INET) {
- ip.ipa_type = IPADDR_V4;
- memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
- sizeof(struct in_addr));
- } else if (c->address->family == AF_INET6) {
- ip.ipa_type = IPADDR_V6;
- memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
- sizeof(struct in6_addr));
- } else {
- continue;
- }
-
- zvni_gw_macip_del(ifp, zvni, &ip);
- }
-
- return 0;
-}
-
-static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
-{
- struct listnode *cnode = NULL, *cnnode = NULL;
- struct connected *c = NULL;
- struct ethaddr macaddr;
-
- memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
-
- for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
- struct ipaddr ip;
-
- memset(&ip, 0, sizeof(struct ipaddr));
- if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
- continue;
-
- if (c->address->family == AF_INET) {
- ip.ipa_type = IPADDR_V4;
- memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
- sizeof(struct in_addr));
- } else if (c->address->family == AF_INET6) {
- ip.ipa_type = IPADDR_V6;
- memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
- sizeof(struct in6_addr));
- } else {
- continue;
- }
-
- zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
- }
- return 0;
-}
-
-
-static int zvni_advertise_subnet(zebra_vni_t *zvni, struct interface *ifp,
- int advertise)
-{
- struct listnode *cnode = NULL, *cnnode = NULL;
- struct connected *c = NULL;
- struct ethaddr macaddr;
-
- memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
-
- for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
- struct prefix p;
-
- memcpy(&p, c->address, sizeof(struct prefix));
-
- /* skip link local address */
- if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
- continue;
-
- apply_mask(&p);
- if (advertise)
- ip_prefix_send_to_client(ifp->vrf_id, &p,
- ZEBRA_IP_PREFIX_ROUTE_ADD);
- else
- ip_prefix_send_to_client(ifp->vrf_id, &p,
- ZEBRA_IP_PREFIX_ROUTE_DEL);
- }
- return 0;
-}
-
-/*
- * zvni_gw_macip_add_to_client
- */
-static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
- struct ethaddr *macaddr, struct ipaddr *ip)
-{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
- struct zebra_vrf *zvrf;
- ns_id_t local_ns_id = NS_DEFAULT;
-
- zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
- if (zvrf && zvrf->zns)
- local_ns_id = zvrf->zns->ns_id;
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
-
- vxl = &zif->l2info.vxl;
-
- mac = zvni_mac_lookup(zvni, macaddr);
- if (!mac) {
- mac = zvni_mac_add(zvni, macaddr);
- if (!mac) {
- flog_err(EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add MAC %s intf %s(%u) VID %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vxl->access_vlan);
- return -1;
- }
- }
-
- /* Set "local" forwarding info. */
- SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.ns_id = local_ns_id;
- mac->fwd_info.local.vid = vxl->access_vlan;
-
- n = zvni_neigh_lookup(zvni, ip);
- if (!n) {
- n = zvni_neigh_add(zvni, ip, macaddr);
- if (!n) {
- flog_err(
- EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
- ipaddr2str(ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, zvni->vni);
- return -1;
- }
- }
-
- /* Set "local" forwarding info. */
- SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- ZEBRA_NEIGH_SET_ACTIVE(n);
- memcpy(&n->emac, macaddr, ETH_ALEN);
- n->ifindex = ifp->ifindex;
-
- /* Only advertise in BGP if the knob is enabled */
- if (advertise_gw_macip_enabled(zvni)) {
-
- SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
- SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
- /* Set Router flag (R-bit) */
- if (ip->ipa_type == IPADDR_V6)
- SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
- ifp->name, ifp->ifindex, zvni->vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
-
- zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
- } else if (advertise_svi_macip_enabled(zvni)) {
-
- SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP);
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x",
- ifp->name, ifp->ifindex, zvni->vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
-
- zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
- }
-
- return 0;
-}
-
-/*
- * zvni_gw_macip_del_from_client
- */
-static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
- struct ipaddr *ip)
-{
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
-
- /* If the neigh entry is not present nothing to do*/
- n = zvni_neigh_lookup(zvni, ip);
- if (!n)
- return 0;
-
- /* mac entry should be present */
- mac = zvni_mac_lookup(zvni, &n->emac);
- if (!mac) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u",
- prefix_mac2str(&n->emac,
- buf1, sizeof(buf1)),
- ipaddr2str(ip, buf2, sizeof(buf2)),
- zvni->vni);
- return -1;
- }
-
- /* If the entry is not local nothing to do*/
- if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
- return -1;
-
- /* only need to delete the entry from bgp if we sent it before */
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
- ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
- prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
- ipaddr2str(ip, buf2, sizeof(buf2)));
-
- /* Remove neighbor from BGP. */
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac,
- ZEBRA_MACIP_TYPE_GW, ZEBRA_NEIGH_ACTIVE);
-
- /* Delete this neighbor entry. */
- zvni_neigh_del(zvni, n);
-
- /* see if the mac needs to be deleted as well*/
- if (mac)
- zvni_deref_ip2mac(zvni, mac);
-
- return 0;
-}
-
-static void zvni_gw_macip_del_for_vni_hash(struct hash_bucket *bucket,
- void *ctxt)
-{
- zebra_vni_t *zvni = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan zl2_info;
- struct interface *vlan_if = NULL;
- struct interface *vrr_if = NULL;
- struct interface *ifp;
-
- /* Add primary SVI MAC*/
- zvni = (zebra_vni_t *)bucket->data;
-
- /* Global (Zvrf) advertise-default-gw is disabled,
- * but zvni advertise-default-gw is enabled
- */
- if (zvni->advertise_gw_macip) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip",
- zvni->vni);
- return;
- }
-
- ifp = zvni->vxlan_if;
- if (!ifp)
- return;
- zif = ifp->info;
-
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- return;
-
- zl2_info = zif->l2info.vxl;
-
- vlan_if =
- zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
- if (!vlan_if)
- return;
-
- /* Del primary MAC-IP */
- zvni_del_macip_for_intf(vlan_if, zvni);
-
- /* Del VRR MAC-IP - if any*/
- vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
- if (vrr_if)
- zvni_del_macip_for_intf(vrr_if, zvni);
-
- return;
-}
-
-static void zvni_gw_macip_add_for_vni_hash(struct hash_bucket *bucket,
- void *ctxt)
-{
- zebra_vni_t *zvni = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan zl2_info;
- struct interface *vlan_if = NULL;
- struct interface *vrr_if = NULL;
- struct interface *ifp = NULL;
-
- zvni = (zebra_vni_t *)bucket->data;
-
- ifp = zvni->vxlan_if;
- if (!ifp)
- return;
- zif = ifp->info;
-
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- return;
- zl2_info = zif->l2info.vxl;
-
- vlan_if =
- zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if);
- if (!vlan_if)
- return;
-
- /* Add primary SVI MAC-IP */
- zvni_add_macip_for_intf(vlan_if, zvni);
-
- if (advertise_gw_macip_enabled(zvni)) {
- /* Add VRR MAC-IP - if any*/
- vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
- if (vrr_if)
- zvni_add_macip_for_intf(vrr_if, zvni);
- }
-
- return;
-}
-
-static void zvni_svi_macip_del_for_vni_hash(struct hash_bucket *bucket,
- void *ctxt)
-{
- zebra_vni_t *zvni = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan zl2_info;
- struct interface *vlan_if = NULL;
- struct interface *ifp;
-
- /* Add primary SVI MAC*/
- zvni = (zebra_vni_t *)bucket->data;
- if (!zvni)
- return;
-
- /* Global(vrf) advertise-svi-ip disabled, but zvni advertise-svi-ip
- * enabled
- */
- if (zvni->advertise_svi_macip) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip",
- zvni->vni);
- return;
- }
-
- ifp = zvni->vxlan_if;
- if (!ifp)
- return;
- zif = ifp->info;
-
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- return;
-
- zl2_info = zif->l2info.vxl;
-
- vlan_if = zvni_map_to_svi(zl2_info.access_vlan,
- zif->brslave_info.br_if);
- if (!vlan_if)
- return;
-
- /* Del primary MAC-IP */
- zvni_del_macip_for_intf(vlan_if, zvni);
-
- return;
-}
-
-static int zvni_local_neigh_update(zebra_vni_t *zvni,
- struct interface *ifp,
- struct ipaddr *ip,
- struct ethaddr *macaddr,
- bool is_router)
-{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- struct zebra_vrf *zvrf;
- zebra_neigh_t *n = NULL;
- zebra_mac_t *zmac = NULL, *old_zmac = NULL;
- uint32_t old_mac_seq = 0, mac_new_seq = 0;
- bool upd_mac_seq = false;
- bool neigh_mac_change = false;
- bool neigh_on_hold = false;
- bool neigh_was_remote = false;
- bool do_dad = false;
- struct in_addr vtep_ip = {.s_addr = 0};
-
- /* Check if the MAC exists. */
- zmac = zvni_mac_lookup(zvni, macaddr);
- if (!zmac) {
- /* create a dummy MAC if the MAC is not already present */
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "AUTO MAC %s created for neigh %s on VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
-
- zmac = zvni_mac_add(zvni, macaddr);
- if (!zmac) {
- zlog_debug("Failed to add MAC %s VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- zvni->vni);
- return -1;
- }
-
- memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info));
- memset(&zmac->flags, 0, sizeof(uint32_t));
- SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO);
- } else {
- if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
- /*
- * We don't change the MAC to local upon a neighbor
- * learn event, we wait for the explicit local MAC
- * learn. However, we have to compute its sequence
- * number in preparation for when it actually turns
- * local.
- */
- upd_mac_seq = true;
- }
- }
-
- zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
- if (!zvrf) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(" Unable to find vrf for: %d",
- zvni->vxlan_if->vrf_id);
- return -1;
- }
-
- /* Check if the neighbor exists. */
- n = zvni_neigh_lookup(zvni, ip);
- if (!n) {
- /* New neighbor - create */
- n = zvni_neigh_add(zvni, ip, macaddr);
- if (!n) {
- flog_err(
- EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
- ipaddr2str(ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, zvni->vni);
- return -1;
- }
- /* Set "local" forwarding info. */
- SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- n->ifindex = ifp->ifindex;
- } else {
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- bool mac_different;
- bool cur_is_router;
-
- /* Note any changes and see if of interest to BGP. */
- mac_different = (memcmp(n->emac.octet,
- macaddr->octet, ETH_ALEN) != 0) ? 1 : 0;
- cur_is_router = !!CHECK_FLAG(n->flags,
- ZEBRA_NEIGH_ROUTER_FLAG);
- if (!mac_different && is_router == cur_is_router) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- " Ignoring entry mac is the same and is_router == cur_is_router");
- n->ifindex = ifp->ifindex;
- return 0;
- }
-
- if (!mac_different) {
- bool is_neigh_freezed = false;
-
- /* Only the router flag has changed. */
- if (is_router)
- SET_FLAG(n->flags,
- ZEBRA_NEIGH_ROUTER_FLAG);
- else
- UNSET_FLAG(n->flags,
- ZEBRA_NEIGH_ROUTER_FLAG);
-
- /* Neigh is in freeze state and freeze action
- * is enabled, do not send update to client.
- */
- is_neigh_freezed = (zvrf->dup_addr_detect &&
- zvrf->dad_freeze &&
- CHECK_FLAG(n->flags,
- ZEBRA_NEIGH_DUPLICATE));
-
- if (IS_ZEBRA_NEIGH_ACTIVE(n) &&
- !is_neigh_freezed)
- return zvni_neigh_send_add_to_client(
- zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
- else {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- " Neighbor active and frozen");
- }
- return 0;
- }
-
- /* The MAC has changed, need to issue a delete
- * first as this means a different MACIP route.
- * Also, need to do some unlinking/relinking.
- * We also need to update the MAC's sequence number
- * in different situations.
- */
- if (IS_ZEBRA_NEIGH_ACTIVE(n))
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
- &n->emac, 0, n->state);
- old_zmac = zvni_mac_lookup(zvni, &n->emac);
- if (old_zmac) {
- old_mac_seq = CHECK_FLAG(old_zmac->flags,
- ZEBRA_MAC_REMOTE) ?
- old_zmac->rem_seq : old_zmac->loc_seq;
- neigh_mac_change = upd_mac_seq = true;
- listnode_delete(old_zmac->neigh_list, n);
- zvni_deref_ip2mac(zvni, old_zmac);
- }
-
- /* Update the forwarding info. */
- n->ifindex = ifp->ifindex;
- memcpy(&n->emac, macaddr, ETH_ALEN);
-
- /* Link to new MAC */
- listnode_add_sort(zmac->neigh_list, n);
- } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
- /*
- * Neighbor has moved from remote to local. Its
- * MAC could have also changed as part of the move.
- */
- if (memcmp(n->emac.octet, macaddr->octet,
- ETH_ALEN) != 0) {
- old_zmac = zvni_mac_lookup(zvni, &n->emac);
- if (old_zmac) {
- old_mac_seq = CHECK_FLAG(
- old_zmac->flags,
- ZEBRA_MAC_REMOTE) ?
- old_zmac->rem_seq :
- old_zmac->loc_seq;
- neigh_mac_change = upd_mac_seq = true;
- listnode_delete(old_zmac->neigh_list,
- n);
- zvni_deref_ip2mac(zvni, old_zmac);
- }
-
- /* Link to new MAC */
- memcpy(&n->emac, macaddr, ETH_ALEN);
- listnode_add_sort(zmac->neigh_list, n);
- }
- /* Based on Mobility event Scenario-B from the
- * draft, neigh's previous state was remote treat this
- * event for DAD.
- */
- neigh_was_remote = true;
- vtep_ip = n->r_vtep_ip;
- /* Mark appropriately */
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
- n->r_vtep_ip.s_addr = INADDR_ANY;
- SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- n->ifindex = ifp->ifindex;
- }
- }
-
- /* If MAC was previously remote, or the neighbor had a different
- * MAC earlier, recompute the sequence number.
- */
- if (upd_mac_seq) {
- uint32_t seq1, seq2;
-
- seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) ?
- zmac->rem_seq + 1 : zmac->loc_seq;
- seq2 = neigh_mac_change ? old_mac_seq + 1 : 0;
- mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ?
- MAX(seq1, seq2) : zmac->loc_seq;
- }
-
- /* Mark Router flag (R-bit) */
- if (is_router)
- SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
- else
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
-
- /* Check old and/or new MAC detected as duplicate mark
- * the neigh as duplicate
- */
- if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) {
- flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC",
- zvni->vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(&n->ip, buf2, sizeof(buf2)));
- }
-
- /* For IP Duplicate Address Detection (DAD) is trigger,
- * when the event is extended mobility based on scenario-B
- * from the draft, IP/Neigh's MAC binding changed and
- * neigh's previous state was remote.
- */
- if (neigh_mac_change && neigh_was_remote)
- do_dad = true;
-
- zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad,
- &neigh_on_hold, true);
-
- /* Before we program this in BGP, we need to check if MAC is locally
- * learnt. If not, force neighbor to be inactive and reset its seq.
- */
- if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) {
- ZEBRA_NEIGH_SET_INACTIVE(n);
- n->loc_seq = 0;
- zmac->loc_seq = mac_new_seq;
- return 0;
- }
-
- /* If the MAC's sequence number has changed, inform the MAC and all
- * neighbors associated with the MAC to BGP, else just inform this
- * neighbor.
- */
- if (upd_mac_seq && zmac->loc_seq != mac_new_seq) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Seq changed for MAC %s VNI %u - old %u new %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- zvni->vni, zmac->loc_seq, mac_new_seq);
- zmac->loc_seq = mac_new_seq;
- if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
- zmac->flags, zmac->loc_seq))
- return -1;
- zvni_process_neigh_on_local_mac_change(zvni, zmac, 1);
- return 0;
- }
-
- n->loc_seq = zmac->loc_seq;
-
- if (!neigh_on_hold) {
- ZEBRA_NEIGH_SET_ACTIVE(n);
-
- return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
- } else {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(" Neighbor on hold not sending");
- }
- return 0;
-}
-
-static int zvni_remote_neigh_update(zebra_vni_t *zvni,
- struct interface *ifp,
- struct ipaddr *ip,
- struct ethaddr *macaddr,
- uint16_t state)
-{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
- zebra_neigh_t *n = NULL;
- zebra_mac_t *zmac = NULL;
-
- /* If the neighbor is unknown, there is no further action. */
- n = zvni_neigh_lookup(zvni, ip);
- if (!n)
- return 0;
-
- /* If a remote entry, see if it needs to be refreshed */
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
-#ifdef GNU_LINUX
- if (state & NUD_STALE)
- zvni_neigh_install(zvni, n);
-#endif
- } else {
- /* We got a "remote" neighbor notification for an entry
- * we think is local. This can happen in a multihoming
- * scenario - but only if the MAC is already "remote".
- * Just mark our entry as "remote".
- */
- zmac = zvni_mac_lookup(zvni, macaddr);
- if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
- zlog_debug(
- "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local",
- ipaddr2str(&n->ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- zvni->vni);
- return -1;
- }
-
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
- ZEBRA_NEIGH_SET_ACTIVE(n);
- n->r_vtep_ip = zmac->fwd_info.r_vtep_ip;
- }
-
- return 0;
-}
-
-/*
- * Make hash key for MAC.
- */
-static unsigned int mac_hash_keymake(const void *p)
-{
- const zebra_mac_t *pmac = p;
- const void *pnt = (void *)pmac->macaddr.octet;
-
- return jhash(pnt, ETH_ALEN, 0xa5a5a55a);
-}
-
-/*
- * Compare two MAC addresses.
- */
-static bool mac_cmp(const void *p1, const void *p2)
-{
- const zebra_mac_t *pmac1 = p1;
- const zebra_mac_t *pmac2 = p2;
-
- if (pmac1 == NULL && pmac2 == NULL)
- return true;
-
- if (pmac1 == NULL || pmac2 == NULL)
- return false;
-
- return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN)
- == 0);
-}
-
-/*
- * Callback to allocate MAC hash entry.
- */
-static void *zvni_mac_alloc(void *p)
-{
- const zebra_mac_t *tmp_mac = p;
- zebra_mac_t *mac;
-
- mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
- *mac = *tmp_mac;
-
- return ((void *)mac);
-}
-
-/*
- * Add MAC entry.
- */
-static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr)
-{
- zebra_mac_t tmp_mac;
- zebra_mac_t *mac = NULL;
-
- memset(&tmp_mac, 0, sizeof(zebra_mac_t));
- memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN);
- mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc);
- assert(mac);
-
- mac->zvni = zvni;
- mac->dad_mac_auto_recovery_timer = NULL;
-
- mac->neigh_list = list_new();
- mac->neigh_list->cmp = neigh_list_cmp;
-
- return mac;
-}
-
-/*
- * Delete MAC entry.
- */
-static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac)
-{
- zebra_mac_t *tmp_mac;
-
- /* Cancel auto recovery */
- THREAD_OFF(mac->dad_mac_auto_recovery_timer);
-
- list_delete(&mac->neigh_list);
-
- /* Free the VNI hash entry and allocated memory. */
- tmp_mac = hash_release(zvni->mac_table, mac);
- XFREE(MTYPE_MAC, tmp_mac);
-
- return 0;
-}
-
-static bool zvni_check_mac_del_from_db(struct mac_walk_ctx *wctx,
- zebra_mac_t *mac)
-{
- if ((wctx->flags & DEL_LOCAL_MAC) &&
- (mac->flags & ZEBRA_MAC_LOCAL))
- return true;
- else if ((wctx->flags & DEL_REMOTE_MAC) &&
- (mac->flags & ZEBRA_MAC_REMOTE))
- return true;
- else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) &&
- (mac->flags & ZEBRA_MAC_REMOTE) &&
- IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip))
- return true;
- else if ((wctx->flags & DEL_LOCAL_MAC) &&
- (mac->flags & ZEBRA_MAC_AUTO) &&
- !listcount(mac->neigh_list)) {
- if (IS_ZEBRA_DEBUG_VXLAN) {
- char buf[ETHER_ADDR_STRLEN];
-
- zlog_debug(
- "%s: Del MAC %s flags 0x%x", __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags);
- }
- wctx->uninstall = 0;
-
- return true;
- }
-
- return false;
-}
-
-/*
- * Free MAC hash entry (callback)
- */
-static void zvni_mac_del_hash_entry(struct hash_bucket *bucket, void *arg)
-{
- struct mac_walk_ctx *wctx = arg;
- zebra_mac_t *mac = bucket->data;
-
- if (zvni_check_mac_del_from_db(wctx, mac)) {
- if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) {
- zvni_mac_send_del_to_client(wctx->zvni->vni,
- &mac->macaddr);
- }
- if (wctx->uninstall)
- zvni_mac_uninstall(wctx->zvni, mac);
-
- zvni_mac_del(wctx->zvni, mac);
- }
-
- return;
-}
-
-/*
- * Delete all MAC entries for this VNI.
- */
-static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client,
- uint32_t flags)
-{
- struct mac_walk_ctx wctx;
-
- if (!zvni->mac_table)
- return;
-
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
- wctx.zvni = zvni;
- wctx.uninstall = uninstall;
- wctx.upd_client = upd_client;
- wctx.flags = flags;
-
- hash_iterate(zvni->mac_table, zvni_mac_del_hash_entry, &wctx);
-}
-
-/*
- * Look up MAC hash entry.
- */
-static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac)
-{
- zebra_mac_t tmp;
- zebra_mac_t *pmac;
-
- memset(&tmp, 0, sizeof(tmp));
- memcpy(&tmp.macaddr, mac, ETH_ALEN);
- pmac = hash_lookup(zvni->mac_table, &tmp);
-
- return pmac;
-}
-
-/*
- * Inform BGP about local MAC addition.
- */
-static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr,
- uint8_t mac_flags, uint32_t seq)
-{
- uint8_t flags = 0;
-
- if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY))
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
- if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW))
- SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
-
- return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
- seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD);
-}
-
-/*
- * Inform BGP about local MAC deletion.
- */
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr)
-{
- return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */,
- 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL);
-}
-
-struct zvni_from_svi_param {
- struct interface *br_if;
- struct interface *svi_if;
- struct zebra_if *zif;
- uint8_t bridge_vlan_aware;
- vlanid_t vid;
-};
-
-static int zvni_map_vlan_ns(struct ns *ns,
- void *_in_param,
- void **_p_zvni)
-{
- struct zebra_ns *zns = ns->info;
- struct route_node *rn;
- struct interface *br_if;
- zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni;
- zebra_vni_t *zvni;
- struct interface *tmp_if = NULL;
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl = NULL;
- struct zvni_from_svi_param *in_param =
- (struct zvni_from_svi_param *)_in_param;
- int found = 0;
-
- if (!in_param)
- return NS_WALK_STOP;
- br_if = in_param->br_if;
- zif = in_param->zif;
- assert(zif);
- assert(br_if);
-
- /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
- /* TODO: Optimize with a hash. */
- for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
- tmp_if = (struct interface *)rn->info;
- if (!tmp_if)
- continue;
- zif = tmp_if->info;
- if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
- continue;
- if (!if_is_operative(tmp_if))
- continue;
- vxl = &zif->l2info.vxl;
-
- if (zif->brslave_info.br_if != br_if)
- continue;
-
- if (!in_param->bridge_vlan_aware
- || vxl->access_vlan == in_param->vid) {
- found = 1;
- break;
- }
- }
- if (!found)
- return NS_WALK_CONTINUE;
-
- zvni = zvni_lookup(vxl->vni);
- if (p_zvni)
- *p_zvni = zvni;
- return NS_WALK_STOP;
-}
-
-/*
- * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC
- * notifications, to see if they are of interest.
- */
-static zebra_vni_t *zvni_map_vlan(struct interface *ifp,
- struct interface *br_if, vlanid_t vid)
-{
- struct zebra_if *zif;
- struct zebra_l2info_bridge *br;
- zebra_vni_t **p_zvni;
- zebra_vni_t *zvni = NULL;
- struct zvni_from_svi_param in_param;
-
- /* Determine if bridge is VLAN-aware or not */
- zif = br_if->info;
- assert(zif);
- br = &zif->l2info.br;
- in_param.bridge_vlan_aware = br->vlan_aware;
- in_param.vid = vid;
- in_param.br_if = br_if;
- in_param.zif = zif;
- p_zvni = &zvni;
-
- ns_walk_func(zvni_map_vlan_ns,
- (void *)&in_param,
- (void **)p_zvni);
- return zvni;
-}
-
-static int zvni_from_svi_ns(struct ns *ns,
- void *_in_param,
- void **_p_zvni)
-{
- struct zebra_ns *zns = ns->info;
- struct route_node *rn;
- struct interface *br_if;
- zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni;
- zebra_vni_t *zvni;
- struct interface *tmp_if = NULL;
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl = NULL;
- struct zvni_from_svi_param *in_param =
- (struct zvni_from_svi_param *)_in_param;
- int found = 0;
-
- if (!in_param)
- return NS_WALK_STOP;
- br_if = in_param->br_if;
- zif = in_param->zif;
- assert(zif);
-
- /* TODO: Optimize with a hash. */
- for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
- tmp_if = (struct interface *)rn->info;
- if (!tmp_if)
- continue;
- zif = tmp_if->info;
- if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
- continue;
- if (!if_is_operative(tmp_if))
- continue;
- vxl = &zif->l2info.vxl;
-
- if (zif->brslave_info.br_if != br_if)
- continue;
-
- if (!in_param->bridge_vlan_aware
- || vxl->access_vlan == !in_param->vid) {
- found = 1;
- break;
- }
- }
-
- if (!found)
- return NS_WALK_CONTINUE;
-
- zvni = zvni_lookup(vxl->vni);
- if (p_zvni)
- *p_zvni = zvni;
- return NS_WALK_STOP;
-}
-
-/*
- * Map SVI and associated bridge to a VNI. This is invoked upon getting
- * neighbor notifications, to see if they are of interest.
- */
-static zebra_vni_t *zvni_from_svi(struct interface *ifp,
- struct interface *br_if)
-{
- struct zebra_l2info_bridge *br;
- zebra_vni_t *zvni = NULL;
- zebra_vni_t **p_zvni;
- struct zebra_if *zif;
- struct zvni_from_svi_param in_param;
-
- if (!br_if)
- return NULL;
-
- /* Make sure the linked interface is a bridge. */
- if (!IS_ZEBRA_IF_BRIDGE(br_if))
- return NULL;
-
- /* Determine if bridge is VLAN-aware or not */
- zif = br_if->info;
- assert(zif);
- br = &zif->l2info.br;
- in_param.bridge_vlan_aware = br->vlan_aware;
- in_param.vid = 0;
-
- if (in_param.bridge_vlan_aware) {
- struct zebra_l2info_vlan *vl;
-
- if (!IS_ZEBRA_IF_VLAN(ifp))
- return NULL;
-
- zif = ifp->info;
- assert(zif);
- vl = &zif->l2info.vl;
- in_param.vid = vl->vid;
- }
-
- in_param.br_if = br_if;
- in_param.zif = zif;
- p_zvni = &zvni;
- /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
- ns_walk_func(zvni_from_svi_ns,
- (void *)&in_param,
- (void **)p_zvni);
- return zvni;
-}
-
-static int zvni_map_to_svi_ns(struct ns *ns,
- void *_in_param,
- void **_p_ifp)
-{
- struct zebra_ns *zns = ns->info;
- struct route_node *rn;
- struct zvni_from_svi_param *in_param =
- (struct zvni_from_svi_param *)_in_param;
- struct zebra_l2info_vlan *vl;
- struct interface *tmp_if = NULL;
- struct interface **p_ifp = (struct interface **)_p_ifp;
- struct zebra_if *zif;
-
- if (!in_param)
- return NS_WALK_STOP;
-
- /* TODO: Optimize with a hash. */
- for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
- tmp_if = (struct interface *)rn->info;
- /* Check oper status of the SVI. */
- if (!tmp_if || !if_is_operative(tmp_if))
- continue;
- zif = tmp_if->info;
- if (!zif || zif->zif_type != ZEBRA_IF_VLAN
- || zif->link != in_param->br_if)
- continue;
- vl = (struct zebra_l2info_vlan *)&zif->l2info.vl;
-
- if (vl->vid == in_param->vid) {
- if (p_ifp)
- *p_ifp = tmp_if;
- return NS_WALK_STOP;
- }
- }
- return NS_WALK_CONTINUE;
-}
-
/* Map to SVI on bridge corresponding to specified VLAN. This can be one
* of two cases:
* (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
@@ -3765,13 +789,17 @@ static int zvni_map_to_svi_ns(struct ns *ns,
* (b) In the case of a VLAN-unaware bridge, the SVI is the bridge interface
* itself
*/
-static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
+struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
{
+ struct zebra_ns *zns;
+ struct route_node *rn;
struct interface *tmp_if = NULL;
struct zebra_if *zif;
struct zebra_l2info_bridge *br;
- struct zvni_from_svi_param in_param;
- struct interface **p_ifp;
+ struct zebra_l2info_vlan *vl;
+ uint8_t bridge_vlan_aware;
+ int found = 0;
+
/* Defensive check, caller expected to invoke only with valid bridge. */
if (!br_if)
return NULL;
@@ -3780,483 +808,59 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
zif = br_if->info;
assert(zif);
br = &zif->l2info.br;
- in_param.bridge_vlan_aware = br->vlan_aware;
+ bridge_vlan_aware = br->vlan_aware;
+
/* Check oper status of the SVI. */
- if (!in_param.bridge_vlan_aware)
+ if (!bridge_vlan_aware)
return if_is_operative(br_if) ? br_if : NULL;
- in_param.vid = vid;
- in_param.br_if = br_if;
- in_param.zif = NULL;
- p_ifp = &tmp_if;
- /* Identify corresponding VLAN interface. */
- ns_walk_func(zvni_map_to_svi_ns,
- (void *)&in_param,
- (void **)p_ifp);
- return tmp_if;
-}
-
-static int zvni_map_to_macvlan_ns(struct ns *ns,
- void *_in_param,
- void **_p_ifp)
-{
- struct zebra_ns *zns = ns->info;
- struct zvni_from_svi_param *in_param =
- (struct zvni_from_svi_param *)_in_param;
- struct interface **p_ifp = (struct interface **)_p_ifp;
- struct route_node *rn;
- struct interface *tmp_if = NULL;
- struct zebra_if *zif;
-
- if (!in_param)
- return NS_WALK_STOP;
-
/* Identify corresponding VLAN interface. */
+ /* TODO: Optimize with a hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
tmp_if = (struct interface *)rn->info;
/* Check oper status of the SVI. */
if (!tmp_if || !if_is_operative(tmp_if))
continue;
zif = tmp_if->info;
-
- if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN)
+ if (!zif || zif->zif_type != ZEBRA_IF_VLAN
+ || zif->link != br_if)
continue;
+ vl = &zif->l2info.vl;
- if (zif->link == in_param->svi_if) {
- if (p_ifp)
- *p_ifp = tmp_if;
- return NS_WALK_STOP;
+ if (vl->vid == vid) {
+ found = 1;
+ break;
}
}
- return NS_WALK_CONTINUE;
+ return found ? tmp_if : NULL;
}
-/* Map to MAC-VLAN interface corresponding to specified SVI interface.
- */
-static struct interface *zvni_map_to_macvlan(struct interface *br_if,
- struct interface *svi_if)
+static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn)
{
- struct interface *tmp_if = NULL;
- struct zebra_if *zif;
- struct interface **p_ifp;
- struct zvni_from_svi_param in_param;
-
- /* Defensive check, caller expected to invoke only with valid bridge. */
- if (!br_if)
- return NULL;
-
- if (!svi_if) {
- zlog_debug("svi_if is not passed.");
- return NULL;
- }
-
- /* Determine if bridge is VLAN-aware or not */
- zif = br_if->info;
- assert(zif);
-
- in_param.vid = 0;
- in_param.br_if = br_if;
- in_param.zif = NULL;
- in_param.svi_if = svi_if;
- p_ifp = &tmp_if;
-
- /* Identify corresponding VLAN interface. */
- ns_walk_func(zvni_map_to_macvlan_ns,
- (void *)&in_param,
- (void **)p_ifp);
- return tmp_if;
-}
-
-/*
- * Install remote MAC into the forwarding plane.
- */
-static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
-{
- const struct zebra_if *zif, *br_zif;
- const struct zebra_l2info_vxlan *vxl;
- bool sticky;
- enum zebra_dplane_result res;
- const struct interface *br_ifp;
- vlanid_t vid;
-
- if (!(mac->flags & ZEBRA_MAC_REMOTE))
- return 0;
-
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
-
- br_ifp = zif->brslave_info.br_if;
- if (br_ifp == NULL)
- return -1;
-
- vxl = &zif->l2info.vxl;
-
- sticky = !!CHECK_FLAG(mac->flags,
- (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
-
- br_zif = (const struct zebra_if *)(br_ifp->info);
-
- if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
- vid = vxl->access_vlan;
- else
- vid = 0;
-
- res = dplane_mac_add(zvni->vxlan_if, br_ifp, vid,
- &mac->macaddr, mac->fwd_info.r_vtep_ip, sticky);
- if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
- return 0;
- else
- return -1;
-}
-
-/*
- * Uninstall remote MAC from the forwarding plane.
- */
-static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
-{
- const struct zebra_if *zif, *br_zif;
- const struct zebra_l2info_vxlan *vxl;
- struct in_addr vtep_ip;
- const struct interface *ifp, *br_ifp;
- vlanid_t vid;
- enum zebra_dplane_result res;
-
- if (!(mac->flags & ZEBRA_MAC_REMOTE))
- return 0;
-
- if (!zvni->vxlan_if) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
- zvni->vni, zvni);
- return -1;
- }
-
- zif = zvni->vxlan_if->info;
- if (!zif)
- return -1;
-
- br_ifp = zif->brslave_info.br_if;
- if (br_ifp == NULL)
- return -1;
-
- vxl = &zif->l2info.vxl;
-
- br_zif = (const struct zebra_if *)br_ifp->info;
-
- if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
- vid = vxl->access_vlan;
- else
- vid = 0;
-
- ifp = zvni->vxlan_if;
- vtep_ip = mac->fwd_info.r_vtep_ip;
-
- res = dplane_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip);
- if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
- return 0;
- else
- return -1;
-}
-
-/*
- * Install MAC hash entry - called upon access VLAN change.
- */
-static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_mac_t *mac;
- struct mac_walk_ctx *wctx = ctxt;
-
- mac = (zebra_mac_t *)bucket->data;
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
- zvni_mac_install(wctx->zvni, mac);
-}
-
-/*
- * Count of remote neighbors referencing this MAC.
- */
-static int remote_neigh_count(zebra_mac_t *zmac)
-{
- zebra_neigh_t *n = NULL;
- struct listnode *node = NULL;
- int count = 0;
-
- for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
- count++;
- }
-
- return count;
-}
-
-/*
- * Decrement neighbor refcount of MAC; uninstall and free it if
- * appropriate.
- */
-static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac)
-{
- if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO))
- return;
-
- /* If all remote neighbors referencing a remote MAC go away,
- * we need to uninstall the MAC.
- */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) &&
- remote_neigh_count(mac) == 0) {
- zvni_mac_uninstall(zvni, mac);
- UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- }
-
- /* If no neighbors, delete the MAC. */
- if (list_isempty(mac->neigh_list))
- zvni_mac_del(zvni, mac);
-}
-
-/*
- * Read and populate local MACs and neighbors corresponding to this VNI.
- */
-static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp)
-{
- struct zebra_ns *zns;
- struct zebra_vrf *zvrf;
- struct zebra_if *zif;
- struct interface *vlan_if;
- struct zebra_l2info_vxlan *vxl;
- struct interface *vrr_if;
-
- zif = ifp->info;
- vxl = &zif->l2info.vxl;
- zvrf = zebra_vrf_lookup_by_id(zvni->vrf_id);
- if (!zvrf || !zvrf->zns)
- return;
- zns = zvrf->zns;
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
- ifp->name, ifp->ifindex, zvni->vni,
- zif->brslave_info.bridge_ifindex);
-
- macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if);
- vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
- if (vlan_if) {
-
- /* Add SVI MAC-IP */
- zvni_add_macip_for_intf(vlan_if, zvni);
-
- /* Add VRR MAC-IP - if any*/
- vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
- if (vrr_if)
- zvni_add_macip_for_intf(vrr_if, zvni);
-
- neigh_read_for_vlan(zns, vlan_if);
- }
-}
-
-/*
- * Hash function for VNI.
- */
-static unsigned int vni_hash_keymake(const void *p)
-{
- const zebra_vni_t *zvni = p;
-
- return (jhash_1word(zvni->vni, 0));
-}
-
-/*
- * Compare 2 VNI hash entries.
- */
-static bool vni_hash_cmp(const void *p1, const void *p2)
-{
- const zebra_vni_t *zvni1 = p1;
- const zebra_vni_t *zvni2 = p2;
-
- return (zvni1->vni == zvni2->vni);
-}
-
-static int vni_list_cmp(void *p1, void *p2)
-{
- const zebra_vni_t *zvni1 = p1;
- const zebra_vni_t *zvni2 = p2;
-
- if (zvni1->vni == zvni2->vni)
- return 0;
- return (zvni1->vni < zvni2->vni) ? -1 : 1;
-}
-
-/*
- * Callback to allocate VNI hash entry.
- */
-static void *zvni_alloc(void *p)
-{
- const zebra_vni_t *tmp_vni = p;
- zebra_vni_t *zvni;
-
- zvni = XCALLOC(MTYPE_ZVNI, sizeof(zebra_vni_t));
- zvni->vni = tmp_vni->vni;
- return ((void *)zvni);
-}
-
-/*
- * Look up VNI hash entry.
- */
-static zebra_vni_t *zvni_lookup(vni_t vni)
-{
- struct zebra_vrf *zvrf;
- zebra_vni_t tmp_vni;
- zebra_vni_t *zvni = NULL;
-
- zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
- memset(&tmp_vni, 0, sizeof(zebra_vni_t));
- tmp_vni.vni = vni;
- zvni = hash_lookup(zvrf->vni_table, &tmp_vni);
-
- return zvni;
-}
-
-/*
- * Add VNI hash entry.
- */
-static zebra_vni_t *zvni_add(vni_t vni)
-{
- struct zebra_vrf *zvrf;
- zebra_vni_t tmp_zvni;
- zebra_vni_t *zvni = NULL;
-
- zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
- memset(&tmp_zvni, 0, sizeof(zebra_vni_t));
- tmp_zvni.vni = vni;
- zvni = hash_get(zvrf->vni_table, &tmp_zvni, zvni_alloc);
- assert(zvni);
-
- /* Create hash table for MAC */
- zvni->mac_table =
- hash_create(mac_hash_keymake, mac_cmp, "Zebra VNI MAC Table");
-
- /* Create hash table for neighbors */
- zvni->neigh_table = hash_create(neigh_hash_keymake, neigh_cmp,
- "Zebra VNI Neighbor Table");
-
- return zvni;
-}
-
-/*
- * Delete VNI hash entry.
- */
-static int zvni_del(zebra_vni_t *zvni)
-{
- struct zebra_vrf *zvrf;
- zebra_vni_t *tmp_zvni;
-
- zvrf = zebra_vrf_get_evpn();
- assert(zvrf);
-
- zvni->vxlan_if = NULL;
+ zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */);
/* Remove references to the BUM mcast grp */
- zebra_vxlan_sg_deref(zvni->local_vtep_ip, zvni->mcast_grp);
-
- /* Free the neighbor hash table. */
- hash_free(zvni->neigh_table);
- zvni->neigh_table = NULL;
-
- /* Free the MAC hash table. */
- hash_free(zvni->mac_table);
- zvni->mac_table = NULL;
-
- /* Free the VNI hash entry and allocated memory. */
- tmp_zvni = hash_release(zvrf->vni_table, zvni);
- XFREE(MTYPE_ZVNI, tmp_zvni);
-
- return 0;
-}
-
-/*
- * Inform BGP about local VNI addition.
- */
-static int zvni_send_add_to_client(zebra_vni_t *zvni)
-{
- struct zserv *client;
- struct stream *s;
-
- client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
- /* BGP may not be running. */
- if (!client)
- return 0;
-
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
-
- zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
- stream_putl(s, zvni->vni);
- stream_put_in_addr(s, &zvni->local_vtep_ip);
- stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
- stream_put_in_addr(s, &zvni->mcast_grp);
+ zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp);
- /* Write packet size. */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni,
- inet_ntoa(zvni->local_vtep_ip),
- vrf_id_to_name(zvni->vrf_id),
- zebra_route_string(client->proto));
-
- client->vniadd_cnt++;
- return zserv_send_message(client, s);
+ return zebra_evpn_del(zevpn);
}
-
/*
- * Inform BGP about local VNI deletion.
+ * Build the VNI hash table by going over the VxLAN interfaces. This
+ * is called when EVPN (advertise-all-vni) is enabled.
*/
-static int zvni_send_del_to_client(vni_t vni)
-{
- struct zserv *client;
- struct stream *s;
-
- client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
- /* BGP may not be running. */
- if (!client)
- return 0;
-
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
- stream_reset(s);
-
- zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id());
- stream_putl(s, vni);
-
- /* Write packet size. */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send VNI_DEL %u to %s", vni,
- zebra_route_string(client->proto));
-
- client->vnidel_cnt++;
- return zserv_send_message(client, s);
-}
-
-static int zvni_build_hash_table_ns(struct ns *ns,
- void *param_in __attribute__((unused)),
- void **param_out __attribute__((unused)))
+static void zevpn_build_hash_table(void)
{
- struct zebra_ns *zns = ns->info;
+ struct zebra_ns *zns;
struct route_node *rn;
struct interface *ifp;
- struct zebra_vrf *zvrf;
-
- zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return NS_WALK_STOP;
-
- /* Walk VxLAN interfaces and create VNI hash. */
+ /* Walk VxLAN interfaces and create EVPN hash. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
vni_t vni;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
@@ -4270,15 +874,7 @@ static int zvni_build_hash_table_ns(struct ns *ns,
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- /* link of VXLAN interface should be in zebra_evpn_vrf */
- if (zvrf->zns->ns_id != vxl->link_nsid) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Intf %s(%u) VNI %u, link not in same "
- "namespace than BGP EVPN core instance ",
- ifp->name, ifp->ifindex, vni);
- continue;
- }
+
/* L3-VNI and L2-VNI are handled seperately */
zl3vni = zl3vni_lookup(vni);
if (zl3vni) {
@@ -4321,11 +917,11 @@ static int zvni_build_hash_table_ns(struct ns *ns,
ifp->name, ifp->ifindex, vni,
inet_ntoa(vxl->vtep_ip));
- /* VNI hash entry is expected to exist, if the BGP process is killed */
- zvni = zvni_lookup(vni);
- if (zvni) {
+ /* EVPN hash entry is expected to exist, if the BGP process is killed */
+ zevpn = zebra_evpn_lookup(vni);
+ if (zevpn) {
zlog_debug(
- "VNI hash already present for IF %s(%u) L2-VNI %u",
+ "EVPN hash already present for IF %s(%u) L2-VNI %u",
ifp->name, ifp->ifindex, vni);
/*
@@ -4334,44 +930,49 @@ static int zvni_build_hash_table_ns(struct ns *ns,
*/
if (if_is_operative(ifp) &&
zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ zebra_evpn_send_add_to_client(zevpn);
/* Send Local MAC-entries to client */
- zvni_send_mac_to_client(zvni);
+ zebra_evpn_send_mac_list_to_client(zevpn);
/* Send Loval Neighbor entries to client */
- zvni_send_neigh_to_client(zvni);
+ zebra_evpn_send_neigh_to_client(zevpn);
} else {
- zvni = zvni_add(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_add(vni);
+ if (!zevpn) {
zlog_debug(
- "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
+ "Failed to add EVPN hash, IF %s(%u) L2-VNI %u",
ifp->name, ifp->ifindex, vni);
- return NS_WALK_CONTINUE;
+ return;
}
- if (zvni->local_vtep_ip.s_addr !=
+ if (zevpn->local_vtep_ip.s_addr !=
vxl->vtep_ip.s_addr ||
- zvni->mcast_grp.s_addr !=
+ zevpn->mcast_grp.s_addr !=
vxl->mcast_grp.s_addr) {
zebra_vxlan_sg_deref(
- zvni->local_vtep_ip,
- zvni->mcast_grp);
+ zevpn->local_vtep_ip,
+ zevpn->mcast_grp);
zebra_vxlan_sg_ref(vxl->vtep_ip,
vxl->mcast_grp);
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->mcast_grp = vxl->mcast_grp;
+ zevpn->local_vtep_ip = vxl->vtep_ip;
+ zevpn->mcast_grp = vxl->mcast_grp;
+ /* on local vtep-ip check if ES
+ * orig-ip needs to be updated
+ */
+ zebra_evpn_es_set_base_evpn(zevpn);
}
- zvni->vxlan_if = ifp;
- vlan_if = zvni_map_to_svi(vxl->access_vlan,
- zif->brslave_info.br_if);
+ zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
+ vlan_if = zvni_map_to_svi(
+ vxl->access_vlan,
+ zif->brslave_info.br_if);
if (vlan_if) {
- zvni->vrf_id = vlan_if->vrf_id;
+ zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(
vlan_if->vrf_id);
if (zl3vni)
listnode_add_sort(
- zl3vni->l2vnis, zvni);
+ zl3vni->l2vnis, zevpn);
}
/*
@@ -4380,195 +981,30 @@ static int zvni_build_hash_table_ns(struct ns *ns,
*/
if (if_is_operative(ifp) &&
zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ zebra_evpn_send_add_to_client(zevpn);
}
}
}
- return NS_WALK_CONTINUE;
-}
-
-/*
- * Build the VNI hash table by going over the VxLAN interfaces. This
- * is called when EVPN (advertise-all-vni) is enabled.
- */
-
-static void zvni_build_hash_table(void)
-{
- ns_walk_func(zvni_build_hash_table_ns,
- (void *)NULL,
- (void **)NULL);
-}
-
-/*
- * See if remote VTEP matches with prefix.
- */
-static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
-{
- return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip));
-}
-
-/*
- * Locate remote VTEP in VNI hash table.
- */
-static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip)
-{
- zebra_vtep_t *zvtep;
-
- if (!zvni)
- return NULL;
-
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
- if (zvni_vtep_match(vtep_ip, zvtep))
- break;
- }
-
- return zvtep;
-}
-
-/*
- * Add remote VTEP to VNI hash table.
- */
-static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip,
- int flood_control)
-
-{
- zebra_vtep_t *zvtep;
-
- zvtep = XCALLOC(MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t));
-
- zvtep->vtep_ip = *vtep_ip;
- zvtep->flood_control = flood_control;
-
- if (zvni->vteps)
- zvni->vteps->prev = zvtep;
- zvtep->next = zvni->vteps;
- zvni->vteps = zvtep;
-
- return zvtep;
-}
-
-/*
- * Remove remote VTEP from VNI hash table.
- */
-static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep)
-{
- if (zvtep->next)
- zvtep->next->prev = zvtep->prev;
- if (zvtep->prev)
- zvtep->prev->next = zvtep->next;
- else
- zvni->vteps = zvtep->next;
-
- zvtep->prev = zvtep->next = NULL;
- XFREE(MTYPE_ZVNI_VTEP, zvtep);
-
- return 0;
-}
-
-/*
- * Delete all remote VTEPs for this VNI (upon VNI delete). Also
- * uninstall from kernel if asked to.
- */
-static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall)
-{
- zebra_vtep_t *zvtep, *zvtep_next;
-
- if (!zvni)
- return -1;
-
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) {
- zvtep_next = zvtep->next;
- if (uninstall)
- zvni_vtep_uninstall(zvni, &zvtep->vtep_ip);
- zvni_vtep_del(zvni, zvtep);
- }
-
- return 0;
-}
-
-/*
- * Install remote VTEP into the kernel if the remote VTEP has asked
- * for head-end-replication.
- */
-static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep)
-{
- if (is_vxlan_flooding_head_end() &&
- (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) {
- if (ZEBRA_DPLANE_REQUEST_FAILURE ==
- dplane_vtep_add(zvni->vxlan_if,
- &zvtep->vtep_ip, zvni->vni))
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Uninstall remote VTEP from the kernel.
- */
-static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip)
-{
- if (!zvni->vxlan_if) {
- zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf",
- zvni->vni, zvni);
- return -1;
- }
-
- if (ZEBRA_DPLANE_REQUEST_FAILURE ==
- dplane_vtep_delete(zvni->vxlan_if, vtep_ip, zvni->vni))
- return -1;
-
- return 0;
-}
-
-/*
- * Install or uninstall flood entries in the kernel corresponding to
- * remote VTEPs. This is invoked upon change to BUM handling.
- */
-static void zvni_handle_flooding_remote_vteps(struct hash_bucket *bucket,
- void *zvrf)
-{
- zebra_vni_t *zvni;
- zebra_vtep_t *zvtep;
-
- zvni = (zebra_vni_t *)bucket->data;
- if (!zvni)
- return;
-
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
- if (is_vxlan_flooding_head_end())
- zvni_vtep_install(zvni, zvtep);
- else
- zvni_vtep_uninstall(zvni, &zvtep->vtep_ip);
- }
}
/*
- * Cleanup VNI/VTEP and update kernel
+ * Cleanup EVPN/VTEP and update kernel
*/
-static void zvni_cleanup_all(struct hash_bucket *bucket, void *arg)
+static void zebra_evpn_vxlan_cleanup_all(struct hash_bucket *bucket, void *arg)
{
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
struct zebra_vrf *zvrf = (struct zebra_vrf *)arg;
- zvni = (zebra_vni_t *)bucket->data;
+ zevpn = (zebra_evpn_t *)bucket->data;
/* remove from l3-vni list */
if (zvrf->l3vni)
zl3vni = zl3vni_lookup(zvrf->l3vni);
if (zl3vni)
- listnode_delete(zl3vni->l2vnis, zvni);
-
- /* Free up all neighbors and MACs, if any. */
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ listnode_delete(zl3vni->l2vnis, zevpn);
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
-
- /* Delete the hash entry. */
- zvni_del(zvni);
+ zebra_evpn_cleanup_all(bucket, arg);
}
/* cleanup L3VNI */
@@ -4641,7 +1077,7 @@ static void *zl3vni_rmac_alloc(void *p)
const zebra_mac_t *tmp_rmac = p;
zebra_mac_t *zrmac;
- zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
+ zrmac = XCALLOC(MTYPE_L3VNI_MAC, sizeof(zebra_mac_t));
*zrmac = *tmp_rmac;
return ((void *)zrmac);
@@ -4685,7 +1121,7 @@ static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
}
tmp_rmac = hash_release(zl3vni->rmac_table, zrmac);
- XFREE(MTYPE_MAC, tmp_rmac);
+ XFREE(MTYPE_L3VNI_MAC, tmp_rmac);
return 0;
}
@@ -4722,8 +1158,9 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
else
vid = 0;
- res = dplane_mac_add(zl3vni->vxlan_if, br_ifp, vid,
- &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+ res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0, 0,
+ false /*was_static*/);
if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
return 0;
else
@@ -4772,7 +1209,7 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
else
vid = 0;
- res = dplane_mac_del(zl3vni->vxlan_if, br_ifp, vid,
+ res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid,
&zrmac->macaddr, zrmac->fwd_info.r_vtep_ip);
if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
return 0;
@@ -4881,7 +1318,7 @@ static void *zl3vni_nh_alloc(void *p)
const zebra_neigh_t *tmp_n = p;
zebra_neigh_t *n;
- n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ n = XCALLOC(MTYPE_L3NEIGH, sizeof(zebra_neigh_t));
*n = *tmp_n;
return ((void *)n);
@@ -4927,7 +1364,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)
}
tmp_n = hash_release(zl3vni->nh_table, n);
- XFREE(MTYPE_NEIGH, tmp_n);
+ XFREE(MTYPE_L3NEIGH, tmp_n);
return 0;
}
@@ -4951,7 +1388,8 @@ static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)
if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG)
flags |= DPLANE_NTF_ROUTER;
- dplane_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags);
+ dplane_rem_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags,
+ false /*was_static*/);
return ret;
}
@@ -4968,7 +1406,7 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)
if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if))
return 0;
- dplane_neigh_delete(zl3vni->svi_if, &n->ip);
+ dplane_rem_neigh_delete(zl3vni->svi_if, &n->ip);
return 0;
}
@@ -5112,7 +1550,7 @@ static void *zl3vni_alloc(void *p)
/*
* Look up L3 VNI hash entry.
*/
-static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
+zebra_l3vni_t *zl3vni_lookup(vni_t vni)
{
zebra_l3vni_t tmp_l3vni;
zebra_l3vni_t *zl3vni = NULL;
@@ -5142,15 +1580,13 @@ static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
zl3vni->svi_if = NULL;
zl3vni->vxlan_if = NULL;
zl3vni->l2vnis = list_new();
- zl3vni->l2vnis->cmp = vni_list_cmp;
+ zl3vni->l2vnis->cmp = zebra_evpn_list_cmp;
/* Create hash table for remote RMAC */
- zl3vni->rmac_table = hash_create(mac_hash_keymake, mac_cmp,
- "Zebra L3-VNI RMAC-Table");
+ zl3vni->rmac_table = zebra_mac_db_create("Zebra L3-VNI RMAC-Table");
/* Create hash table for neighbors */
- zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp,
- "Zebra L3-VNI next-hop table");
+ zl3vni->nh_table = zebra_neigh_db_create("Zebra L3-VNI next-hop table");
return zl3vni;
}
@@ -5181,22 +1617,14 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni)
return 0;
}
-static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
- void *_zl3vni,
- void **_pifp)
+struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
{
- struct zebra_ns *zns = ns->info;
- zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni;
+ struct zebra_ns *zns = NULL;
struct route_node *rn = NULL;
struct interface *ifp = NULL;
- struct zebra_vrf *zvrf;
-
- zvrf = zebra_vrf_get_evpn();
-
- if (!zvrf)
- return NS_WALK_STOP;
/* loop through all vxlan-interface */
+ zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
struct zebra_if *zif = NULL;
@@ -5211,39 +1639,13 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns,
continue;
vxl = &zif->l2info.vxl;
- if (vxl->vni != zl3vni->vni)
- continue;
-
- /* link of VXLAN interface should be in zebra_evpn_vrf */
- if (zvrf->zns->ns_id != vxl->link_nsid) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Intf %s(%u) VNI %u, link not in same "
- "namespace than BGP EVPN core instance ",
- ifp->name, ifp->ifindex, vxl->vni);
- continue;
+ if (vxl->vni == zl3vni->vni) {
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ return ifp;
}
-
-
- zl3vni->local_vtep_ip = vxl->vtep_ip;
- if (_pifp)
- *_pifp = (void *)ifp;
- return NS_WALK_STOP;
}
- return NS_WALK_CONTINUE;
-}
-
-struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
-{
- struct interface **p_ifp;
- struct interface *ifp = NULL;
-
- p_ifp = &ifp;
-
- ns_walk_func(zl3vni_map_to_vxlan_if_ns,
- (void *)zl3vni, (void **)p_ifp);
- return ifp;
+ return NULL;
}
struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
@@ -5280,7 +1682,8 @@ struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni)
if (!zif)
return NULL;
- return zvni_map_to_macvlan(zif->brslave_info.br_if, zl3vni->svi_if);
+ return zebra_evpn_map_to_macvlan(zif->brslave_info.br_if,
+ zl3vni->svi_if);
}
@@ -5493,13 +1896,13 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
zl3vni_send_del_to_client(zl3vni);
}
-static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt)
+static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt)
{
- zebra_vni_t *zvni = (zebra_vni_t *)bucket->data;
+ zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data;
zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)ctxt;
- if (zvni->vrf_id == zl3vni_vrf_id(zl3vni))
- listnode_add_sort(zl3vni->l2vnis, zvni);
+ if (zevpn->vrf_id == zl3vni_vrf_id(zl3vni))
+ listnode_add_sort(zl3vni->l2vnis, zevpn);
}
/*
@@ -5508,7 +1911,7 @@ static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt)
static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
int add)
{
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
/* There is a possibility that VNI notification was already received
* from kernel and we programmed it as L2-VNI
@@ -5520,28 +1923,27 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
*/
if (add) {
/* Locate hash entry */
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Del L2-VNI %u - transition to L3-VNI", vni);
- /* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ /* Delete EVPN from BGP. */
+ zebra_evpn_send_del_to_client(zevpn);
- /* Free up all neighbors and MAC, if any. */
- zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+ zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH);
+ zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ zebra_evpn_vtep_del_all(zevpn, 0);
/* Delete the hash entry. */
- if (zvni_del(zvni)) {
+ if (zebra_evpn_vxlan_del(zevpn)) {
flog_err(EC_ZEBRA_VNI_DEL_FAILED,
- "Failed to del VNI hash %p, VNI %u", zvni,
- zvni->vni);
+ "Failed to del EVPN hash %p, VNI %u", zevpn,
+ zevpn->vni);
return -1;
}
} else {
@@ -5583,40 +1985,6 @@ static void zl3vni_del_nh_hash_entry(struct hash_bucket *bucket, void *ctx)
zl3vni_nh_del(zl3vni, n);
}
-static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
- uint16_t cmd)
-{
- struct zserv *client = NULL;
- struct stream *s = NULL;
- char buf[PREFIX_STRLEN];
-
- client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
- /* BGP may not be running. */
- if (!client)
- return 0;
-
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
-
- zclient_create_header(s, cmd, vrf_id);
- stream_put(s, p, sizeof(struct prefix));
-
- /* Write packet size. */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send ip prefix %s %s on vrf %s",
- prefix2str(p, buf, sizeof(buf)),
- (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
- vrf_id_to_name(vrf_id));
-
- if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD)
- client->prefixadd_cnt++;
- else
- client->prefixdel_cnt++;
-
- return zserv_send_message(client, s);
-}
-
/* re-add remote rmac if needed */
static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
struct ethaddr *rmac)
@@ -5636,503 +2004,6 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
return 0;
}
-/* Process a remote MACIP add from BGP. */
-static void process_remote_macip_add(vni_t vni,
- struct ethaddr *macaddr,
- uint16_t ipa_len,
- struct ipaddr *ipaddr,
- uint8_t flags,
- uint32_t seq,
- struct in_addr vtep_ip)
-{
- zebra_vni_t *zvni;
- zebra_vtep_t *zvtep;
- zebra_mac_t *mac = NULL, *old_mac = NULL;
- zebra_neigh_t *n = NULL;
- int update_mac = 0, update_neigh = 0;
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
- struct interface *ifp = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_vrf *zvrf;
- uint32_t tmp_seq;
- bool sticky;
- bool remote_gw;
- bool is_router;
- bool do_dad = false;
- bool is_dup_detect = false;
-
- /* Locate VNI hash entry - expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni);
- return;
- }
-
- ifp = zvni->vxlan_if;
- if (ifp)
- zif = ifp->info;
- if (!ifp ||
- !if_is_operative(ifp) ||
- !zif ||
- !zif->brslave_info.br_if) {
- zlog_warn("Ignoring remote MACIP ADD VNI %u, invalid interface state or info",
- vni);
- return;
- }
-
- /* The remote VTEP specified should normally exist, but it is
- * possible that when peering comes up, peer may advertise MACIP
- * routes before advertising type-3 routes.
- */
- zvtep = zvni_vtep_find(zvni, &vtep_ip);
- if (!zvtep) {
- zvtep = zvni_vtep_add(zvni, &vtep_ip, VXLAN_FLOOD_DISABLED);
- if (!zvtep) {
- flog_err(
- EC_ZEBRA_VTEP_ADD_FAILED,
- "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD",
- vni, zvni);
- return;
- }
-
- zvni_vtep_install(zvni, zvtep);
- }
-
- sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
- remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
- is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
-
- mac = zvni_mac_lookup(zvni, macaddr);
-
- /* Ignore if the mac is already present as a gateway mac */
- if (mac &&
- CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) &&
- CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
- vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipa_len ? " IP " : "",
- ipa_len ?
- ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
- return;
- }
-
- zvrf = zebra_vrf_get_evpn();
- if (!zvrf)
- return;
-
- /* check if the remote MAC is unknown or has a change.
- * If so, that needs to be updated first. Note that client could
- * install MAC and MACIP separately or just install the latter.
- */
- if (!mac
- || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
- || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)
- || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)
- || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip)
- || seq != mac->rem_seq)
- update_mac = 1;
-
- if (update_mac) {
- if (!mac) {
- mac = zvni_mac_add(zvni, macaddr);
- if (!mac) {
- zlog_warn(
- "Failed to add MAC %s VNI %u Remote VTEP %s",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- vni, inet_ntoa(vtep_ip));
- return;
- }
-
- /* Is this MAC created for a MACIP? */
- if (ipa_len)
- SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- } else {
- /* When host moves but changes its (MAC,IP)
- * binding, BGP may install a MACIP entry that
- * corresponds to "older" location of the host
- * in transient situations (because {IP1,M1}
- * is a different route from {IP1,M2}). Check
- * the sequence number and ignore this update
- * if appropriate.
- */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
- tmp_seq = mac->loc_seq;
- else
- tmp_seq = mac->rem_seq;
-
- if (seq < tmp_seq) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x",
- vni,
- prefix_mac2str(macaddr,
- buf, sizeof(buf)),
- ipa_len ? " IP " : "",
- ipa_len ?
- ipaddr2str(ipaddr,
- buf1, sizeof(buf1)) : "",
- tmp_seq, mac->flags);
- return;
- }
- }
-
- /* Check MAC's curent state is local (this is the case
- * where MAC has moved from L->R) and check previous
- * detection started via local learning.
- * RFC-7432: A PE/VTEP that detects a MAC mobility
- * event via local learning starts an M-second timer.
- *
- * VTEP-IP or seq. change alone is not considered
- * for dup. detection.
- *
- * MAC is already marked duplicate set dad, then
- * is_dup_detect will be set to not install the entry.
- */
- if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) &&
- mac->dad_count) ||
- CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
- do_dad = true;
-
- /* Remove local MAC from BGP. */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
- zvni_mac_send_del_to_client(zvni->vni, macaddr);
-
- /* Set "auto" and "remote" forwarding info. */
- UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- mac->fwd_info.r_vtep_ip = vtep_ip;
-
- if (sticky)
- SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
- else
- UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
-
- if (remote_gw)
- SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
- else
- UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW);
-
- zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac,
- mac->fwd_info.r_vtep_ip,
- do_dad, &is_dup_detect,
- false);
-
- if (!is_dup_detect) {
- zvni_process_neigh_on_remote_mac_add(zvni, mac);
- /* Install the entry. */
- zvni_mac_install(zvni, mac);
- }
- }
-
- /* Update seq number. */
- mac->rem_seq = seq;
-
- /* If there is no IP, return after clearing AUTO flag of MAC. */
- if (!ipa_len) {
- UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- return;
- }
-
- /* Reset flag */
- do_dad = false;
-
- /* Check if the remote neighbor itself is unknown or has a
- * change. If so, create or update and then install the entry.
- */
- n = zvni_neigh_lookup(zvni, ipaddr);
- if (!n
- || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
- || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)
- || (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0)
- || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip)
- || seq != n->rem_seq)
- update_neigh = 1;
-
- if (update_neigh) {
- if (!n) {
- n = zvni_neigh_add(zvni, ipaddr, macaddr);
- if (!n) {
- zlog_warn(
- "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
- ipaddr2str(ipaddr, buf1,
- sizeof(buf1)),
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- vni, inet_ntoa(vtep_ip));
- return;
- }
-
- } else {
- const char *n_type;
-
- /* When host moves but changes its (MAC,IP)
- * binding, BGP may install a MACIP entry that
- * corresponds to "older" location of the host
- * in transient situations (because {IP1,M1}
- * is a different route from {IP1,M2}). Check
- * the sequence number and ignore this update
- * if appropriate.
- */
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
- tmp_seq = n->loc_seq;
- n_type = "local";
- } else {
- tmp_seq = n->rem_seq;
- n_type = "remote";
- }
- if (seq < tmp_seq) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u",
- vni,
- prefix_mac2str(macaddr,
- buf, sizeof(buf)),
- " IP ",
- ipaddr2str(ipaddr, buf1, sizeof(buf1)),
- n_type,
- tmp_seq);
- return;
- }
- if (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) {
- /* MAC change, send a delete for old
- * neigh if learnt locally.
- */
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) &&
- IS_ZEBRA_NEIGH_ACTIVE(n))
- zvni_neigh_send_del_to_client(
- zvni->vni, &n->ip,
- &n->emac, 0, n->state);
-
- /* update neigh list for macs */
- old_mac = zvni_mac_lookup(zvni, &n->emac);
- if (old_mac) {
- listnode_delete(old_mac->neigh_list, n);
- zvni_deref_ip2mac(zvni, old_mac);
- }
- listnode_add_sort(mac->neigh_list, n);
- memcpy(&n->emac, macaddr, ETH_ALEN);
-
- /* Check Neigh's curent state is local
- * (this is the case where neigh/host has moved
- * from L->R) and check previous detction
- * started via local learning.
- *
- * RFC-7432: A PE/VTEP that detects a MAC
- * mobilit event via local learning starts
- * an M-second timer.
- * VTEP-IP or seq. change along is not
- * considered for dup. detection.
- *
- * Mobilty event scenario-B IP-MAC binding
- * changed.
- */
- if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
- && n->dad_count)
- do_dad = true;
-
- }
- }
-
- /* Set "remote" forwarding info. */
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- n->r_vtep_ip = vtep_ip;
- SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
-
- /* Set router flag (R-bit) to this Neighbor entry */
- if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG))
- SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
- else
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
-
- /* Check old or new MAC detected as duplicate,
- * inherit duplicate flag to this neigh.
- */
- if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac,
- mac, n)) {
- flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC",
- zvni->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- ipaddr2str(&n->ip, buf1, sizeof(buf1)));
- }
-
- /* Check duplicate address detection for IP */
- zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n,
- n->r_vtep_ip,
- do_dad,
- &is_dup_detect,
- false);
- /* Install the entry. */
- if (!is_dup_detect)
- zvni_neigh_install(zvni, n);
- }
-
- zvni_probe_neigh_on_mac_add(zvni, mac);
-
- /* Update seq number. */
- n->rem_seq = seq;
-}
-
-/* Process a remote MACIP delete from BGP. */
-static void process_remote_macip_del(vni_t vni,
- struct ethaddr *macaddr,
- uint16_t ipa_len,
- struct ipaddr *ipaddr,
- struct in_addr vtep_ip)
-{
- zebra_vni_t *zvni;
- zebra_mac_t *mac = NULL;
- zebra_neigh_t *n = NULL;
- struct interface *ifp = NULL;
- struct zebra_if *zif = NULL;
- struct zebra_ns *zns;
- struct zebra_l2info_vxlan *vxl;
- struct zebra_vrf *zvrf;
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
-
- /* Locate VNI hash entry - expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni);
- return;
- }
-
- ifp = zvni->vxlan_if;
- if (ifp)
- zif = ifp->info;
- if (!ifp ||
- !if_is_operative(ifp) ||
- !zif ||
- !zif->brslave_info.br_if) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Ignoring remote MACIP DEL VNI %u, invalid interface state or info",
- vni);
- return;
- }
- zns = zebra_ns_lookup(NS_DEFAULT);
- vxl = &zif->l2info.vxl;
-
- /* It is possible remote vtep del request is processed prior to
- * remote macip route delete. remote_vtep_del does not clean up
- * the macip route delete. Explicite withdraw of the macip route
- * is expected to recieve. This handler removes the remote route.
- */
- mac = zvni_mac_lookup(zvni, macaddr);
- if (ipa_len)
- n = zvni_neigh_lookup(zvni, ipaddr);
-
- if (n && !mac) {
- zlog_warn("Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni);
- return;
- }
-
- /* If the remote mac or neighbor doesn't exist there is nothing
- * more to do. Otherwise, uninstall the entry and then remove it.
- */
- if (!mac && !n)
- return;
-
- zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
-
- /* Ignore the delete if this mac is a gateway mac-ip */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
- && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
- zlog_warn(
- "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
- vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipa_len ? " IP " : "",
- ipa_len ?
- ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
- return;
- }
-
- /* Uninstall remote neighbor or MAC. */
- if (n) {
- if (zvrf->dad_freeze &&
- CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) &&
- CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) &&
- (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) {
- struct interface *vlan_if;
-
- vlan_if = zvni_map_to_svi(vxl->access_vlan,
- zif->brslave_info.br_if);
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
- __func__,
- ipaddr2str(ipaddr, buf1, sizeof(buf1)),
- n->flags,
- vlan_if ? vlan_if->name : "Unknown");
- if (vlan_if)
- neigh_read_specific_ip(ipaddr, vlan_if);
- }
-
- /* When the MAC changes for an IP, it is possible the
- * client may update the new MAC before trying to delete the
- * "old" neighbor (as these are two different MACIP routes).
- * Do the delete only if the MAC matches.
- */
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
- && (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) {
- zvni_neigh_uninstall(zvni, n);
- zvni_neigh_del(zvni, n);
- zvni_deref_ip2mac(zvni, mac);
- }
- } else {
- /* DAD: when MAC is freeze state as remote learn event,
- * remote mac-ip delete event is received will result in freeze
- * entry removal, first fetch kernel for the same entry present
- * as LOCAL and reachable, avoid deleting this entry instead
- * use kerenel local entry to update during unfreeze time.
- */
- if (zvrf->dad_freeze &&
- CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) &&
- CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry",
- __func__,
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- mac->flags);
- macfdb_read_specific_mac(zns, zif->brslave_info.br_if,
- macaddr, vxl->access_vlan);
- }
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- zvni_process_neigh_on_remote_mac_del(zvni, mac);
- /*
- * the remote sequence number in the auto mac entry
- * needs to be reset to 0 as the mac entry may have
- * been removed on all VTEPs (including
- * the originating one)
- */
- mac->rem_seq = 0;
-
- /* If all remote neighbors referencing a remote MAC
- * go away, we need to uninstall the MAC.
- */
- if (remote_neigh_count(mac) == 0) {
- zvni_mac_uninstall(zvni, mac);
- UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- }
- if (list_isempty(mac->neigh_list))
- zvni_mac_del(zvni, mac);
- else
- SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- }
- }
-}
-
-
/* Public functions */
int is_l3vni_for_prefix_routes_only(vni_t vni)
@@ -6526,22 +2397,22 @@ void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf,
void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_neigh;
struct neigh_walk_ctx wctx;
json_object *json = NULL;
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_neigh = hashcount(zvni->neigh_table);
+ num_neigh = hashcount(zevpn->neigh_table);
if (!num_neigh)
return;
@@ -6553,22 +2424,22 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
* the maximum width.
*/
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
wctx.json = json;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width,
+ &wctx);
if (!use_json) {
vty_out(vty,
"Number of ARPs (local and remote) known for this VNI: %u\n",
num_neigh);
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width,
- "IP", "Type", "State", "MAC", "Remote VTEP", "Seq #'s");
+ zebra_evpn_print_neigh_hdr(vty, &wctx);
} else
json_object_int_add(json, "numArpNd", num_neigh);
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
json, JSON_C_TO_STRING_PRETTY));
@@ -6595,9 +2466,9 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
args[1] = json;
args[2] = (void *)(ptrdiff_t)print_dup;
- hash_iterate(zvrf->vni_table,
+ hash_iterate(zvrf->evpn_table,
(void (*)(struct hash_bucket *,
- void *))zvni_print_neigh_hash_all_vni,
+ void *))zevpn_print_neigh_hash_all_evpn,
args);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6626,9 +2497,9 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty,
args[1] = json;
args[2] = (void *)(ptrdiff_t)print_dup;
- hash_iterate(zvrf->vni_table,
+ hash_iterate(zvrf->evpn_table,
(void (*)(struct hash_bucket *,
- void *))zvni_print_neigh_hash_all_vni_detail,
+ void *))zevpn_print_neigh_hash_all_evpn_detail,
args);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6644,21 +2515,21 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip, bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_neigh_t *n;
json_object *json = NULL;
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- n = zvni_neigh_lookup(zvni, ip);
+ n = zebra_evpn_neigh_lookup(zevpn, ip);
if (!n) {
if (!use_json)
vty_out(vty,
@@ -6669,7 +2540,7 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
if (use_json)
json = json_object_new_object();
- zvni_print_neigh(n, vty, json);
+ zebra_evpn_print_neigh(n, vty, json);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6686,22 +2557,22 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct in_addr vtep_ip,
bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_neigh;
struct neigh_walk_ctx wctx;
json_object *json = NULL;
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_neigh = hashcount(zvni->neigh_table);
+ num_neigh = hashcount(zevpn->neigh_table);
if (!num_neigh)
return;
@@ -6709,14 +2580,15 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
json = json_object_new_object();
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
wctx.json = json;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width,
+ &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6734,7 +2606,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
vni_t vni,
bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_neigh;
struct neigh_walk_ctx wctx;
json_object *json = NULL;
@@ -6742,17 +2614,17 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_neigh = hashcount(zvni->neigh_table);
+ num_neigh = hashcount(zevpn->neigh_table);
if (!num_neigh)
return;
- num_neigh = num_dup_detected_neighs(zvni);
+ num_neigh = num_dup_detected_neighs(zevpn);
if (!num_neigh)
return;
@@ -6764,23 +2636,25 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
* the maximum width.
*/
memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.addr_width = 15;
wctx.json = json;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width,
+ &wctx);
if (!use_json) {
vty_out(vty,
"Number of ARPs (local and remote) known for this VNI: %u\n",
num_neigh);
- vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n",
+ vty_out(vty, "%*s %-6s %-8s %-17s %-30s\n",
-wctx.addr_width, "IP", "Type",
- "State", "MAC", "Remote VTEP");
+ "State", "MAC", "Remote ES/VTEP");
} else
json_object_int_add(json, "numArpNd", num_neigh);
- hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx);
+ hash_iterate(zevpn->neigh_table, zebra_evpn_print_dad_neigh_hash,
+ &wctx);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6795,7 +2669,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_macs;
struct mac_walk_ctx wctx;
json_object *json = NULL;
@@ -6803,15 +2677,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_macs = num_valid_macs(zvni);
+ num_macs = num_valid_macs(zevpn);
if (!num_macs)
return;
@@ -6821,7 +2695,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
}
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.json = json_mac;
@@ -6829,12 +2703,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
vty_out(vty,
"Number of MACs (local and remote) known for this VNI: %u\n",
num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN", "Seq #'s");
+ vty_out(vty,
+ "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n");
+ vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC",
+ "Type", "Flags", "Intf/Remote ES/VTEP",
+ "VLAN", "Seq #'s");
} else
json_object_int_add(json, "numMacs", num_macs);
- hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx);
if (use_json) {
json_object_object_add(json, "macs", json_mac);
@@ -6865,7 +2742,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
wctx.vty = vty;
wctx.json = json;
wctx.print_dup = print_dup;
- hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+ hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6896,7 +2773,7 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty,
wctx.vty = vty;
wctx.json = json;
wctx.print_dup = print_dup;
- hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni_detail,
+ hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn_detail,
&wctx);
if (use_json) {
@@ -6927,7 +2804,7 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
wctx.json = json;
- hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+ hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
@@ -6943,22 +2820,22 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct ethaddr *macaddr,
bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_mac_t *mac;
json_object *json = NULL;
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- mac = zvni_mac_lookup(zvni, macaddr);
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!mac) {
if (use_json)
vty_out(vty, "{}\n");
@@ -6972,7 +2849,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
if (use_json)
json = json_object_new_object();
- zvni_print_mac(mac, vty, json);
+ zebra_evpn_print_mac(mac, vty, json);
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
json, JSON_C_TO_STRING_PRETTY));
@@ -6985,7 +2862,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
struct zebra_vrf *zvrf,
vni_t vni, bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
struct mac_walk_ctx wctx;
uint32_t num_macs;
json_object *json = NULL;
@@ -6994,17 +2871,17 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_macs = num_valid_macs(zvni);
+ num_macs = num_valid_macs(zevpn);
if (!num_macs)
return;
- num_macs = num_dup_detected_macs(zvni);
+ num_macs = num_dup_detected_macs(zevpn);
if (!num_macs)
return;
@@ -7014,7 +2891,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
}
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.json = json_mac;
@@ -7022,12 +2899,12 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
vty_out(vty,
"Number of MACs (local and remote) known for this VNI: %u\n",
num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+ vty_out(vty, "%-17s %-6s %-5s %-30s %-5s\n", "MAC", "Type",
+ "Flags", "Intf/Remote ES/VTEP", "VLAN");
} else
json_object_int_add(json, "numMacs", num_macs);
- hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, &wctx);
if (use_json) {
json_object_object_add(json, "macs", json_mac);
@@ -7041,7 +2918,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty,
int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_mac_t *mac;
struct listnode *node = NULL;
zebra_neigh_t *nbr = NULL;
@@ -7049,13 +2926,13 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni,
if (!is_evpn_enabled())
return 0;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_warn("VNI %u does not exist\n", vni);
return -1;
}
- mac = zvni_mac_lookup(zvni, macaddr);
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!mac) {
zlog_warn("Requested MAC does not exist in VNI %u\n", vni);
return -1;
@@ -7080,7 +2957,8 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni,
if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
ZEBRA_NEIGH_SET_INACTIVE(nbr);
else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE))
- zvni_neigh_install(zvni, nbr);
+ zebra_evpn_rem_neigh_install(
+ zevpn, nbr, false /*was_static*/);
}
UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
@@ -7103,20 +2981,20 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni,
/* Local: Notify Peer VTEPs, Remote: Install the entry */
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
/* Inform to BGP */
- if (zvni_mac_send_add_to_client(zvni->vni,
- &mac->macaddr,
- mac->flags,
- mac->loc_seq))
+ if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr,
+ mac->flags, mac->loc_seq,
+ mac->es))
return 0;
/* Process all neighbors associated with this MAC. */
- zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+ zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0,
+ 0 /*es_change*/);
} else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- zvni_process_neigh_on_remote_mac_add(zvni, mac);
+ zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
}
return 0;
@@ -7125,7 +3003,7 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni,
int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_neigh_t *nbr;
zebra_mac_t *mac;
char buf[INET6_ADDRSTRLEN];
@@ -7134,13 +3012,13 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni,
if (!is_evpn_enabled())
return 0;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_debug("VNI %u does not exist\n", vni);
return -1;
}
- nbr = zvni_neigh_lookup(zvni, ip);
+ nbr = zebra_evpn_neigh_lookup(zevpn, ip);
if (!nbr) {
zlog_warn("Requested host IP does not exist in VNI %u\n", vni);
return -1;
@@ -7154,7 +3032,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni,
return -1;
}
- mac = zvni_mac_lookup(zvni, &nbr->emac);
+ mac = zebra_evpn_mac_lookup(zevpn, &nbr->emac);
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
zlog_warn(
@@ -7175,21 +3053,21 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni,
THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
- zvni_neigh_send_add_to_client(zvni->vni, ip,
- &nbr->emac,
- nbr->flags, nbr->loc_seq);
+ zebra_evpn_neigh_send_add_to_client(zevpn->vni, ip, &nbr->emac,
+ nbr->mac, nbr->flags,
+ nbr->loc_seq);
} else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
+ zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/);
}
return 0;
}
-static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt)
+static void zevpn_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt)
{
struct mac_walk_ctx *wctx = ctxt;
zebra_mac_t *mac;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
struct listnode *node = NULL;
zebra_neigh_t *nbr = NULL;
@@ -7197,7 +3075,7 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt)
if (!mac)
return;
- zvni = wctx->zvni;
+ zevpn = wctx->zevpn;
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE))
return;
@@ -7224,87 +3102,50 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt)
/* Local: Notify Peer VTEPs, Remote: Install the entry */
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
/* Inform to BGP */
- if (zvni_mac_send_add_to_client(zvni->vni,
- &mac->macaddr,
- mac->flags, mac->loc_seq))
+ if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr,
+ mac->flags, mac->loc_seq,
+ mac->es))
return;
/* Process all neighbors associated with this MAC. */
- zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
+ zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0,
+ 0 /*es_change*/);
} else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- zvni_process_neigh_on_remote_mac_add(zvni, mac);
+ zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
}
}
-static void zvni_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt)
-{
- struct neigh_walk_ctx *wctx = ctxt;
- zebra_neigh_t *nbr;
- zebra_vni_t *zvni;
- char buf[INET6_ADDRSTRLEN];
-
- nbr = (zebra_neigh_t *)bucket->data;
- if (!nbr)
- return;
-
- zvni = wctx->zvni;
-
- if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE))
- return;
-
- if (IS_ZEBRA_DEBUG_VXLAN) {
- ipaddr2str(&nbr->ip, buf, sizeof(buf));
- zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u",
- __func__, buf, nbr->flags, nbr->loc_seq);
- }
-
- UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
- nbr->dad_count = 0;
- nbr->detect_start_time.tv_sec = 0;
- nbr->detect_start_time.tv_usec = 0;
- nbr->dad_dup_detect_time = 0;
- THREAD_OFF(nbr->dad_ip_auto_recovery_timer);
-
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
- zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip,
- &nbr->emac,
- nbr->flags, nbr->loc_seq);
- } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
- }
-}
-
-static void zvni_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket,
+static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket,
void **args)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
struct zebra_vrf *zvrf;
struct mac_walk_ctx m_wctx;
struct neigh_walk_ctx n_wctx;
- zvni = (zebra_vni_t *)bucket->data;
- if (!zvni)
+ zevpn = (zebra_evpn_t *)bucket->data;
+ if (!zevpn)
return;
zvrf = (struct zebra_vrf *)args[0];
- if (hashcount(zvni->neigh_table)) {
+ if (hashcount(zevpn->neigh_table)) {
memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
+ n_wctx.zevpn = zevpn;
n_wctx.zvrf = zvrf;
- hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash,
- &n_wctx);
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_clear_dup_neigh_hash, &n_wctx);
}
- if (num_valid_macs(zvni)) {
+ if (num_valid_macs(zevpn)) {
memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
+ m_wctx.zevpn = zevpn;
m_wctx.zvrf = zvrf;
- hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx);
+ hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx);
}
}
@@ -7318,41 +3159,41 @@ int zebra_vxlan_clear_dup_detect_vni_all(struct zebra_vrf *zvrf)
args[0] = zvrf;
- hash_iterate(zvrf->vni_table,
+ hash_iterate(zvrf->evpn_table,
(void (*)(struct hash_bucket *, void *))
- zvni_clear_dup_detect_hash_vni_all, args);
+ zevpn_clear_dup_detect_hash_vni_all, args);
return 0;
}
int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
struct mac_walk_ctx m_wctx;
struct neigh_walk_ctx n_wctx;
if (!is_evpn_enabled())
return 0;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_warn("VNI %u does not exist\n", vni);
- return -1;
+ return CMD_WARNING;
}
- if (hashcount(zvni->neigh_table)) {
+ if (hashcount(zevpn->neigh_table)) {
memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
+ n_wctx.zevpn = zevpn;
n_wctx.zvrf = zvrf;
- hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash,
- &n_wctx);
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_clear_dup_neigh_hash, &n_wctx);
}
- if (num_valid_macs(zvni)) {
+ if (num_valid_macs(zevpn)) {
memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
+ m_wctx.zevpn = zevpn;
m_wctx.zvrf = zvrf;
- hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx);
+ hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx);
}
return 0;
@@ -7365,7 +3206,7 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, struct in_addr vtep_ip,
bool use_json)
{
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
uint32_t num_macs;
struct mac_walk_ctx wctx;
json_object *json = NULL;
@@ -7373,15 +3214,15 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
if (!is_evpn_enabled())
return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (use_json)
vty_out(vty, "{}\n");
else
vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_macs = num_valid_macs(zvni);
+ num_macs = num_valid_macs(zevpn);
if (!num_macs)
return;
@@ -7391,12 +3232,12 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
}
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
- wctx.zvni = zvni;
+ wctx.zevpn = zevpn;
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
wctx.json = json_mac;
- hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+ hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx);
if (use_json) {
json_object_int_add(json, "numMacs", wctx.count);
@@ -7423,7 +3264,7 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
json_object *json = NULL;
void *args[2];
zebra_l3vni_t *zl3vni = NULL;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
if (!is_evpn_enabled())
return;
@@ -7438,9 +3279,9 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
if (zl3vni) {
zl3vni_print(zl3vni, (void *)args);
} else {
- zvni = zvni_lookup(vni);
- if (zvni)
- zvni_print(zvni, (void *)args);
+ zevpn = zebra_evpn_lookup(vni);
+ if (zevpn)
+ zebra_evpn_print(zevpn, (void *)args);
else if (!json)
vty_out(vty, "%% VNI %u does not exist\n", vni);
}
@@ -7478,7 +3319,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
return;
num_l3vnis = hashcount(zrouter.l3vni_table);
- num_l2vnis = hashcount(zvrf->vni_table);
+ num_l2vnis = hashcount(zvrf->evpn_table);
num_vnis = num_l2vnis + num_l3vnis;
if (uj) {
@@ -7550,9 +3391,10 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
args[1] = json;
/* Display all L2-VNIs */
- hash_iterate(zvrf->vni_table,
- (void (*)(struct hash_bucket *, void *))zvni_print_hash,
- args);
+ hash_iterate(
+ zvrf->evpn_table,
+ (void (*)(struct hash_bucket *, void *))zebra_evpn_print_hash,
+ args);
/* Display all L3-VNIs */
hash_iterate(zrouter.l3vni_table,
@@ -7616,7 +3458,7 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf,
{
json_object *json_array = NULL;
struct zebra_ns *zns = NULL;
- struct zvni_evpn_show zes;
+ struct zebra_evpn_show zes;
if (!is_evpn_enabled())
return;
@@ -7634,10 +3476,10 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf,
zes.use_json = use_json;
/* Display all L2-VNIs */
- hash_iterate(
- zvrf->vni_table,
- (void (*)(struct hash_bucket *, void *))zvni_print_hash_detail,
- &zes);
+ hash_iterate(zvrf->evpn_table,
+ (void (*)(struct hash_bucket *,
+ void *))zebra_evpn_print_hash_detail,
+ &zes);
/* Display all L3-VNIs */
hash_iterate(zrouter.l3vni_table,
@@ -7664,12 +3506,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
struct ipaddr *ip)
{
char buf[INET6_ADDRSTRLEN];
- char buf2[ETHER_ADDR_STRLEN];
- zebra_neigh_t *n = NULL;
- zebra_vni_t *zvni = NULL;
- zebra_mac_t *zmac = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
- struct zebra_vrf *zvrf;
/* check if this is a remote neigh entry corresponding to remote
* next-hop
@@ -7681,80 +3519,29 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_from_svi(ifp, link_if);
- if (!zvni) {
+ zevpn = zebra_evpn_from_svi(ifp, link_if);
+ if (!zevpn) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: Del neighbor %s VNI is not present for interface %s",
+ "%s: Del neighbor %s EVPN is not present for interface %s",
__func__, ipaddr2str(ip, buf, sizeof(buf)),
ifp->name);
return 0;
}
- if (!zvni->vxlan_if) {
+ if (!zevpn->vxlan_if) {
zlog_debug(
"VNI %u hash %p doesn't have intf upon local neighbor DEL",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u",
ipaddr2str(ip, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, zvni->vni);
-
- /* If entry doesn't exist, nothing to do. */
- n = zvni_neigh_lookup(zvni, ip);
- if (!n)
- return 0;
+ ifp->ifindex, zevpn->vni);
- zmac = zvni_mac_lookup(zvni, &n->emac);
- if (!zmac) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Trying to del a neigh %s without a mac %s on VNI %u",
- ipaddr2str(ip, buf, sizeof(buf)),
- prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
- zvni->vni);
-
- return 0;
- }
-
- /* If it is a remote entry, the kernel has aged this out or someone has
- * deleted it, it needs to be re-installed as Quagga is the owner.
- */
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, n);
- return 0;
- }
-
- zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
- if (!zvrf) {
- zlog_debug("%s: VNI %u vrf lookup failed.", __func__,
- zvni->vni);
- return -1;
- }
-
- /* In case of feeze action, if local neigh is in duplicate state,
- * Mark the Neigh as inactive before sending delete request to BGPd,
- * If BGPd has remote entry, it will re-install
- */
- if (zvrf->dad_freeze &&
- CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE))
- ZEBRA_NEIGH_SET_INACTIVE(n);
-
- /* Remove neighbor from BGP. */
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, 0, n->state);
-
- /* Delete this neighbor entry. */
- zvni_neigh_del(zvni, n);
-
- /* see if the AUTO mac needs to be deleted */
- if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO)
- && !listcount(zmac->neigh_list))
- zvni_mac_del(zvni, zmac);
-
- return 0;
+ return zebra_evpn_neigh_del_ip(zevpn, ip);
}
/*
@@ -7769,11 +3556,12 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
struct ethaddr *macaddr,
uint16_t state,
bool is_ext,
- bool is_router)
+ bool is_router,
+ bool local_inactive, bool dp_static)
{
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
/* check if this is a remote neigh entry corresponding to remote
@@ -7786,32 +3574,34 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_from_svi(ifp, link_if);
- if (!zvni)
+ zevpn = zebra_evpn_from_svi(ifp, link_if);
+ if (!zevpn)
return 0;
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s %s-> L2-VNI %u",
+ "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s%s%s-> L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, state, is_ext ? "ext-learned " : "",
is_router ? "router " : "",
- zvni->vni);
+ local_inactive ? "local_inactive " : "",
+ zevpn->vni);
/* Is this about a local neighbor or a remote one? */
if (!is_ext)
- return zvni_local_neigh_update(zvni, ifp, ip, macaddr,
- is_router);
+ return zebra_evpn_local_neigh_update(zevpn, ifp, ip, macaddr,
+ is_router, local_inactive,
+ dp_static);
- return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state);
+ return zebra_evpn_remote_neigh_update(zevpn, ifp, ip, macaddr, state);
}
static int32_t
zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni,
struct ethaddr *macaddr, uint16_t *ipa_len,
struct ipaddr *ip, struct in_addr *vtep_ip,
- uint8_t *flags, uint32_t *seq)
+ uint8_t *flags, uint32_t *seq, esi_t *esi)
{
uint16_t l = 0;
@@ -7849,6 +3639,8 @@ zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni,
STREAM_GETC(s, *flags);
STREAM_GETL(s, *seq);
l += 5;
+ STREAM_GET(esi, s, sizeof(esi_t));
+ l += sizeof(esi_t);
}
return l;
@@ -7880,7 +3672,7 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
while (l < hdr->length) {
int res_length = zebra_vxlan_remote_macip_helper(
false, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, NULL,
- NULL);
+ NULL, NULL);
if (res_length == -1)
goto stream_failure;
@@ -7921,6 +3713,8 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
uint32_t seq;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
+ esi_t esi;
+ char esi_buf[ESI_STR_LEN];
memset(&macaddr, 0, sizeof(struct ethaddr));
memset(&ip, 0, sizeof(struct ipaddr));
@@ -7936,25 +3730,32 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
while (l < hdr->length) {
int res_length = zebra_vxlan_remote_macip_helper(
true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip,
- &flags, &seq);
+ &flags, &seq, &esi);
if (res_length == -1)
goto stream_failure;
l += res_length;
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ if (memcmp(&esi, zero_esi, sizeof(esi_t)))
+ esi_to_str(&esi, esi_buf, sizeof(esi_buf));
+ else
+ strlcpy(esi_buf, "-", ESI_STR_LEN);
zlog_debug(
- "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s",
+ "Recv %sMACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s ESI %s from %s",
+ (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) ?
+ "sync-" : "",
vni,
prefix_mac2str(&macaddr, buf, sizeof(buf)),
ipa_len ? " IP " : "",
ipa_len ?
ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
- flags, seq, inet_ntoa(vtep_ip),
+ flags, seq, inet_ntoa(vtep_ip), esi_buf,
zebra_route_string(client->proto));
+ }
process_remote_macip_add(vni, &macaddr, ipa_len, &ip,
- flags, seq, vtep_ip);
+ flags, seq, vtep_ip, &esi);
}
stream_failure:
@@ -7971,7 +3772,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
struct zebra_vrf *zvrf = NULL;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_vtep_t *zvtep = NULL;
zif = ifp->info;
@@ -7989,12 +3790,12 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
return -1;
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return 0;
/* If the remote vtep entry doesn't exists nothing to do */
- zvtep = zvni_vtep_find(zvni, &vtep_ip);
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
if (!zvtep)
return 0;
@@ -8003,7 +3804,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp,
"Del MAC for remote VTEP %s intf %s(%u) VNI %u - readd",
inet_ntoa(vtep_ip), ifp->name, ifp->ifindex, vni);
- zvni_vtep_install(zvni, zvtep);
+ zebra_evpn_vtep_install(zevpn, zvtep);
return 0;
}
@@ -8019,7 +3820,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
vni_t vni;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
@@ -8033,12 +3834,12 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
return 0;
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return 0;
/* If entry doesn't exist, nothing to do. */
- mac = zvni_mac_lookup(zvni, macaddr);
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!mac)
return 0;
@@ -8053,16 +3854,17 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
ifp->ifindex, vni, mac->flags);
/* Remove MAC from BGP. */
- zvni_mac_send_del_to_client(zvni->vni, macaddr);
+ zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags,
+ false /* force */);
/*
* If there are no neigh associated with the mac delete the mac
* else mark it as AUTO for forward reference
*/
if (!listcount(mac->neigh_list)) {
- zvni_mac_del(zvni, mac);
+ zebra_evpn_mac_del(zevpn, mac);
} else {
- UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS);
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
}
@@ -8082,7 +3884,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
vni_t vni;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
@@ -8102,12 +3904,12 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
return zebra_vxlan_readd_remote_rmac(zl3vni, macaddr);
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return 0;
/* If entry doesn't exist, nothing to do. */
- mac = zvni_mac_lookup(zvni, macaddr);
+ mac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!mac)
return 0;
@@ -8120,7 +3922,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, vni);
- zvni_mac_install(zvni, mac);
+ zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */);
return 0;
}
@@ -8130,57 +3932,22 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp,
int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid)
{
- zebra_vni_t *zvni;
- zebra_mac_t *mac;
- char buf[ETHER_ADDR_STRLEN];
+ zebra_evpn_t *zevpn;
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
*/
- zvni = zvni_map_vlan(ifp, br_if, vid);
- if (!zvni)
+ zevpn = zebra_evpn_map_vlan(ifp, br_if, vid);
+ if (!zevpn)
return 0;
- if (!zvni->vxlan_if) {
+ if (!zevpn->vxlan_if) {
zlog_debug(
"VNI %u hash %p doesn't have intf upon local MAC DEL",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
return -1;
}
- /* If entry doesn't exist, nothing to do. */
- mac = zvni_mac_lookup(zvni, macaddr);
- if (!mac)
- return 0;
-
- /* Is it a local entry? */
- if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
- return 0;
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, vid, zvni->vni, mac->loc_seq,
- mac->flags, listcount(mac->neigh_list));
-
- /* Update all the neigh entries associated with this mac */
- zvni_process_neigh_on_local_mac_del(zvni, mac);
-
- /* Remove MAC from BGP. */
- zvni_mac_send_del_to_client(zvni->vni, macaddr);
-
- /*
- * If there are no neigh associated with the mac delete the mac
- * else mark it as AUTO for forward reference
- */
- if (!listcount(mac->neigh_list)) {
- zvni_mac_del(zvni, mac);
- } else {
- UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
- SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- }
-
- return 0;
+ return zebra_evpn_del_local_mac(zevpn, macaddr, ifp);
}
/*
@@ -8189,201 +3956,52 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
int zebra_vxlan_local_mac_add_update(struct interface *ifp,
struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid,
- bool sticky)
+ bool sticky, bool local_inactive,
+ bool dp_static)
{
- zebra_vni_t *zvni;
- zebra_mac_t *mac;
+ zebra_evpn_t *zevpn;
struct zebra_vrf *zvrf;
char buf[ETHER_ADDR_STRLEN];
- bool mac_sticky = false;
- bool inform_client = false;
- bool upd_neigh = false;
- bool is_dup_detect = false;
- struct in_addr vtep_ip = {.s_addr = 0};
- ns_id_t local_ns_id = NS_DEFAULT;
- zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
- if (zvrf && zvrf->zns)
- local_ns_id = zvrf->zns->ns_id;
+ assert(ifp);
/* We are interested in MACs only on ports or (port, VLAN) that
- * map to a VNI.
+ * map to an EVPN.
*/
- zvni = zvni_map_vlan(ifp, br_if, vid);
- if (!zvni) {
+ zevpn = zebra_evpn_map_vlan(ifp, br_if, vid);
+ if (!zevpn) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- " Add/Update %sMAC %s intf %s(%u) VID %u, could not find VNI",
+ " Add/Update %sMAC %s intf %s(%u) VID %u, could not find EVPN",
sticky ? "sticky " : "",
prefix_mac2str(macaddr, buf, sizeof(buf)),
ifp->name, ifp->ifindex, vid);
return 0;
}
- if (!zvni->vxlan_if) {
+ if (!zevpn->vxlan_if) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
" VNI %u hash %p doesn't have intf upon local MAC ADD",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
return -1;
}
- zvrf = zebra_vrf_get_evpn();
+ zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
if (!zvrf) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(" No Evpn Global Vrf found");
+ zlog_debug(" No Vrf found for vrf_id: %d",
+ zevpn->vxlan_if->vrf_id);
return -1;
}
- /* Check if we need to create or update or it is a NO-OP. */
- mac = zvni_mac_lookup(zvni, macaddr);
- if (!mac) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vid, zvni->vni);
-
- mac = zvni_mac_add(zvni, macaddr);
- if (!mac) {
- flog_err(
- EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add MAC %s intf %s(%u) VID %u VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vid, zvni->vni);
- return -1;
- }
- SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.ns_id = local_ns_id;
- mac->fwd_info.local.vid = vid;
- if (sticky)
- SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
- inform_client = true;
-
- } else {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u curFlags 0x%x",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vid, zvni->vni,
- mac->flags);
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
- mac_sticky = true;
-
- /*
- * Update any changes and if changes are relevant to
- * BGP, note it.
- */
- if (mac_sticky == sticky
- && mac->fwd_info.local.ifindex == ifp->ifindex
- && mac->fwd_info.local.ns_id == local_ns_id
- && mac->fwd_info.local.vid == vid) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u, "
- "entry exists and has not changed ",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- ifp->name, ifp->ifindex, vid,
- zvni->vni);
- return 0;
- }
- if (mac_sticky != sticky) {
- if (sticky)
- SET_FLAG(mac->flags,
- ZEBRA_MAC_STICKY);
- else
- UNSET_FLAG(mac->flags,
- ZEBRA_MAC_STICKY);
- inform_client = true;
- }
-
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.ns_id = local_ns_id;
- mac->fwd_info.local.vid = vid;
-
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) ||
- CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
- bool do_dad = false;
-
- /*
- * MAC has either moved or was "internally" created due
- * to a neighbor learn and is now actually learnt. If
- * it was learnt as a remote sticky MAC, this is an
- * operator error.
- */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
- flog_warn(
- EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT,
- "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- inet_ntoa(mac->fwd_info.r_vtep_ip),
- zvni->vni);
- return 0;
- }
-
- /* If an actual move, compute MAC's seq number */
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- mac->loc_seq = MAX(mac->rem_seq + 1,
- mac->loc_seq);
- vtep_ip = mac->fwd_info.r_vtep_ip;
- /* Trigger DAD for remote MAC */
- do_dad = true;
- }
-
- UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
- SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.ns_id = local_ns_id;
- mac->fwd_info.local.vid = vid;
- if (sticky)
- SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
- else
- UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
- /*
- * We have to inform BGP of this MAC as well as process
- * all neighbors.
- */
- inform_client = true;
- upd_neigh = true;
-
- zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip,
- do_dad,
- &is_dup_detect,
- true);
- if (is_dup_detect) {
- inform_client = false;
- upd_neigh = false;
- }
- }
- }
-
- /* Inform BGP if required. */
- if (inform_client) {
- if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
- mac->flags, mac->loc_seq))
- return -1;
- }
-
- /* Process all neighbors associated with this MAC, if required. */
- if (upd_neigh)
- zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
-
- return 0;
+ return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid,
+ sticky, local_inactive,
+ dp_static);
}
/*
- * Handle message from client to delete a remote VTEP for a VNI.
+ * Handle message from client to delete a remote VTEP for an EVPN.
*/
void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
{
@@ -8391,7 +4009,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
unsigned short l = 0;
vni_t vni;
struct in_addr vtep_ip;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_vtep_t *zvtep;
struct interface *ifp;
struct zebra_if *zif;
@@ -8430,21 +4048,20 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
zebra_route_string(client->proto));
/* Locate VNI hash entry - expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Failed to locate VNI hash upon remote VTEP DEL, "
- "VNI %u",
+ "Failed to locate VNI hash upon remote VTEP DEL, VNI %u",
vni);
continue;
}
- ifp = zvni->vxlan_if;
+ ifp = zevpn->vxlan_if;
if (!ifp) {
zlog_debug(
"VNI %u hash %p doesn't have intf upon remote VTEP DEL",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
continue;
}
zif = ifp->info;
@@ -8459,12 +4076,12 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS)
* and
* then, the VTEP entry itself and remove it.
*/
- zvtep = zvni_vtep_find(zvni, &vtep_ip);
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
if (!zvtep)
continue;
- zvni_vtep_uninstall(zvni, &vtep_ip);
- zvni_vtep_del(zvni, zvtep);
+ zebra_evpn_vtep_uninstall(zevpn, &vtep_ip);
+ zebra_evpn_vtep_del(zevpn, zvtep);
}
stream_failure:
@@ -8472,7 +4089,7 @@ stream_failure:
}
/*
- * Handle message from client to add a remote VTEP for a VNI.
+ * Handle message from client to add a remote VTEP for an EVPN.
*/
void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
{
@@ -8480,7 +4097,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
unsigned short l = 0;
vni_t vni;
struct in_addr vtep_ip;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
struct interface *ifp;
struct zebra_if *zif;
int flood_control;
@@ -8515,21 +4132,21 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
zebra_route_string(client->proto));
/* Locate VNI hash entry - expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
flog_err(
EC_ZEBRA_VTEP_ADD_FAILED,
- "Failed to locate VNI hash upon remote VTEP ADD, VNI %u",
+ "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u",
vni);
continue;
}
- ifp = zvni->vxlan_if;
+ ifp = zevpn->vxlan_if;
if (!ifp) {
flog_err(
EC_ZEBRA_VTEP_ADD_FAILED,
"VNI %u hash %p doesn't have intf upon remote VTEP ADD",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
continue;
}
@@ -8539,7 +4156,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
continue;
- zvtep = zvni_vtep_find(zvni, &vtep_ip);
+ zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip);
if (zvtep) {
/* If the remote VTEP already exists check if
* the flood mode has changed
@@ -8551,18 +4168,20 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS)
* is no longer; get rid of the HER fdb
* entry installed before
*/
- zvni_vtep_uninstall(zvni, &vtep_ip);
+ zebra_evpn_vtep_uninstall(zevpn,
+ &vtep_ip);
zvtep->flood_control = flood_control;
- zvni_vtep_install(zvni, zvtep);
+ zebra_evpn_vtep_install(zevpn, zvtep);
}
} else {
- zvtep = zvni_vtep_add(zvni, &vtep_ip, flood_control);
+ zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip,
+ flood_control);
if (zvtep)
- zvni_vtep_install(zvni, zvtep);
+ zebra_evpn_vtep_install(zevpn, zvtep);
else
flog_err(EC_ZEBRA_VTEP_ADD_FAILED,
- "Failed to add remote VTEP, VNI %u zvni %p",
- vni, zvni);
+ "Failed to add remote VTEP, VNI %u zevpn %p",
+ vni, zevpn);
}
}
@@ -8583,7 +4202,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
{
struct ipaddr ip;
struct ethaddr macaddr;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
memset(&ip, 0, sizeof(struct ipaddr));
memset(&macaddr, 0, sizeof(struct ethaddr));
@@ -8627,14 +4246,15 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT),
svi_if_zif->link_ifindex);
- zvni = zvni_from_svi(svi_if, svi_if_link);
+ zevpn = zebra_evpn_from_svi(svi_if,
+ svi_if_link);
}
} else if (IS_ZEBRA_IF_BRIDGE(svi_if)) {
/*
* If it is a vlan unaware bridge then svi is the bridge
* itself
*/
- zvni = zvni_from_svi(svi_if, svi_if);
+ zevpn = zebra_evpn_from_svi(svi_if, svi_if);
}
} else if (IS_ZEBRA_IF_VLAN(ifp)) {
struct zebra_if *svi_if_zif =
@@ -8648,18 +4268,18 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
zebra_ns_lookup(NS_DEFAULT),
svi_if_zif->link_ifindex);
if (svi_if_link)
- zvni = zvni_from_svi(ifp, svi_if_link);
+ zevpn = zebra_evpn_from_svi(ifp, svi_if_link);
}
} else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
- zvni = zvni_from_svi(ifp, ifp);
+ zevpn = zebra_evpn_from_svi(ifp, ifp);
}
- if (!zvni)
+ if (!zevpn)
return 0;
- if (!zvni->vxlan_if) {
+ if (!zevpn->vxlan_if) {
zlog_debug("VNI %u hash %p doesn't have intf upon MACVLAN up",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
return -1;
}
@@ -8678,9 +4298,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
if (add)
- zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
+ zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip);
else
- zvni_gw_macip_del(ifp, zvni, &ip);
+ zebra_evpn_gw_macip_del(ifp, zevpn, &ip);
return 0;
}
@@ -8690,7 +4310,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
* SVI can be associated to either L3-VNI or L2-VNI.
* For L2-VNI: At this point, this is a NOP since
* the kernel deletes the neighbor entries on this SVI (if any).
- * We only need to update the vrf corresponding to zvni.
+ * We only need to update the vrf corresponding to zevpn.
* For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete
* from bgp
*/
@@ -8707,17 +4327,17 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
/* remove association with svi-if */
zl3vni->svi_if = NULL;
} else {
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
- /* since we dont have svi corresponding to zvni, we associate it
+ /* since we dont have svi corresponding to zevpn, we associate it
* to default vrf. Note: the corresponding neigh entries on the
* SVI would have already been deleted */
- zvni = zvni_from_svi(ifp, link_if);
- if (zvni) {
- zvni->vrf_id = VRF_DEFAULT;
+ zevpn = zebra_evpn_from_svi(ifp, link_if);
+ if (zevpn) {
+ zevpn->vrf_id = VRF_DEFAULT;
/* update the tenant vrf in BGP */
- zvni_send_add_to_client(zvni);
+ zebra_evpn_send_add_to_client(zevpn);
}
}
return 0;
@@ -8733,7 +4353,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
*/
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
zl3vni = zl3vni_from_svi(ifp, link_if);
@@ -8750,31 +4370,31 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
/* process SVI up for l2-vni */
struct neigh_walk_ctx n_wctx;
- zvni = zvni_from_svi(ifp, link_if);
- if (!zvni)
+ zevpn = zebra_evpn_from_svi(ifp, link_if);
+ if (!zevpn)
return 0;
- if (!zvni->vxlan_if) {
+ if (!zevpn->vxlan_if) {
zlog_debug(
"VNI %u hash %p doesn't have intf upon SVI up",
- zvni->vni, zvni);
+ zevpn->vni, zevpn);
return -1;
}
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"SVI %s(%u) VNI %u VRF %s is UP, installing neighbors",
- ifp->name, ifp->ifindex, zvni->vni,
+ ifp->name, ifp->ifindex, zevpn->vni,
vrf_id_to_name(ifp->vrf_id));
/* update the vrf information for l2-vni and inform bgp */
- zvni->vrf_id = ifp->vrf_id;
- zvni_send_add_to_client(zvni);
+ zevpn->vrf_id = ifp->vrf_id;
+ zebra_evpn_send_add_to_client(zevpn);
/* Install any remote neighbors for this VNI. */
memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
+ n_wctx.zevpn = zevpn;
+ hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash,
&n_wctx);
}
@@ -8861,7 +4481,7 @@ int zebra_vxlan_if_down(struct interface *ifp)
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
zebra_l3vni_t *zl3vni = NULL;
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -8887,25 +4507,25 @@ int zebra_vxlan_if_down(struct interface *ifp)
ifp->ifindex, vni);
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_debug(
"Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
return -1;
}
- assert(zvni->vxlan_if == ifp);
+ assert(zevpn->vxlan_if == ifp);
/* Delete this VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ zebra_evpn_send_del_to_client(zevpn);
/* Free up all neighbors and MACs, if any. */
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
+ zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ zebra_evpn_vtep_del_all(zevpn, 1);
}
return 0;
}
@@ -8918,7 +4538,7 @@ int zebra_vxlan_if_up(struct interface *ifp)
vni_t vni;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
/* Check if EVPN is enabled. */
@@ -8956,29 +4576,29 @@ int zebra_vxlan_if_up(struct interface *ifp)
ifp->ifindex, vni);
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_debug(
- "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
+ "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
return -1;
}
- assert(zvni->vxlan_if == ifp);
+ assert(zevpn->vxlan_if == ifp);
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
- zvni->vrf_id = vlan_if->vrf_id;
+ zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
- listnode_add_sort(zl3vni->l2vnis, zvni);
+ listnode_add_sort(zl3vni->l2vnis, zevpn);
}
/* If part of a bridge, inform BGP about this VNI. */
/* Also, read and populate local MACs and neighbors. */
if (zif->brslave_info.br_if) {
- zvni_send_add_to_client(zvni);
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_evpn_send_add_to_client(zevpn);
+ zebra_evpn_read_mac_neigh(zevpn, ifp);
}
}
@@ -8994,7 +4614,7 @@ int zebra_vxlan_if_del(struct interface *ifp)
vni_t vni;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
/* Check if EVPN is enabled. */
@@ -9027,8 +4647,8 @@ int zebra_vxlan_if_del(struct interface *ifp)
ifp->ifindex);
/* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_debug(
"Failed to locate VNI hash at del, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
@@ -9036,25 +4656,24 @@ int zebra_vxlan_if_del(struct interface *ifp)
}
/* remove from l3-vni list */
- zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ zl3vni = zl3vni_from_vrf(zevpn->vrf_id);
if (zl3vni)
- listnode_delete(zl3vni->l2vnis, zvni);
-
+ listnode_delete(zl3vni->l2vnis, zevpn);
/* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ zebra_evpn_send_del_to_client(zevpn);
/* Free up all neighbors and MAC, if any. */
- zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+ zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH);
+ zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC);
/* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ zebra_evpn_vtep_del_all(zevpn, 0);
/* Delete the hash entry. */
- if (zvni_del(zvni)) {
+ if (zebra_evpn_vxlan_del(zevpn)) {
flog_err(EC_ZEBRA_VNI_DEL_FAILED,
- "Failed to del VNI hash %p, IF %s(%u) VNI %u",
- zvni, ifp->name, ifp->ifindex, zvni->vni);
+ "Failed to del EVPN hash %p, IF %s(%u) VNI %u",
+ zevpn, ifp->name, ifp->ifindex, zevpn->vni);
return -1;
}
}
@@ -9069,7 +4688,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
vni_t vni;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
/* Check if EVPN is enabled. */
@@ -9140,10 +4759,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
} else {
/* Update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
zlog_debug(
- "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u",
+ "Failed to find EVPN hash on update, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
return -1;
}
@@ -9160,10 +4779,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
&& (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
/* Delete from client, remove all remote VTEPs */
/* Also, free up all MACs and neighbors. */
- zvni_send_del_to_client(zvni->vni);
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
- zvni_vtep_del_all(zvni, 1);
+ zebra_evpn_send_del_to_client(zevpn);
+ zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
+ zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
+ zebra_evpn_vtep_del_all(zevpn, 1);
return 0;
}
@@ -9172,20 +4791,23 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
/* Remove all existing local neigh and MACs for this VNI
* (including from BGP)
*/
- zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC);
+ zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC);
}
- if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
- zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
- zebra_vxlan_sg_deref(zvni->local_vtep_ip,
- zvni->mcast_grp);
+ if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
+ zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
+ zebra_vxlan_sg_deref(zevpn->local_vtep_ip,
+ zevpn->mcast_grp);
zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp);
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->mcast_grp = vxl->mcast_grp;
+ zevpn->local_vtep_ip = vxl->vtep_ip;
+ zevpn->mcast_grp = vxl->mcast_grp;
+ /* on local vtep-ip check if ES orig-ip
+ * needs to be updated
+ */
+ zebra_evpn_es_set_base_evpn(zevpn);
}
- zvni->vxlan_if = ifp;
-
+ zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
/* Take further actions needed.
* Note that if we are here, there is a change of interest.
*/
@@ -9198,7 +4820,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
& (ZEBRA_VXLIF_MASTER_CHANGE |
ZEBRA_VXLIF_LOCAL_IP_CHANGE |
ZEBRA_VXLIF_MCAST_GRP_CHANGE))
- zvni_send_add_to_client(zvni);
+ zebra_evpn_send_add_to_client(zevpn);
/* If there is a valid new master or a VLAN mapping change,
* read and populate local MACs and neighbors.
@@ -9206,22 +4828,22 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
* for this VNI (based on new VLAN).
*/
if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_evpn_read_mac_neigh(zevpn, ifp);
else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
struct mac_walk_ctx m_wctx;
struct neigh_walk_ctx n_wctx;
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_evpn_read_mac_neigh(zevpn, ifp);
memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
- hash_iterate(zvni->mac_table, zvni_install_mac_hash,
- &m_wctx);
+ m_wctx.zevpn = zevpn;
+ hash_iterate(zevpn->mac_table,
+ zebra_evpn_install_mac_hash, &m_wctx);
memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
- &n_wctx);
+ n_wctx.zevpn = zevpn;
+ hash_iterate(zevpn->neigh_table,
+ zebra_evpn_install_neigh_hash, &n_wctx);
}
}
@@ -9236,7 +4858,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
vni_t vni;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
/* Check if EVPN is enabled. */
@@ -9276,35 +4898,39 @@ int zebra_vxlan_if_add(struct interface *ifp)
/* process if-add for l2-vni */
struct interface *vlan_if = NULL;
- /* Create or update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zvni = zvni_add(vni);
- if (!zvni) {
+ /* Create or update EVPN hash. */
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn) {
+ zevpn = zebra_evpn_add(vni);
+ if (!zevpn) {
flog_err(
EC_ZEBRA_VNI_ADD_FAILED,
- "Failed to add VNI hash, IF %s(%u) VNI %u",
+ "Failed to add EVPN hash, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
return -1;
}
}
- if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
- zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
- zebra_vxlan_sg_deref(zvni->local_vtep_ip,
- zvni->mcast_grp);
+ if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
+ zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {
+ zebra_vxlan_sg_deref(zevpn->local_vtep_ip,
+ zevpn->mcast_grp);
zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp);
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->mcast_grp = vxl->mcast_grp;
+ zevpn->local_vtep_ip = vxl->vtep_ip;
+ zevpn->mcast_grp = vxl->mcast_grp;
+ /* on local vtep-ip check if ES orig-ip
+ * needs to be updated
+ */
+ zebra_evpn_es_set_base_evpn(zevpn);
}
- zvni->vxlan_if = ifp;
+ zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
- zvni->vrf_id = vlan_if->vrf_id;
+ zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
- listnode_add_sort(zl3vni->l2vnis, zvni);
+ listnode_add_sort(zl3vni->l2vnis, zevpn);
}
if (IS_ZEBRA_DEBUG_VXLAN) {
@@ -9331,10 +4957,10 @@ int zebra_vxlan_if_add(struct interface *ifp)
return 0;
/* Inform BGP */
- zvni_send_add_to_client(zvni);
+ zebra_evpn_send_add_to_client(zevpn);
/* Read and populate local MACs and neighbors */
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_evpn_read_mac_neigh(zevpn, ifp);
}
return 0;
@@ -9412,7 +5038,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
: "NIL");
/* formulate l2vni list */
- hash_iterate(zvrf_evpn->vni_table, zvni_add_to_l3vni_list,
+ hash_iterate(zvrf_evpn->evpn_table, zevpn_add_to_l3vni_list,
zl3vni);
if (is_l3vni_oper_up(zl3vni))
@@ -9480,8 +5106,15 @@ int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf)
if (!zl3vni)
return 0;
- zl3vni->vrf_id = VRF_UNKNOWN;
zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry, zl3vni);
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry, zl3vni);
+
+ zl3vni->vrf_id = VRF_UNKNOWN;
+
return 0;
}
@@ -9535,7 +5168,7 @@ void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS)
/* Install or uninstall flood entries corresponding to
* remote VTEPs.
*/
- hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps,
+ hash_iterate(zvrf->evpn_table, zebra_evpn_handle_flooding_remote_vteps,
zvrf);
stream_failure:
@@ -9551,7 +5184,7 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
struct stream *s;
int advertise;
vni_t vni = 0;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
struct interface *ifp = NULL;
if (!EVPN_ENABLED(zvrf)) {
@@ -9578,11 +5211,13 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
if (advertise) {
zvrf->advertise_svi_macip = advertise;
- hash_iterate(zvrf->vni_table,
- zvni_gw_macip_add_for_vni_hash, NULL);
+ hash_iterate(zvrf->evpn_table,
+ zebra_evpn_gw_macip_add_for_evpn_hash,
+ NULL);
} else {
- hash_iterate(zvrf->vni_table,
- zvni_svi_macip_del_for_vni_hash, NULL);
+ hash_iterate(zvrf->evpn_table,
+ zebra_evpn_svi_macip_del_for_evpn_hash,
+ NULL);
zvrf->advertise_svi_macip = advertise;
}
@@ -9591,27 +5226,27 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
struct zebra_l2info_vxlan zl2_info;
struct interface *vlan_if = NULL;
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"EVPN SVI macip Adv %s on VNI %d , currently %s",
advertise ? "enabled" : "disabled", vni,
- advertise_svi_macip_enabled(zvni)
+ advertise_svi_macip_enabled(zevpn)
? "enabled"
: "disabled");
- if (zvni->advertise_svi_macip == advertise)
+ if (zevpn->advertise_svi_macip == advertise)
return;
/* Store flag even though SVI is not present.
* Once SVI comes up triggers self MAC-IP route add.
*/
- zvni->advertise_svi_macip = advertise;
+ zevpn->advertise_svi_macip = advertise;
- ifp = zvni->vxlan_if;
+ ifp = zevpn->vxlan_if;
if (!ifp)
return;
@@ -9629,10 +5264,10 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
if (advertise) {
/* Add primary SVI MAC-IP */
- zvni_add_macip_for_intf(vlan_if, zvni);
+ zebra_evpn_add_macip_for_intf(vlan_if, zevpn);
} else {
/* Del primary SVI MAC-IP */
- zvni_del_macip_for_intf(vlan_if, zvni);
+ zebra_evpn_del_macip_for_intf(vlan_if, zevpn);
}
}
@@ -9649,7 +5284,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
struct stream *s;
int advertise;
vni_t vni = 0;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan zl2_info;
@@ -9665,22 +5300,22 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
STREAM_GETC(s, advertise);
STREAM_GET(&vni, s, 3);
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return;
- if (zvni->advertise_subnet == advertise)
+ if (zevpn->advertise_subnet == advertise)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("EVPN subnet Adv %s on VNI %d , currently %s",
advertise ? "enabled" : "disabled", vni,
- zvni->advertise_subnet ? "enabled" : "disabled");
+ zevpn->advertise_subnet ? "enabled" : "disabled");
- zvni->advertise_subnet = advertise;
+ zevpn->advertise_subnet = advertise;
- ifp = zvni->vxlan_if;
+ ifp = zevpn->vxlan_if;
if (!ifp)
return;
@@ -9697,10 +5332,10 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
if (!vlan_if)
return;
- if (zvni->advertise_subnet)
- zvni_advertise_subnet(zvni, vlan_if, 1);
+ if (zevpn->advertise_subnet)
+ zebra_evpn_advertise_subnet(zevpn, vlan_if, 1);
else
- zvni_advertise_subnet(zvni, vlan_if, 0);
+ zebra_evpn_advertise_subnet(zevpn, vlan_if, 0);
stream_failure:
return;
@@ -9715,7 +5350,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
struct stream *s;
int advertise;
vni_t vni = 0;
- zebra_vni_t *zvni = NULL;
+ zebra_evpn_t *zevpn = NULL;
struct interface *ifp = NULL;
if (!EVPN_ENABLED(zvrf)) {
@@ -9741,12 +5376,14 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
zvrf->advertise_gw_macip = advertise;
- if (advertise_gw_macip_enabled(zvni))
- hash_iterate(zvrf->vni_table,
- zvni_gw_macip_add_for_vni_hash, NULL);
+ if (advertise_gw_macip_enabled(zevpn))
+ hash_iterate(zvrf->evpn_table,
+ zebra_evpn_gw_macip_add_for_evpn_hash,
+ NULL);
else
- hash_iterate(zvrf->vni_table,
- zvni_gw_macip_del_for_vni_hash, NULL);
+ hash_iterate(zvrf->evpn_table,
+ zebra_evpn_gw_macip_del_for_evpn_hash,
+ NULL);
} else {
struct zebra_if *zif = NULL;
@@ -9754,23 +5391,23 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
struct interface *vlan_if = NULL;
struct interface *vrr_if = NULL;
- zvni = zvni_lookup(vni);
- if (!zvni)
+ zevpn = zebra_evpn_lookup(vni);
+ if (!zevpn)
return;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"EVPN gateway macip Adv %s on VNI %d , currently %s",
advertise ? "enabled" : "disabled", vni,
- advertise_gw_macip_enabled(zvni) ? "enabled"
+ advertise_gw_macip_enabled(zevpn) ? "enabled"
: "disabled");
- if (zvni->advertise_gw_macip == advertise)
+ if (zevpn->advertise_gw_macip == advertise)
return;
- zvni->advertise_gw_macip = advertise;
+ zevpn->advertise_gw_macip = advertise;
- ifp = zvni->vxlan_if;
+ ifp = zevpn->vxlan_if;
if (!ifp)
return;
@@ -9787,22 +5424,22 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
if (!vlan_if)
return;
- if (advertise_gw_macip_enabled(zvni)) {
+ if (advertise_gw_macip_enabled(zevpn)) {
/* Add primary SVI MAC-IP */
- zvni_add_macip_for_intf(vlan_if, zvni);
+ zebra_evpn_add_macip_for_intf(vlan_if, zevpn);
/* Add VRR MAC-IP - if any*/
vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
if (vrr_if)
- zvni_add_macip_for_intf(vrr_if, zvni);
+ zebra_evpn_add_macip_for_intf(vrr_if, zevpn);
} else {
/* Del primary MAC-IP */
- zvni_del_macip_for_intf(vlan_if, zvni);
+ zebra_evpn_del_macip_for_intf(vlan_if, zevpn);
/* Del VRR MAC-IP - if any*/
vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
if (vrr_if)
- zvni_del_macip_for_intf(vrr_if, zvni);
+ zebra_evpn_del_macip_for_intf(vrr_if, zevpn);
}
}
@@ -9810,25 +5447,6 @@ stream_failure:
return;
}
-static int macfdb_read_ns(struct ns *ns,
- void *_in_param __attribute__((unused)),
- void **out_param __attribute__((unused)))
-{
- struct zebra_ns *zns = ns->info;
-
- macfdb_read(zns);
- return NS_WALK_CONTINUE;
-}
-
-static int neigh_read_ns(struct ns *ns,
- void *_in_param __attribute__((unused)),
- void **out_param __attribute__((unused)))
-{
- struct zebra_ns *zns = ns->info;
-
- neigh_read(zns);
- return NS_WALK_CONTINUE;
-}
/*
* Handle message from client to learn (or stop learning) about VNIs and MACs.
@@ -9870,23 +5488,30 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS)
/* Note BUM handling */
zvrf->vxlan_flood_ctrl = flood_ctrl;
- /* Build VNI hash table and inform BGP. */
- zvni_build_hash_table();
+ /* Replay all ESs */
+ zebra_evpn_es_send_all_to_client(true /* add */);
+
+ /* Build EVPN hash table and inform BGP. */
+ zevpn_build_hash_table();
/* Add all SVI (L3 GW) MACs to BGP*/
- hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash,
- NULL);
+ hash_iterate(zvrf->evpn_table,
+ zebra_evpn_gw_macip_add_for_evpn_hash, NULL);
/* Read the MAC FDB */
- ns_walk_func(macfdb_read_ns, NULL, NULL);
+ macfdb_read(zvrf->zns);
/* Read neighbors */
- ns_walk_func(neigh_read_ns, NULL, NULL);
+ neigh_read(zvrf->zns);
} else {
- /* Cleanup VTEPs for all VNIs - uninstall from
+ /* Cleanup VTEPs for all EVPNs - uninstall from
* kernel and free entries.
*/
- hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+ hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all,
+ zvrf);
+
+ /* Delete all ESs in BGP */
+ zebra_evpn_es_send_all_to_client(false /* add */);
/* cleanup all l3vnis */
hash_iterate(zrouter.l3vni_table, zl3vni_cleanup_all, NULL);
@@ -9900,35 +5525,41 @@ stream_failure:
}
/*
- * Allocate VNI hash table for this VRF and do other initialization.
+ * Allocate EVPN hash table for this VRF and do other initialization.
* NOTE: Currently supported only for default VRF.
*/
void zebra_vxlan_init_tables(struct zebra_vrf *zvrf)
{
if (!zvrf)
return;
- zvrf->vni_table = hash_create(vni_hash_keymake, vni_hash_cmp,
- "Zebra VRF VNI Table");
+ zvrf->evpn_table =
+ hash_create(zebra_evpn_hash_keymake, zebra_evpn_hash_cmp,
+ "Zebra VRF EVPN Table");
zvrf->vxlan_sg_table = hash_create(zebra_vxlan_sg_hash_key_make,
zebra_vxlan_sg_hash_eq, "Zebra VxLAN SG Table");
}
-/* Cleanup VNI info, but don't free the table. */
+/* Cleanup EVPN info, but don't free the table. */
void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf)
{
+ struct zebra_vrf *evpn_zvrf = zebra_vrf_get_evpn();
+
if (!zvrf)
return;
- hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+ hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf);
hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_cleanup, NULL);
+
+ if (zvrf == evpn_zvrf)
+ zebra_evpn_es_cleanup();
}
-/* Close all VNI handling */
+/* Close all EVPN handling */
void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
{
if (!zvrf)
return;
- hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
- hash_free(zvrf->vni_table);
+ hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf);
+ hash_free(zvrf->evpn_table);
}
/* init the l3vni table */
@@ -9937,12 +5568,14 @@ void zebra_vxlan_init(void)
zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
"Zebra VRF L3 VNI table");
zrouter.evpn_vrf = NULL;
+ zebra_evpn_mh_init();
}
/* free l3vni table */
void zebra_vxlan_disable(void)
{
hash_free(zrouter.l3vni_table);
+ zebra_evpn_mh_terminate();
}
/* get the l3vni svi ifindex */
@@ -9957,128 +5590,6 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
return zl3vni->svi_if->ifindex;
}
-static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t)
-{
- struct zebra_vrf *zvrf = NULL;
- zebra_neigh_t *nbr = NULL;
- zebra_vni_t *zvni = NULL;
- char buf1[INET6_ADDRSTRLEN];
- char buf2[ETHER_ADDR_STRLEN];
-
- nbr = THREAD_ARG(t);
-
- /* since this is asynchronous we need sanity checks*/
- zvrf = vrf_info_lookup(nbr->zvni->vrf_id);
- if (!zvrf)
- return 0;
-
- zvni = zvni_lookup(nbr->zvni->vni);
- if (!zvni)
- return 0;
-
- nbr = zvni_neigh_lookup(zvni, &nbr->ip);
- if (!nbr)
- return 0;
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired",
- __func__,
- prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags,
- nbr->dad_count, zvni->vni);
-
- UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
- nbr->dad_count = 0;
- nbr->detect_start_time.tv_sec = 0;
- nbr->detect_start_time.tv_usec = 0;
- nbr->dad_dup_detect_time = 0;
- nbr->dad_ip_auto_recovery_timer = NULL;
- ZEBRA_NEIGH_SET_ACTIVE(nbr);
-
- /* Send to BGP */
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) {
- zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac,
- nbr->flags, nbr->loc_seq);
- } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
- }
-
- return 0;
-}
-
-static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t)
-{
- struct zebra_vrf *zvrf = NULL;
- zebra_mac_t *mac = NULL;
- zebra_vni_t *zvni = NULL;
- struct listnode *node = NULL;
- zebra_neigh_t *nbr = NULL;
- char buf[ETHER_ADDR_STRLEN];
-
- mac = THREAD_ARG(t);
-
- /* since this is asynchronous we need sanity checks*/
- zvrf = vrf_info_lookup(mac->zvni->vrf_id);
- if (!zvrf)
- return 0;
-
- zvni = zvni_lookup(mac->zvni->vni);
- if (!zvni)
- return 0;
-
- mac = zvni_mac_lookup(zvni, &mac->macaddr);
- if (!mac)
- return 0;
-
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count, listcount(mac->neigh_list));
-
- /* Remove all IPs as duplicate associcated with this MAC */
- for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
- if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL))
- ZEBRA_NEIGH_SET_INACTIVE(nbr);
- else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE))
- zvni_neigh_install(zvni, nbr);
- }
-
- UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
- nbr->dad_count = 0;
- nbr->detect_start_time.tv_sec = 0;
- nbr->dad_dup_detect_time = 0;
- }
-
- UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE);
- mac->dad_count = 0;
- mac->detect_start_time.tv_sec = 0;
- mac->detect_start_time.tv_usec = 0;
- mac->dad_dup_detect_time = 0;
- mac->dad_mac_auto_recovery_timer = NULL;
-
- if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
- /* Inform to BGP */
- if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr,
- mac->flags, mac->loc_seq))
- return -1;
-
- /* Process all neighbors associated with this MAC. */
- zvni_process_neigh_on_local_mac_change(zvni, mac, 0);
-
- } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
- zvni_process_neigh_on_remote_mac_add(zvni, mac);
-
- /* Install the entry. */
- zvni_mac_install(zvni, mac);
- }
-
- return 0;
-}
-
/************************** vxlan SG cache management ************************/
/* Inform PIM about the mcast group */
static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf,
@@ -10330,87 +5841,6 @@ void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS)
hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_replay_send, NULL);
}
-/************************** EVPN BGP config management ************************/
-/* Notify Local MACs to the clienti, skips GW MAC */
-static void zvni_send_mac_hash_entry_to_client(struct hash_bucket *bucket,
- void *arg)
-{
- struct mac_walk_ctx *wctx = arg;
- zebra_mac_t *zmac = bucket->data;
-
- if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW))
- return;
-
- if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL))
- zvni_mac_send_add_to_client(wctx->zvni->vni, &zmac->macaddr,
- zmac->flags, zmac->loc_seq);
-}
-
-/* Iterator to Notify Local MACs of a L2VNI */
-static void zvni_send_mac_to_client(zebra_vni_t *zvni)
-{
- struct mac_walk_ctx wctx;
-
- if (!zvni->mac_table)
- return;
-
- memset(&wctx, 0, sizeof(struct mac_walk_ctx));
- wctx.zvni = zvni;
-
- hash_iterate(zvni->mac_table, zvni_send_mac_hash_entry_to_client,
- &wctx);
-}
-
-/* Notify Neighbor entries to the Client, skips the GW entry */
-static void zvni_send_neigh_hash_entry_to_client(struct hash_bucket *bucket,
- void *arg)
-{
- struct mac_walk_ctx *wctx = arg;
- zebra_neigh_t *zn = bucket->data;
- zebra_mac_t *zmac = NULL;
-
- if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW))
- return;
-
- if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) &&
- IS_ZEBRA_NEIGH_ACTIVE(zn)) {
- zmac = zvni_mac_lookup(wctx->zvni, &zn->emac);
- if (!zmac)
- return;
-
- zvni_neigh_send_add_to_client(wctx->zvni->vni, &zn->ip,
- &zn->emac, zn->flags,
- zn->loc_seq);
- }
-}
-
-/* Iterator of a specific L2VNI */
-static void zvni_send_neigh_to_client(zebra_vni_t *zvni)
-{
- struct neigh_walk_ctx wctx;
-
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
-
- hash_iterate(zvni->neigh_table, zvni_send_neigh_hash_entry_to_client,
- &wctx);
-}
-
-static void zvni_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt)
-{
- zebra_vni_t *zvni = NULL;
-
- zvni = (zebra_vni_t *)bucket->data;
- zvni->advertise_gw_macip = 0;
- zvni->advertise_svi_macip = 0;
- zvni->advertise_subnet = 0;
-
- zvni_neigh_del_all(zvni, 1, 0,
- DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP);
- zvni_mac_del_all(zvni, 1, 0,
- DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP);
- zvni_vtep_del_all(zvni, 1);
-}
/* Cleanup EVPN configuration of a specific VRF */
static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf)
@@ -10422,7 +5852,7 @@ static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf)
zvrf->advertise_svi_macip = 0;
zvrf->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
- hash_iterate(zvrf->vni_table, zvni_evpn_cfg_cleanup, NULL);
+ hash_iterate(zvrf->evpn_table, zebra_evpn_cfg_cleanup, NULL);
if (zvrf->l3vni)
zl3vni = zl3vni_lookup(zvrf->l3vni);
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 064dda6cd0..9c8af9d1fc 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -165,14 +165,15 @@ extern int zebra_vxlan_svi_down(struct interface *ifp,
extern int zebra_vxlan_handle_kernel_neigh_update(
struct interface *ifp, struct interface *link_if, struct ipaddr *ip,
struct ethaddr *macaddr, uint16_t state, bool is_ext,
- bool is_router);
+ bool is_router, bool local_inactive, bool dp_static);
extern int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
struct interface *link_if,
struct ipaddr *ip);
extern int zebra_vxlan_local_mac_add_update(struct interface *ifp,
struct interface *br_if,
struct ethaddr *mac, vlanid_t vid,
- bool sticky);
+ bool sticky, bool local_inactive,
+ bool dp_static);
extern int zebra_vxlan_local_mac_del(struct interface *ifp,
struct interface *br_if,
struct ethaddr *mac, vlanid_t vid);
@@ -217,6 +218,7 @@ extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx);
extern void zebra_evpn_init(void);
extern void zebra_vxlan_macvlan_up(struct interface *ifp);
extern void zebra_vxlan_macvlan_down(struct interface *ifp);
+extern int vni_list_cmp(void *p1, void *p2);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index d2b02df2ad..9a88d98b81 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -29,6 +29,8 @@
#include "if.h"
#include "linklist.h"
#include "zebra_vxlan.h"
+#include "zebra_evpn.h"
+#include "zebra_evpn_mac.h"
#ifdef __cplusplus
extern "C" {
@@ -37,72 +39,8 @@ extern "C" {
#define ERR_STR_SZ 256
/* definitions */
-typedef struct zebra_vni_t_ zebra_vni_t;
-typedef struct zebra_vtep_t_ zebra_vtep_t;
-typedef struct zebra_mac_t_ zebra_mac_t;
-typedef struct zebra_neigh_t_ zebra_neigh_t;
typedef struct zebra_l3vni_t_ zebra_l3vni_t;
-/*
- * VTEP info
- *
- * Right now, this just has each remote VTEP's IP address.
- */
-struct zebra_vtep_t_ {
- /* Remote IP. */
- /* NOTE: Can only be IPv4 right now. */
- struct in_addr vtep_ip;
- /* Flood mode (one of enum vxlan_flood_control) based on the PMSI
- * tunnel type advertised by the remote VTEP
- */
- int flood_control;
-
- /* Links. */
- struct zebra_vtep_t_ *next;
- struct zebra_vtep_t_ *prev;
-};
-
-
-/*
- * VNI hash table
- *
- * Contains information pertaining to a VNI:
- * - the list of remote VTEPs (with this VNI)
- */
-struct zebra_vni_t_ {
- /* VNI - key */
- vni_t vni;
-
- /* Flag for advertising gw macip */
- uint8_t advertise_gw_macip;
-
- /* Flag for advertising svi macip */
- uint8_t advertise_svi_macip;
-
- /* Flag for advertising gw macip */
- uint8_t advertise_subnet;
-
- /* Corresponding VxLAN interface. */
- struct interface *vxlan_if;
-
- /* List of remote VTEPs */
- zebra_vtep_t *vteps;
-
- /* Local IP */
- struct in_addr local_vtep_ip;
-
- /* PIM-SM MDT group for BUM flooding */
- struct in_addr mcast_grp;
-
- /* tenant VRF, if any */
- vrf_id_t vrf_id;
-
- /* List of local or remote MAC */
- struct hash *mac_table;
-
- /* List of local or remote neighbors (MAC+IP) */
- struct hash *neigh_table;
-};
/* L3 VNI hash table */
struct zebra_l3vni_t_ {
@@ -266,202 +204,11 @@ static inline void zl3vni_get_svi_rmac(zebra_l3vni_t *zl3vni,
memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN);
}
-struct host_rb_entry {
- RB_ENTRY(host_rb_entry) hl_entry;
-
- struct prefix p;
-};
-
-RB_HEAD(host_rb_tree_entry, host_rb_entry);
-RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry,
- host_rb_entry_compare);
-/*
- * MAC hash table.
- *
- * This table contains the MAC addresses pertaining to this VNI.
- * This includes local MACs learnt on an attached VLAN that maps
- * to this VNI as well as remote MACs learnt and installed by BGP.
- * Local MACs will be known either on a VLAN sub-interface or
- * on (port, VLAN); however, it is sufficient for zebra to maintain
- * against the VNI i.e., it does not need to retain the local "port"
- * information. The correct VNI will be obtained as zebra maintains
- * the mapping (of VLAN to VNI).
- */
-struct zebra_mac_t_ {
- /* MAC address. */
- struct ethaddr macaddr;
-
- uint32_t flags;
-#define ZEBRA_MAC_LOCAL 0x01
-#define ZEBRA_MAC_REMOTE 0x02
-#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */
-#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */
-#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */
-#define ZEBRA_MAC_DEF_GW 0x20
-/* remote VTEP advertised MAC as default GW */
-#define ZEBRA_MAC_REMOTE_DEF_GW 0x40
-#define ZEBRA_MAC_DUPLICATE 0x80
-#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */
-
- /* back pointer to zvni */
- zebra_vni_t *zvni;
-
- /* Local or remote info. */
- union {
- struct {
- ifindex_t ifindex;
- ns_id_t ns_id;
- vlanid_t vid;
- } local;
-
- struct in_addr r_vtep_ip;
- } fwd_info;
-
- /* Mobility sequence numbers associated with this entry. */
- uint32_t rem_seq;
- uint32_t loc_seq;
-
- /* List of neigh associated with this mac */
- struct list *neigh_list;
-
- /* list of hosts pointing to this remote RMAC */
- struct host_rb_tree_entry host_rb;
-
- /* Duplicate mac detection */
- uint32_t dad_count;
-
- struct thread *dad_mac_auto_recovery_timer;
-
- struct timeval detect_start_time;
-
- time_t dad_dup_detect_time;
-};
-
-/*
- * Context for MAC hash walk - used by callbacks.
- */
-struct mac_walk_ctx {
- zebra_vni_t *zvni; /* VNI hash */
- struct zebra_vrf *zvrf; /* VRF - for client notification. */
- int uninstall; /* uninstall from kernel? */
- int upd_client; /* uninstall from client? */
-
- uint32_t flags;
-#define DEL_LOCAL_MAC 0x1
-#define DEL_REMOTE_MAC 0x2
-#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC)
-#define DEL_REMOTE_MAC_FROM_VTEP 0x4
-#define SHOW_REMOTE_MAC_FROM_VTEP 0x8
-
- struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */
-
- struct vty *vty; /* Used by VTY handlers */
- uint32_t count; /* Used by VTY handlers */
- struct json_object *json; /* Used for JSON Output */
- bool print_dup; /* Used to print dup addr list */
-};
-
-struct rmac_walk_ctx {
- struct vty *vty;
- struct json_object *json;
-};
-
-#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE)
-
-#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE)
-
-#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE
-
-#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE
-
-/*
- * Neighbor hash table.
- *
- * This table contains the neighbors (IP to MAC bindings) pertaining to
- * this VNI. This includes local neighbors learnt on the attached VLAN
- * device that maps to this VNI as well as remote neighbors learnt and
- * installed by BGP.
- * Local neighbors will be known against the VLAN device (SVI); however,
- * it is sufficient for zebra to maintain against the VNI. The correct
- * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI).
- */
-struct zebra_neigh_t_ {
- /* IP address. */
- struct ipaddr ip;
-
- /* MAC address. */
- struct ethaddr emac;
-
- /* Underlying interface. */
- ifindex_t ifindex;
-
- zebra_vni_t *zvni;
-
- uint32_t flags;
-#define ZEBRA_NEIGH_LOCAL 0x01
-#define ZEBRA_NEIGH_REMOTE 0x02
-#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */
-#define ZEBRA_NEIGH_DEF_GW 0x08
-#define ZEBRA_NEIGH_ROUTER_FLAG 0x10
-#define ZEBRA_NEIGH_DUPLICATE 0x20
-#define ZEBRA_NEIGH_SVI_IP 0x40
-
- enum zebra_neigh_state state;
-
- /* Remote VTEP IP - applicable only for remote neighbors. */
- struct in_addr r_vtep_ip;
-
- /*
- * Mobility sequence numbers associated with this entry. The rem_seq
- * represents the sequence number from the client (BGP) for the most
- * recent add or update of this entry while the loc_seq represents
- * the sequence number informed (or to be informed) by zebra to BGP
- * for this entry.
- */
- uint32_t rem_seq;
- uint32_t loc_seq;
-
- /* list of hosts pointing to this remote NH entry */
- struct host_rb_tree_entry host_rb;
-
- /* Duplicate ip detection */
- uint32_t dad_count;
-
- struct thread *dad_ip_auto_recovery_timer;
-
- struct timeval detect_start_time;
-
- time_t dad_dup_detect_time;
-};
-
-/*
- * Context for neighbor hash walk - used by callbacks.
- */
-struct neigh_walk_ctx {
- zebra_vni_t *zvni; /* VNI hash */
- struct zebra_vrf *zvrf; /* VRF - for client notification. */
- int uninstall; /* uninstall from kernel? */
- int upd_client; /* uninstall from client? */
-
- uint32_t flags;
-#define DEL_LOCAL_NEIGH 0x1
-#define DEL_REMOTE_NEIGH 0x2
-#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH)
-#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4
-#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8
-
- struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */
-
- struct vty *vty; /* Used by VTY handlers */
- uint32_t count; /* Used by VTY handlers */
- uint8_t addr_width; /* Used by VTY handlers */
- struct json_object *json; /* Used for JSON Output */
-};
/* context for neigh hash walk - update l3vni and rmac */
struct neigh_l3info_walk_ctx {
- zebra_vni_t *zvni;
+ zebra_evpn_t *zevpn;
zebra_l3vni_t *zl3vni;
int add;
};
@@ -476,6 +223,7 @@ extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id);
extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni);
+extern zebra_l3vni_t *zl3vni_lookup(vni_t vni);
DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason), (rmac, zl3vni, delete, reason))
@@ -507,4 +255,8 @@ typedef struct zebra_vxlan_sg_ {
uint32_t ref_cnt;
} zebra_vxlan_sg_t;
+extern zebra_evpn_t *zevpn_lookup(vni_t vni);
+extern void zebra_vxlan_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive,
+ bool force_clear_static, const char *caller);
+
#endif /* _ZEBRA_VXLAN_PRIVATE_H */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 99a85fd2ce..cded6ea12b 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -628,8 +628,8 @@ static void zserv_client_free(struct zserv *client)
}
vrf_bitmap_free(client->redist_default[afi]);
+ vrf_bitmap_free(client->ridinfo[afi]);
}
- vrf_bitmap_free(client->ridinfo);
/*
* If any instance are graceful restart enabled,
@@ -750,8 +750,8 @@ static struct zserv *zserv_client_create(int sock)
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
client->redist[afi][i] = vrf_bitmap_init();
client->redist_default[afi] = vrf_bitmap_init();
+ client->ridinfo[afi] = vrf_bitmap_init();
}
- client->ridinfo = vrf_bitmap_init();
/* Add this client to linked list. */
frr_with_mutex(&client_mutex) {
@@ -1075,6 +1075,12 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client)
vty_out(vty, "L3-VNI delete notifications: %u\n", client->l3vnidel_cnt);
vty_out(vty, "MAC-IP add notifications: %u\n", client->macipadd_cnt);
vty_out(vty, "MAC-IP delete notifications: %u\n", client->macipdel_cnt);
+ vty_out(vty, "ES add notifications: %u\n", client->local_es_add_cnt);
+ vty_out(vty, "ES delete notifications: %u\n", client->local_es_del_cnt);
+ vty_out(vty, "ES-EVI add notifications: %u\n",
+ client->local_es_evi_add_cnt);
+ vty_out(vty, "ES-EVI delete notifications: %u\n",
+ client->local_es_evi_del_cnt);
TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id));
diff --git a/zebra/zserv.h b/zebra/zserv.h
index f2a4523818..54e840cd56 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -135,7 +135,7 @@ struct zserv {
vrf_bitmap_t redist_default[AFI_MAX];
/* Router-id information. */
- vrf_bitmap_t ridinfo;
+ vrf_bitmap_t ridinfo[AFI_MAX];
bool notify_owner;
@@ -194,6 +194,10 @@ struct zserv {
uint32_t v6_nh_watch_rem_cnt;
uint32_t vxlan_sg_add_cnt;
uint32_t vxlan_sg_del_cnt;
+ uint32_t local_es_add_cnt;
+ uint32_t local_es_del_cnt;
+ uint32_t local_es_evi_add_cnt;
+ uint32_t local_es_evi_del_cnt;
uint32_t error_cnt;
time_t nh_reg_time;