]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #5473 from yasuhiro-ohara-ntt/ospf6d-self-orig-maxage-fix
authorSri Mohana Singamsetty <srimohans@gmail.com>
Tue, 21 Jan 2020 17:04:04 +0000 (22:34 +0530)
committerGitHub <noreply@github.com>
Tue, 21 Jan 2020 17:04:04 +0000 (22:34 +0530)
ospf6d: ospf6_flood.c: self-originated MaxAge LSAs to install and refresh.

366 files changed:
.gitignore
Makefile.am
alpine/APKBUILD.in
bfdd/subdir.am
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_bmp.c
bgpd/bgp_bmp.h
bgpd/bgp_evpn.c
bgpd/bgp_evpn_vty.c
bgpd/bgp_fsm.c
bgpd/bgp_label.c
bgpd/bgp_mac.c
bgpd/bgp_main.c
bgpd/bgp_mpath.c
bgpd/bgp_mplsvpn.c
bgpd/bgp_nht.c
bgpd/bgp_open.c
bgpd/bgp_packet.c
bgpd/bgp_pbr.c
bgpd/bgp_rd.c
bgpd/bgp_route.c
bgpd/bgp_updgrp.c
bgpd/bgp_updgrp.h
bgpd/bgp_updgrp_adv.c
bgpd/bgp_updgrp_packet.c
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgp_zebra.c
bgpd/bgp_zebra.h
bgpd/bgpd.c
bgpd/bgpd.h
bgpd/rfapi/vnc_export_bgp.c
bgpd/rfapi/vnc_import_bgp.c
bgpd/subdir.am
configure.ac
debian/README.Debian
debian/frr.install
debian/frr.logrotate
debian/frr.manpages
defaults.h [deleted file]
doc/developer/building-frr-for-centos6.rst
doc/developer/building-frr-for-centos7.rst
doc/developer/building-frr-for-centos8.rst [new file with mode: 0644]
doc/developer/building-frr-for-fedora.rst
doc/developer/building.rst
doc/developer/packaging-redhat.rst
doc/developer/static-linking.rst [new file with mode: 0644]
doc/developer/subdir.am
doc/developer/topotests.rst
doc/developer/workflow.rst
doc/manpages/bfdd.rst [deleted file]
doc/manpages/bgpd.rst [deleted file]
doc/manpages/conf.py
doc/manpages/defines.rst
doc/manpages/eigrpd.rst [deleted file]
doc/manpages/fabricd.rst [deleted file]
doc/manpages/frr-bfdd.rst [new file with mode: 0644]
doc/manpages/frr-bgpd.rst [new file with mode: 0644]
doc/manpages/frr-eigrpd.rst [new file with mode: 0644]
doc/manpages/frr-fabricd.rst [new file with mode: 0644]
doc/manpages/frr-isisd.rst [new file with mode: 0644]
doc/manpages/frr-ldpd.rst [new file with mode: 0644]
doc/manpages/frr-nhrpd.rst [new file with mode: 0644]
doc/manpages/frr-ospf6d.rst [new file with mode: 0644]
doc/manpages/frr-ospfclient.rst [new file with mode: 0644]
doc/manpages/frr-ospfd.rst [new file with mode: 0644]
doc/manpages/frr-pbrd.rst [new file with mode: 0644]
doc/manpages/frr-pimd.rst [new file with mode: 0644]
doc/manpages/frr-ripd.rst [new file with mode: 0644]
doc/manpages/frr-ripngd.rst [new file with mode: 0644]
doc/manpages/frr-sharpd.rst [new file with mode: 0644]
doc/manpages/frr-staticd.rst [new file with mode: 0644]
doc/manpages/frr-vrrpd.rst [new file with mode: 0644]
doc/manpages/frr-watchfrr.rst [new file with mode: 0644]
doc/manpages/frr-zebra.rst [new file with mode: 0644]
doc/manpages/index.rst
doc/manpages/isisd.rst [deleted file]
doc/manpages/ldpd.rst [deleted file]
doc/manpages/nhrpd.rst [deleted file]
doc/manpages/ospf6d.rst [deleted file]
doc/manpages/ospfclient.rst [deleted file]
doc/manpages/ospfd.rst [deleted file]
doc/manpages/pbrd.rst [deleted file]
doc/manpages/pimd.rst [deleted file]
doc/manpages/ripd.rst [deleted file]
doc/manpages/ripngd.rst [deleted file]
doc/manpages/sharpd.rst [deleted file]
doc/manpages/staticd.rst [deleted file]
doc/manpages/subdir.am
doc/manpages/vrrpd.rst [deleted file]
doc/manpages/watchfrr.rst [deleted file]
doc/manpages/zebra.rst [deleted file]
doc/user/basic.rst
doc/user/bfd.rst
doc/user/bgp.rst
doc/user/nhrpd.rst
doc/user/overview.rst
doc/user/pbr.rst
doc/user/pim.rst
doc/user/zebra.rst
docker/alpine/Dockerfile
docker/centos-7/Dockerfile [new file with mode: 0644]
docker/centos-7/build.sh [new file with mode: 0755]
docker/centos-7/docker-start [new file with mode: 0755]
docker/centos-8/Dockerfile [new file with mode: 0644]
docker/centos-8/build.sh [new file with mode: 0755]
docker/centos-8/docker-start [new file with mode: 0755]
docker/centos/Dockerfile [deleted file]
docker/centos/build.sh [deleted file]
docker/centos/docker-start [deleted file]
eigrpd/eigrp_interface.c
eigrpd/eigrp_structs.h
eigrpd/subdir.am
isisd/isis_main.c
isisd/isis_nb_config.c
isisd/isis_pdu.c
isisd/isis_zebra.c
isisd/isisd.c
isisd/subdir.am
ldpd/subdir.am
lib/agg_table.c
lib/buffer.c
lib/command.c
lib/command_lex.l
lib/defaults.c [new file with mode: 0644]
lib/defaults.h [new file with mode: 0644]
lib/filter.c
lib/frrcu.c
lib/frrcu.h
lib/gitversion.pl
lib/grammar_sandbox.c
lib/grammar_sandbox_main.c
lib/if.c
lib/ipaddr.h
lib/lib_vty.c [new file with mode: 0644]
lib/lib_vty.h [new file with mode: 0644]
lib/libfrr.c
lib/linklist.c
lib/linklist.h
lib/log.c
lib/memory.h
lib/memory_vty.c [deleted file]
lib/memory_vty.h [deleted file]
lib/netns_linux.c
lib/netns_other.c
lib/nexthop.c
lib/nexthop.h
lib/nexthop_group.c
lib/nexthop_group.h
lib/nexthop_group_private.h
lib/northbound_cli.c
lib/ns.h
lib/plist.c
lib/prefix.c
lib/prefix.h
lib/resolver.c
lib/resolver.h
lib/routemap.c
lib/skiplist.c
lib/sockopt.c
lib/sockopt.h
lib/stream.c
lib/stream.h
lib/subdir.am
lib/systemd.c
lib/systemd.h
lib/vrf.c
lib/vrf.h
lib/vty.c
lib/yang.c
lib/zclient.c
lib/zclient.h
m4/ax_python.m4
mlag/subdir.am
nhrpd/nhrp_main.c
nhrpd/nhrp_nhs.c
nhrpd/nhrp_peer.c
nhrpd/subdir.am
ospf6d/ospf6_main.c
ospf6d/ospf6_top.c
ospf6d/ospf6_top.h
ospf6d/subdir.am
ospfclient/subdir.am
ospfd/ospf_apiserver.c
ospfd/ospf_interface.c
ospfd/ospf_interface.h
ospfd/ospf_main.c
ospfd/ospf_packet.c
ospfd/ospf_vty.c
ospfd/ospfd.c
ospfd/ospfd.h
ospfd/subdir.am
pbrd/pbr_map.c
pbrd/pbr_vty.c
pbrd/pbr_zebra.c
pbrd/subdir.am
pimd/pim_cmd.c
pimd/pim_iface.c
pimd/pim_ifchannel.c
pimd/pim_igmp.c
pimd/pim_igmp_join.h
pimd/pim_igmp_mtrace.c
pimd/pim_instance.h
pimd/pim_join.c
pimd/pim_jp_agg.c
pimd/pim_jp_agg.h
pimd/pim_main.c
pimd/pim_mroute.c
pimd/pim_mroute.h
pimd/pim_msdp.c
pimd/pim_msg.c
pimd/pim_nht.c
pimd/pim_oil.c
pimd/pim_oil.h
pimd/pim_register.c
pimd/pim_rp.c
pimd/pim_rpf.c
pimd/pim_rpf.h
pimd/pim_static.c
pimd/pim_upstream.c
pimd/pim_upstream.h
pimd/pim_vty.c
pimd/pim_vxlan.c
pimd/pim_vxlan.h
pimd/pim_zebra.c
pimd/pim_zlookup.c
pimd/subdir.am
redhat/frr.spec.in
ripd/rip_cli.c
ripd/rip_main.c
ripd/rip_zebra.c
ripd/subdir.am
ripngd/ripng_cli.c
ripngd/ripng_main.c
ripngd/ripng_zebra.c
ripngd/subdir.am
sharpd/sharp_zebra.c
sharpd/subdir.am
solaris/prototype.doc.in
staticd/static_debug.c [new file with mode: 0644]
staticd/static_debug.h [new file with mode: 0644]
staticd/static_main.c
staticd/static_vty.c
staticd/static_zebra.c
staticd/subdir.am
tests/.gitignore
tests/bgpd/test_capability.c
tests/bgpd/test_mp_attr.c
tests/bgpd/test_packet.c
tests/helpers/c/main.c
tests/lib/cli/common_cli.c
tests/lib/cxxcompat.c
tests/lib/northbound/test_oper_data.c
tests/lib/test_buffer.c
tests/lib/test_privs.c
tests/lib/test_typelist.h
tests/lib/test_versioncmp.c [new file with mode: 0644]
tests/lib/test_versioncmp.py [new file with mode: 0644]
tests/subdir.am
tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
tests/topotests/all-protocol-startup/r1/zebra.conf
tests/topotests/all-protocol-startup/test_all_protocol_startup.py
tests/topotests/bgp_default-route_route-map/__init__.py [new file with mode: 0644]
tests/topotests/bgp_default-route_route-map/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_default-route_route-map/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_default-route_route-map/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_default-route_route-map/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py
tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
tests/topotests/bgp_maximum_prefix_out/__init__.py [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py [new file with mode: 0644]
tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py
tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
tests/topotests/lib/common_config.py
tests/topotests/lib/ltemplate.py
tests/topotests/lib/lutil.py
tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref [new file with mode: 0644]
tests/topotests/ospf6-topo1/test_ospf6_topo1.py
tests/topotests/pim-basic/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/pim-basic/r1/pimd.conf
tests/topotests/pim-basic/r1/rp-info.json [new file with mode: 0644]
tests/topotests/pim-basic/r1/zebra.conf
tests/topotests/pim-basic/rp/bgpd.conf [new file with mode: 0644]
tests/topotests/pim-basic/rp/pimd.conf [new file with mode: 0644]
tests/topotests/pim-basic/rp/upstream.json [new file with mode: 0644]
tests/topotests/pim-basic/rp/zebra.conf [new file with mode: 0644]
tests/topotests/pim-basic/test_pim.py
tools/etc/frr/daemons
tools/frr-reload.py
tools/frr.in
tools/frrcommon.sh.in
tools/symalyzer.html [new file with mode: 0644]
tools/symalyzer.py [new file with mode: 0755]
vrrpd/subdir.am
vrrpd/vrrp.c
vrrpd/vrrp.h
vrrpd/vrrp_main.c
vrrpd/vrrp_northbound.c [new file with mode: 0644]
vrrpd/vrrp_vty.c
vrrpd/vrrp_vty.h
vrrpd/vrrp_zebra.c
vtysh/extract.pl.in
vtysh/vtysh.c
vtysh/vtysh.h
vtysh/vtysh_config.c
vtysh/vtysh_main.c
watchfrr/subdir.am
watchfrr/watchfrr.c
yang/frr-isisd.yang
yang/frr-vrrpd.yang [new file with mode: 0644]
yang/subdir.am
zebra/debug.c
zebra/debug.h
zebra/if_netlink.c
zebra/interface.c
zebra/kernel_netlink.c
zebra/label_manager.c
zebra/main.c
zebra/redistribute.c
zebra/rib.h
zebra/router-id.c
zebra/rt_netlink.c
zebra/rtadv.c
zebra/rtadv.h
zebra/subdir.am
zebra/zapi_msg.c
zebra/zebra_dplane.c
zebra/zebra_errors.c
zebra/zebra_errors.h
zebra/zebra_fpm_dt.c
zebra/zebra_fpm_netlink.c
zebra/zebra_fpm_protobuf.c
zebra/zebra_mlag.c
zebra/zebra_mlag.h
zebra/zebra_mlag_private.c
zebra/zebra_mlag_private.h [deleted file]
zebra/zebra_mlag_vty.c [new file with mode: 0644]
zebra/zebra_mlag_vty.h [new file with mode: 0644]
zebra/zebra_mpls.c
zebra/zebra_nhg.c
zebra/zebra_nhg.h
zebra/zebra_nhg_private.h
zebra/zebra_ns.c
zebra/zebra_pbr.c
zebra/zebra_ptm.c
zebra/zebra_pw.c
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_router.c
zebra/zebra_snmp.c
zebra/zebra_vty.c
zebra/zebra_vxlan.c
zebra/zebra_vxlan_private.h
zebra/zserv.c
zebra/zserv.h

index 6cfe23e9214740d26a7cbe8eb88b3a84d04bcd39..226dca09d0a5270b52aa70f751798cacab43cde0 100644 (file)
 /Makefile
 /Makefile.in
 
+/symalyzer_report.html
+/jquery-3.4.1.min.js
+/jquery-3.4.1.min.js.tmp
+
 ### autoconf/automake subdir stuff
 
 .deps
index 34f112bf01da8dda396e2310605e6d1fbb0f669d..6dc8e0d354967e89a6d4d9361863783557d1ec62 100644 (file)
@@ -228,8 +228,6 @@ EXTRA_DIST += \
        vrrpd/Makefile \
        # end
 
-noinst_HEADERS += defaults.h
-
 clean-local: clean-python
 .PHONY: clean-python
 clean-python:
index 1c579a8e72477167a2597f6f0f774311fe73b182..f740a34583cf1be31f7ad6e2479a8f36fb545b0b 100644 (file)
@@ -17,7 +17,7 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
     linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev
     ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre
     perl pkgconf python2 python2-dev readline readline-dev sqlite-libs
-    squashfs-tools sudo tar texinfo xorriso xz-libs py-pip py-sphinx rtrlib
+    squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib
     rtrlib-dev"
 checkdepends="pytest py-setuptools"
 install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
@@ -34,6 +34,12 @@ _user=frr
 
 build() {
        cd "$builddir"
+
+       _localpythondir=$PWD/.python
+       pip2 install --prefix $_localpythondir sphinx
+       export PATH=${_localpythondir}/bin:$PATH
+       export PYTHONPATH=${_localpythondir}/lib/python2.7/site-packages
+
        ./configure \
                --prefix=/usr \
                --sbindir=$_sbindir \
index ed1d3962bf76d45c1581ef67d5033b2d699a0b04..9aa522f3f076b31cfa6ddf7e17bb0d00be909379 100644 (file)
@@ -8,7 +8,7 @@ sbin_PROGRAMS += bfdd/bfdd
 dist_examples_DATA += bfdd/bfdd.conf.sample
 vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c
 vtysh_scan += $(top_srcdir)/bfdd/bfdd_cli.c
-man8 += $(MANBUILD)/bfdd.8
+man8 += $(MANBUILD)/frr-bfdd.8
 endif
 
 bfdd_libbfd_a_SOURCES = \
index bfa578085d7907e7960bfe403f201bd3d5d826f9..16de59b72caca0ee2320ff71164d22ba90bb58bb 100644 (file)
@@ -152,8 +152,9 @@ static bool cluster_hash_cmp(const void *p1, const void *p2)
        const struct cluster_list *cluster2 = p2;
 
        return (cluster1->length == cluster2->length
-               && memcmp(cluster1->list, cluster2->list, cluster1->length)
-                          == 0);
+               && (cluster1->list == cluster2->list
+                   || memcmp(cluster1->list, cluster2->list, cluster1->length)
+                              == 0));
 }
 
 static void cluster_free(struct cluster_list *cluster)
@@ -421,14 +422,15 @@ static struct transit *transit_intern(struct transit *transit)
        return find;
 }
 
-void transit_unintern(struct transit *transit)
+static void transit_unintern(struct transit **transit)
 {
-       if (transit->refcnt)
-               transit->refcnt--;
+       if ((*transit)->refcnt)
+               (*transit)->refcnt--;
 
-       if (transit->refcnt == 0) {
-               hash_release(transit_hash, transit);
-               transit_free(transit);
+       if ((*transit)->refcnt == 0) {
+               hash_release(transit_hash, *transit);
+               transit_free(*transit);
+               *transit = NULL;
        }
 }
 
@@ -464,15 +466,6 @@ static void transit_finish(void)
 /* Attribute hash routines. */
 static struct hash *attrhash;
 
-/* Shallow copy of an attribute
- * Though, not so shallow that it doesn't copy the contents
- * of the attr_extra pointed to by 'extra'
- */
-void bgp_attr_dup(struct attr *new, struct attr *orig)
-{
-       *new = *orig;
-}
-
 unsigned long int attr_count(void)
 {
        return attrhash->count;
@@ -860,7 +853,7 @@ void bgp_attr_unintern_sub(struct attr *attr)
        UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST));
 
        if (attr->transit)
-               transit_unintern(attr->transit);
+               transit_unintern(&attr->transit);
 
        if (attr->encap_subtlvs)
                encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
@@ -2143,8 +2136,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
  * Read an individual SID value returning how much data we have read
  * Returns 0 if there was an error that needs to be passed up the stack
  */
-static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
-                                             int32_t length,
+static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
                                              struct bgp_attr_parser_args *args,
                                              struct bgp_nlri *mp_update)
 {
@@ -2157,11 +2149,12 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
        int srgb_count;
 
        if (type == BGP_PREFIX_SID_LABEL_INDEX) {
-               if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
-                       flog_err(
-                               EC_BGP_ATTR_LEN,
-                               "Prefix SID label index length is %d instead of %d",
-                               length, BGP_PREFIX_SID_LABEL_INDEX_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",
+                                length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
                        return bgp_attr_malformed(args,
                                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                                  args->total);
@@ -2193,9 +2186,11 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
 
        /* Placeholder code for the IPv6 SID type */
        else if (type == BGP_PREFIX_SID_IPV6) {
-               if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
+               if (STREAM_READABLE(peer->curr) < length
+                   || length != BGP_PREFIX_SID_IPV6_LENGTH) {
                        flog_err(EC_BGP_ATTR_LEN,
-                                "Prefix SID IPv6 length is %d instead of %d",
+                                "Prefix SID IPv6 length is %" PRIu16
+                                " instead of %u",
                                 length, BGP_PREFIX_SID_IPV6_LENGTH);
                        return bgp_attr_malformed(args,
                                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
@@ -2211,15 +2206,54 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
 
        /* Placeholder code for the Originator SRGB type */
        else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
-               /* Ignore flags */
-               stream_getw(peer->curr);
+               /*
+                * ietf-idr-bgp-prefix-sid-05:
+                *     Length is the total length of the value portion of the
+                *     TLV: 2 + multiple of 6.
+                *
+                * peer->curr stream readp should be at the beginning of the 16
+                * bit flag field at this point in the code.
+                */
 
-               length -= 2;
+               /*
+                * Check that the TLV length field is sane: at least 2 bytes of
+                * flag, and at least 1 SRGB (these are 6 bytes each)
+                */
+               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",
+                               length,
+                               2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
+
+               /*
+                * Check that we actually have at least as much data as
+                * specified by the length field
+                */
+               if (STREAM_READABLE(peer->curr) < length) {
+                       flog_err(EC_BGP_ATTR_LEN,
+                                "Prefix SID Originator SRGB specifies length %" PRIu16 ", but only %zu bytes remain",
+                                length, STREAM_READABLE(peer->curr));
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
 
+               /*
+                * Check that the portion of the TLV containing the sequence of
+                * SRGBs corresponds to a multiple of the SRGB size; to get
+                * that length, we skip the 16 bit flags field
+                */
+               stream_getw(peer->curr);
+               length -= 2;
                if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) {
                        flog_err(
                                EC_BGP_ATTR_LEN,
-                               "Prefix SID Originator SRGB length is %d, it must be a multiple of %d ",
+                               "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %" PRIu16 "bytes, 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,
@@ -2241,12 +2275,24 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
         */
        else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE
            || type == BGP_PREFIX_SID_SRV6_L2_SERVICE) {
+
+               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",
+                               length, STREAM_READABLE(peer->curr));
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
+
                if (bgp_debug_update(peer, NULL, NULL, 1))
                        zlog_debug(
                                "%s attr Prefix-SID sub-type=%u is not supported, skipped",
                                peer->host, type);
-               for (int i = 0; i < length; i++)
-                       stream_getc(peer->curr);
+
+               stream_forward_getp(peer->curr, length);
        }
 
        return BGP_ATTR_PARSE_PROCEED;
@@ -2255,9 +2301,8 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
 /* Prefix SID attribute
  * draft-ietf-idr-bgp-prefix-sid-05
  */
-bgp_attr_parse_ret_t
-bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
-                   struct bgp_nlri *mp_update)
+bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
+                                        struct bgp_nlri *mp_update)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
@@ -2265,31 +2310,40 @@ bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
 
-       while (tlength) {
-               int32_t type, length;
+       uint8_t type;
+       uint16_t length;
+       size_t headersz = sizeof(type) + sizeof(length);
 
-               type = stream_getc(peer->curr);
-               length = stream_getw(peer->curr);
+       while (STREAM_READABLE(peer->curr) > 0) {
 
-               ret = bgp_attr_psid_sub(type, length, args, mp_update);
+               if (STREAM_READABLE(peer->curr) < headersz) {
+                       flog_err(
+                               EC_BGP_ATTR_LEN,
+                               "Malformed Prefix SID attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+                               headersz, STREAM_READABLE(peer->curr));
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
 
-               if (ret != BGP_ATTR_PARSE_PROCEED)
-                       return ret;
-               /*
-                * Subtract length + the T and the L
-                * since length is the Vector portion
-                */
-               tlength -= length + 3;
+               type = stream_getc(peer->curr);
+               length = stream_getw(peer->curr);
 
-               if (tlength < 0) {
+               if (STREAM_READABLE(peer->curr) < length) {
                        flog_err(
                                EC_BGP_ATTR_LEN,
-                               "Prefix SID internal length %d causes us to read beyond the total Prefix SID length",
-                               length);
+                               "Malformed Prefix SID attribute - insufficient data (need %" PRIu8
+                               " for attribute body, have %zu remaining in UPDATE)",
+                               length, STREAM_READABLE(peer->curr));
                        return bgp_attr_malformed(args,
                                                  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
                                                  args->total);
                }
+
+               ret = bgp_attr_psid_sub(type, length, args, mp_update);
+
+               if (ret != BGP_ATTR_PARSE_PROCEED)
+                       return ret;
        }
 
        return BGP_ATTR_PARSE_PROCEED;
@@ -2492,7 +2546,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
 
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
 
                /* Fetch attribute flag and type. */
@@ -2515,7 +2570,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
 
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
 
                /* Check extended attribue length bit. */
@@ -2536,7 +2592,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
 
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_MAL_ATTR);
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
 
                /* Set type to bitmap to check duplicate attribute.  `type' is
@@ -2593,7 +2650,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata,
                                ndl + lfl + 1);
 
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
 
                struct bgp_attr_parser_args attr_args = {
@@ -2618,7 +2676,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                attr_args.total);
                        if (ret == BGP_ATTR_PARSE_PROCEED)
                                continue;
-                       return ret;
+                       goto done;
                }
 
                /* OK check attribute and store it's value. */
@@ -2681,8 +2739,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                             startp);
                        break;
                case BGP_ATTR_PREFIX_SID:
-                       ret = bgp_attr_prefix_sid(length,
-                                                 &attr_args, mp_update);
+                       ret = bgp_attr_prefix_sid(&attr_args, mp_update);
                        break;
                case BGP_ATTR_PMSI_TUNNEL:
                        ret = bgp_attr_pmsi_tunnel(&attr_args);
@@ -2696,32 +2753,25 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_MAL_ATTR);
                        ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
 
                if (ret == BGP_ATTR_PARSE_EOR) {
-                       if (as4_path)
-                               aspath_unintern(&as4_path);
-                       return ret;
+                       goto done;
                }
 
-               /* If hard error occurred immediately return to the caller. */
                if (ret == BGP_ATTR_PARSE_ERROR) {
                        flog_warn(EC_BGP_ATTRIBUTE_PARSE_ERROR,
                                  "%s: Attribute %s, parse error", peer->host,
                                  lookup_msg(attr_str, type, NULL));
-                       if (as4_path)
-                               aspath_unintern(&as4_path);
-                       return ret;
+                       goto done;
                }
                if (ret == BGP_ATTR_PARSE_WITHDRAW) {
-
                        flog_warn(
                                EC_BGP_ATTRIBUTE_PARSE_WITHDRAW,
                                "%s: Attribute %s, parse error - treating as withdrawal",
                                peer->host, lookup_msg(attr_str, type, NULL));
-                       if (as4_path)
-                               aspath_unintern(&as4_path);
-                       return ret;
+                       goto done;
                }
 
                /* Check the fetched length. */
@@ -2731,9 +2781,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                  peer->host, lookup_msg(attr_str, type, NULL));
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
-                       if (as4_path)
-                               aspath_unintern(&as4_path);
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
        }
 
@@ -2744,9 +2793,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                          lookup_msg(attr_str, type, NULL));
                bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
-               if (as4_path)
-                       aspath_unintern(&as4_path);
-               return BGP_ATTR_PARSE_ERROR;
+
+               ret = BGP_ATTR_PARSE_ERROR;
+               goto done;
        }
 
        /*
@@ -2765,16 +2814,14 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
        if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))
            && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) {
                if (bgp_attr_nexthop_valid(peer, attr) < 0) {
-                       return BGP_ATTR_PARSE_ERROR;
+                       ret = BGP_ATTR_PARSE_ERROR;
+                       goto done;
                }
        }
 
        /* Check all mandatory well-known attributes are present */
-       if ((ret = bgp_attr_check(peer, attr)) < 0) {
-               if (as4_path)
-                       aspath_unintern(&as4_path);
-               return ret;
-       }
+       if ((ret = bgp_attr_check(peer, attr)) < 0)
+               goto done;
 
        /*
         * At this place we can see whether we got AS4_PATH and/or
@@ -2797,28 +2844,10 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                        &as4_aggregator_addr)) {
                bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                BGP_NOTIFY_UPDATE_MAL_ATTR);
-               if (as4_path)
-                       aspath_unintern(&as4_path);
-               return BGP_ATTR_PARSE_ERROR;
+               ret = BGP_ATTR_PARSE_ERROR;
+               goto done;
        }
 
-       /* At this stage, we have done all fiddling with as4, and the
-        * resulting info is in attr->aggregator resp. attr->aspath
-        * so we can chuck as4_aggregator and as4_path alltogether in
-        * order to save memory
-        */
-       if (as4_path) {
-               aspath_unintern(&as4_path); /* unintern - it is in the hash */
-               /* The flag that we got this is still there, but that does not
-                * do any trouble
-                */
-       }
-       /*
-        * The "rest" of the code does nothing with as4_aggregator.
-        * there is no memory attached specifically which is not part
-        * of the attr.
-        * so ignoring just means do nothing.
-        */
        /*
         * Finally do the checks on the aspath we did not do yet
         * because we waited for a potentially synthesized aspath.
@@ -2826,21 +2855,59 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
        if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) {
                ret = bgp_attr_aspath_check(peer, attr);
                if (ret != BGP_ATTR_PARSE_PROCEED)
-                       return ret;
+                       goto done;
+       }
+
+       ret = BGP_ATTR_PARSE_PROCEED;
+done:
+
+       /*
+        * At this stage, we have done all fiddling with as4, and the
+        * resulting info is in attr->aggregator resp. attr->aspath so
+        * we can chuck as4_aggregator and as4_path alltogether in order
+        * to save memory
+        */
+       if (as4_path) {
+               /*
+                * unintern - it is in the hash
+                * The flag that we got this is still there, but that
+                * does not do any trouble
+                */
+               aspath_unintern(&as4_path);
        }
-       /* Finally intern unknown attribute. */
+
+       if (ret != BGP_ATTR_PARSE_ERROR) {
+               /* Finally intern unknown attribute. */
+               if (attr->transit)
+                       attr->transit = transit_intern(attr->transit);
+               if (attr->encap_subtlvs)
+                       attr->encap_subtlvs = encap_intern(attr->encap_subtlvs,
+                                                          ENCAP_SUBTLV_TYPE);
+#if ENABLE_BGP_VNC
+               if (attr->vnc_subtlvs)
+                       attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs,
+                                                        VNC_SUBTLV_TYPE);
+#endif
+       } else {
+               if (attr->transit) {
+                       transit_free(attr->transit);
+                       attr->transit = NULL;
+               }
+
+               bgp_attr_flush_encap(attr);
+       };
+
+       /* Sanity checks */
        if (attr->transit)
-               attr->transit = transit_intern(attr->transit);
+               assert(attr->transit->refcnt > 0);
        if (attr->encap_subtlvs)
-               attr->encap_subtlvs =
-                       encap_intern(attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+               assert(attr->encap_subtlvs->refcnt > 0);
 #if ENABLE_BGP_VNC
        if (attr->vnc_subtlvs)
-               attr->vnc_subtlvs =
-                       encap_intern(attr->vnc_subtlvs, VNC_SUBTLV_TYPE);
+               assert(attr->vnc_subtlvs->refcnt > 0);
 #endif
 
-       return BGP_ATTR_PARSE_PROCEED;
+       return ret;
 }
 
 /*
@@ -2899,7 +2966,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
 
        /* Nexthop AFI */
        if (afi == AFI_IP
-           && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
+           && (safi == SAFI_UNICAST ||
+               safi == SAFI_LABELED_UNICAST ||
+               safi == SAFI_MULTICAST))
                nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP;
        else
                nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len);
index 375a2272e1d0f85adb61d3ae69c058c525e80d65..8bd527c0ff169da64cd75b3139dd271fad8fac06 100644 (file)
@@ -272,7 +272,6 @@ extern void bgp_attr_finish(void);
 extern bgp_attr_parse_ret_t bgp_attr_parse(struct peer *, struct attr *,
                                           bgp_size_t, struct bgp_nlri *,
                                           struct bgp_nlri *);
-extern void bgp_attr_dup(struct attr *, struct attr *);
 extern void bgp_attr_undup(struct attr *new, struct attr *old);
 extern struct attr *bgp_attr_intern(struct attr *attr);
 extern void bgp_attr_unintern_sub(struct attr *);
@@ -305,9 +304,6 @@ extern unsigned long int attr_unknown_count(void);
 extern int cluster_loop_check(struct cluster_list *, struct in_addr);
 extern void cluster_unintern(struct cluster_list *);
 
-/* Transit attribute prototypes. */
-void transit_unintern(struct transit *);
-
 /* Below exported for unit-test purposes only */
 struct bgp_attr_parser_args {
        struct peer *peer;
@@ -323,7 +319,7 @@ extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
 extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
                                struct bgp_nlri *);
 extern bgp_attr_parse_ret_t
-bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
+bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
                    struct bgp_nlri *mp_update);
 
 extern struct bgp_attr_encap_subtlv *
index 9f1fe648135c24c984df3b15ab48902aba2d489f..24a9cab5d150f4dfe56963d9739c9b6f699118b8 100644 (file)
@@ -1662,18 +1662,23 @@ static void bmp_active_connect(struct bmp_active *ba)
        bmp_active_setup(ba);
 }
 
-static void bmp_active_resolved(struct resolver_query *resq, int numaddrs,
-                               union sockunion *addr)
+static void bmp_active_resolved(struct resolver_query *resq, const char *errstr,
+                               int numaddrs, union sockunion *addr)
 {
        struct bmp_active *ba = container_of(resq, struct bmp_active, resq);
        unsigned i;
 
        if (numaddrs <= 0) {
-               zlog_warn("bmp[%s]: hostname resolution failed", ba->hostname);
+               zlog_warn("bmp[%s]: hostname resolution failed: %s",
+                         ba->hostname, errstr);
+               ba->last_err = errstr;
                ba->curretry += ba->curretry / 2;
+               ba->addrpos = 0;
+               ba->addrtotal = 0;
                bmp_active_setup(ba);
                return;
        }
+
        if (numaddrs > (int)array_size(ba->addrs))
                numaddrs = array_size(ba->addrs);
 
@@ -1698,6 +1703,8 @@ static int bmp_active_thread(struct thread *t)
        THREAD_OFF(ba->t_read);
        THREAD_OFF(ba->t_write);
 
+       ba->last_err = NULL;
+
        if (ba->socket == -1) {
                resolver_resolve(&ba->resq, AF_UNSPEC, ba->hostname,
                                 bmp_active_resolved);
@@ -1710,8 +1717,9 @@ static int bmp_active_thread(struct thread *t)
 
        sockunion2str(&ba->addrs[ba->addrpos], buf, sizeof(buf));
        if (ret < 0 || status != 0) {
-               zlog_warn("bmp[%s]: failed to connect to %s:%d",
-                         ba->hostname, buf, ba->port);
+               ba->last_err = strerror(status);
+               zlog_warn("bmp[%s]: failed to connect to %s:%d: %s",
+                         ba->hostname, buf, ba->port, ba->last_err);
                goto out_next;
        }
 
@@ -2071,9 +2079,12 @@ DEFPY(show_bmp,
        struct bmp_bgp *bmpbgp;
        struct bmp_targets *bt;
        struct bmp_listener *bl;
+       struct bmp_active *ba;
        struct bmp *bmp;
        struct ttable *tt;
        char buf[SU_ADDRSTRLEN];
+       char uptime[BGP_UPTIME_LEN];
+       char *out;
 
        frr_each(bmp_bgph, &bmp_bgph, bmpbgp) {
                vty_out(vty, "BMP state for BGP %s:\n\n",
@@ -2122,6 +2133,51 @@ DEFPY(show_bmp,
                                        sockunion2str(&bl->addr, buf,
                                                      SU_ADDRSTRLEN), bl->port);
 
+                       vty_out(vty, "\n    Outbound connections:\n");
+                       tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+                       ttable_add_row(tt, "remote|state||timer");
+                       ttable_rowseps(tt, 0, BOTTOM, true, '-');
+                       frr_each (bmp_actives, &bt->actives, ba) {
+                               const char *state_str = "?";
+
+                               if (ba->bmp) {
+                                       peer_uptime(ba->bmp->t_up.tv_sec,
+                                                   uptime, sizeof(uptime),
+                                                   false, NULL);
+                                       ttable_add_row(tt, "%s:%d|Up|%s|%s",
+                                                      ba->hostname, ba->port,
+                                                      ba->bmp->remote, uptime);
+                                       continue;
+                               }
+
+                               uptime[0] = '\0';
+
+                               if (ba->t_timer) {
+                                       long trem = thread_timer_remain_second(
+                                               ba->t_timer);
+
+                                       peer_uptime(monotime(NULL) - trem,
+                                                   uptime, sizeof(uptime),
+                                                   false, NULL);
+                                       state_str = "RetryWait";
+                               } else if (ba->t_read) {
+                                       state_str = "Connecting";
+                               } else if (ba->resq.callback) {
+                                       state_str = "Resolving";
+                               }
+
+                               ttable_add_row(tt, "%s:%d|%s|%s|%s",
+                                              ba->hostname, ba->port,
+                                              state_str,
+                                              ba->last_err ? ba->last_err : "",
+                                              uptime);
+                               continue;
+                       }
+                       out = ttable_dump(tt, "\n");
+                       vty_out(vty, "%s", out);
+                       XFREE(MTYPE_TMP, out);
+                       ttable_del(tt);
+
                        vty_out(vty, "\n    %zu connected clients:\n",
                                        bmp_session_count(&bt->sessions));
                        tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
@@ -2134,14 +2190,17 @@ DEFPY(show_bmp,
 
                                pullwr_stats(bmp->pullwr, &total, &q, &kq);
 
-                               ttable_add_row(tt, "%s|-|%Lu|%Lu|%Lu|%Lu|%zu|%zu",
-                                              bmp->remote,
+                               peer_uptime(bmp->t_up.tv_sec, uptime,
+                                           sizeof(uptime), false, NULL);
+
+                               ttable_add_row(tt, "%s|%s|%Lu|%Lu|%Lu|%Lu|%zu|%zu",
+                                              bmp->remote, uptime,
                                               bmp->cnt_update,
                                               bmp->cnt_mirror,
                                               bmp->cnt_mirror_overruns,
                                               total, q, kq);
                        }
-                       char *out = ttable_dump(tt, "\n");
+                       out = ttable_dump(tt, "\n");
                        vty_out(vty, "%s", out);
                        XFREE(MTYPE_TMP, out);
                        ttable_del(tt);
index 9d270e808c1db237f06a30f17a6ec53d4bdc22da..94a17f70da2071a1ede105b793a7910246d6d863 100644 (file)
@@ -182,6 +182,7 @@ struct bmp_active {
        unsigned addrpos, addrtotal;
        union sockunion addrs[8];
        int socket;
+       const char *last_err;
        struct thread *t_timer, *t_read, *t_write;
 };
 
index 48b714c7df4cdab8294fc3329cd2089511c671b1..79a8fae530a005e74d562543af73627c2dbba19f 100644 (file)
@@ -49,6 +49,7 @@
 #include "bgpd/bgp_nexthop.h"
 #include "bgpd/bgp_addpath.h"
 #include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_vty.h"
 
 /*
  * Definitions and external declarations.
@@ -1571,7 +1572,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
         * present, else treat as locally originated.
         */
        if (src_attr)
-               bgp_attr_dup(&attr, src_attr);
+               attr = *src_attr;
        else {
                memset(&attr, 0, sizeof(struct attr));
                bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
@@ -2661,7 +2662,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
         * address for the rest of the code to flow through. In the case of IPv4,
         * make sure to set the flag for next hop attribute.
         */
-       bgp_attr_dup(&attr, parent_pi->attr);
+       attr = *parent_pi->attr;
        if (afi == AFI_IP6)
                evpn_convert_nexthop_to_ipv6(&attr);
        else
@@ -5099,7 +5100,8 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
                                return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
-                       addpath_id = ntohl(*((uint32_t *)pnt));
+                       memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+                       addpath_id = ntohl(addpath_id);
                        pnt += BGP_ADDPATH_ID_LEN;
                }
 
@@ -5709,9 +5711,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,
 
                int ret = 0;
 
-               ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
-                             vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT
-                                                   : BGP_INSTANCE_TYPE_VRF);
+               ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
+                                 vrf_id == VRF_DEFAULT
+                                 ? BGP_INSTANCE_TYPE_DEFAULT
+                                 : BGP_INSTANCE_TYPE_VRF);
                switch (ret) {
                case BGP_ERR_AS_MISMATCH:
                        flog_err(EC_BGP_EVPN_AS_MISMATCH,
index d316a28dcb80d1f93aaa1c36a7e38f13b581a2ea..3049a00ce3205822958d66b8738de902c7e547c2 100644 (file)
@@ -5620,19 +5620,24 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
                if (!bgp->evpn_info->advertise_pip)
                        vty_out(vty, "  no advertise-pip\n");
                if (bgp->evpn_info->advertise_pip) {
-                       if (bgp->evpn_info->pip_ip_static.s_addr != INADDR_ANY)
+                       if (bgp->evpn_info->pip_ip_static.s_addr
+                           != INADDR_ANY) {
                                vty_out(vty, "  advertise-pip ip %s",
                                        inet_ntop(AF_INET,
                                        &bgp->evpn_info->pip_ip_static,
                                        buf2, INET_ADDRSTRLEN));
-                       if (!is_zero_mac(&(bgp->evpn_info->pip_rmac_static))) {
-                               char buf[ETHER_ADDR_STRLEN];
-
-                               vty_out(vty, " mac %s",
-                               prefix_mac2str(&bgp->evpn_info->pip_rmac,
-                                                      buf, sizeof(buf)));
+                               if (!is_zero_mac(&(
+                                           bgp->evpn_info->pip_rmac_static))) {
+                                       char buf[ETHER_ADDR_STRLEN];
+
+                                       vty_out(vty, " mac %s",
+                                               prefix_mac2str(
+                                                       &bgp->evpn_info
+                                                                ->pip_rmac,
+                                                       buf, sizeof(buf)));
+                               }
+                               vty_out(vty, "\n");
                        }
-                       vty_out(vty, "\n");
                }
        }
        if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD))
index 6460ff76fe6c28bb960e94a684922ac818d4ff11..3667dae83d164df70054b335843b00fb08e4416f 100644 (file)
@@ -164,6 +164,14 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
        bgp_writes_off(from_peer);
        bgp_reads_off(from_peer);
 
+       /*
+        * Before exchanging FD remove doppelganger from
+        * keepalive peer hash. It could be possible conf peer
+        * fd is set to -1. If blocked on lock then keepalive
+        * thread can access peer pointer with fd -1.
+        */
+       bgp_keepalives_off(from_peer);
+
        BGP_TIMER_OFF(peer->t_routeadv);
        BGP_TIMER_OFF(peer->t_connect);
        BGP_TIMER_OFF(peer->t_connect_check_r);
@@ -1879,7 +1887,7 @@ static const struct {
                {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message         */
                {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message    */
                {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message       */
-               {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+               {bgp_fsm_event_error, Idle}, /* Receive_NOTIFICATION_message */
                {bgp_fsm_exeption, Idle},    /* Clearing_Completed           */
        },
        {
index 489ac6ea9f86d724e7191d556ad55184f5985a72..ff1ab1a37d9e3e5a0fa3f8204c0e90b115aae2a5 100644 (file)
@@ -368,7 +368,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
                                return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
-                       addpath_id = ntohl(*((uint32_t *)pnt));
+                       memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+                       addpath_id = ntohl(addpath_id);
                        pnt += BGP_ADDPATH_ID_LEN;
                }
 
index 61c7b4080c8600790769a6283c6d889e817753ae..537bb45455e8c2357485fc88f4c22389a4789436 100644 (file)
@@ -284,11 +284,13 @@ static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname,
        }
 
        if (bsm->ifp_list->count == 0) {
+               struct ethaddr mac = *macaddr;
+
                hash_release(bm->self_mac_hash, bsm);
                list_delete(&bsm->ifp_list);
                XFREE(MTYPE_BSM, bsm);
 
-               bgp_mac_rescan_all_evpn_tables(macaddr);
+               bgp_mac_rescan_all_evpn_tables(&mac);
        }
 }
 
index 08c5d3468d422ed64935fa29431f7e950e6a15e9..9cb3957a86c2ff24195554f02121941509afef7d 100644 (file)
@@ -27,7 +27,6 @@
 #include "thread.h"
 #include <lib/version.h>
 #include "memory.h"
-#include "memory_vty.h"
 #include "prefix.h"
 #include "log.h"
 #include "privs.h"
index d37bf5473466b3e1fbcd923b15d3c3eda59f22b4..77448ec15d3477375149fbb5f717497b55f65701 100644 (file)
@@ -720,7 +720,7 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best,
                return;
        }
 
-       bgp_attr_dup(&attr, new_best->attr);
+       attr = *new_best->attr;
 
        if (new_best->peer && bgp_flag_check(new_best->peer->bgp,
                                             BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
index 1d153614161007298daead22171370cf30f26eda..86c04b71f07429b0eea345a2a913525fa8ab3ebf 100644 (file)
@@ -142,7 +142,8 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
                        if (pnt + BGP_ADDPATH_ID_LEN > lim)
                                return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
-                       addpath_id = ntohl(*((uint32_t *)pnt));
+                       memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+                       addpath_id = ntohl(addpath_id);
                        pnt += BGP_ADDPATH_ID_LEN;
                }
 
@@ -699,7 +700,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn,      /* to */
                return;
        }
 
-       bgp_attr_dup(&static_attr, path_vrf->attr); /* shallow copy */
+       /* shallow copy */
+       static_attr = *path_vrf->attr;
 
        /*
         * route map handling
@@ -1082,7 +1084,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf,            /* to */
                                buf_prefix, bgp_vrf->name_pretty);
        }
 
-       bgp_attr_dup(&static_attr, path_vpn->attr); /* shallow copy */
+       /* shallow copy */
+       static_attr = *path_vpn->attr;
 
        /*
         * Nexthop: stash and clear
index 0969c8e77e38db684937669074e870d73f9afd18..a50fc7d69726c61ecd9d31c5ba27c448238bc31a 100644 (file)
@@ -421,7 +421,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                        if (peer && !peer->ifp
                            && CHECK_FLAG(peer->flags,
                                          PEER_FLAG_CAPABILITY_ENHE)
-                           && nhr.prefix.family == AF_INET6) {
+                           && nhr.prefix.family == AF_INET6
+                           && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) {
                                struct interface *ifp;
 
                                ifp = if_lookup_by_index(nexthop->ifindex,
index f17bc7b8c06e7de9d3844be140bd19bf5df1ec14..23b893c1c87e1e8c2bbc240e5c31895f26159d4e 100644 (file)
@@ -747,6 +747,12 @@ static int bgp_capability_hostname(struct peer *peer,
 
        if (len) {
                str[len] = '\0';
+
+               if (peer->domainname != NULL) {
+                       XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+                       peer->domainname = NULL;
+               }
+
                peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
        }
 
index 5296246b31854bdc09369075351d55726cb3d243..b7e9af002b40294084bd6f56627a928a38034b4e 100644 (file)
@@ -1658,10 +1658,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
                        if (peer->nsf[afi][safi])
                                bgp_clear_stale_route(peer, afi, safi);
 
-                       zlog_info("%%NOTIFICATION: rcvd End-of-RIB for %s from %s in vrf %s",
-                                 get_afi_safi_str(afi, safi, false), peer->host,
-                                 vrf ? vrf->name : VRF_DEFAULT_NAME);
-               }
+                        zlog_info(
+                            "%s: rcvd End-of-RIB for %s from %s in vrf %s",
+                            __func__, get_afi_safi_str(afi, safi, false),
+                            peer->host, vrf ? vrf->name : VRF_DEFAULT_NAME);
+                }
        }
 
        /* Everything is done.  We unintern temporary structures which
index 83194e010a4e5d5a14ceaa5688e3b9dad0ebcdb3..14f5fefb2020955cf544a93448bd857a8e1e3c62 100644 (file)
@@ -1725,7 +1725,7 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit(
                        temp.type = IPSET_NET_NET;
        }
        if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
-               temp.vrf_id = 0;
+               temp.vrf_id = VRF_DEFAULT;
        else
                temp.vrf_id = bpf->vrf_id;
        bpme = &temp2;
index 571139a49aba0224ecdb521db6d82dc7f0120bcf..be950dfa51659a593c626d48e0e02f53b41011b5 100644 (file)
@@ -174,15 +174,16 @@ char *prefix_rd2str(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:%d", rd_as.as, rd_as.val);
+               snprintf(buf, size, "%u:%" PRIu32, rd_as.as, rd_as.val);
                return buf;
        } else if (type == RD_TYPE_AS4) {
                decode_rd_as4(pnt + 2, &rd_as);
-               snprintf(buf, size, "%u:%d", rd_as.as, rd_as.val);
+               snprintf(buf, size, "%u:%" PRIu32, rd_as.as, rd_as.val);
                return buf;
        } else if (type == RD_TYPE_IP) {
                decode_rd_ip(pnt + 2, &rd_ip);
-               snprintf(buf, size, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val);
+               snprintf(buf, size, "%s:%" PRIu16, inet_ntoa(rd_ip.ip),
+                        rd_ip.val);
                return buf;
        }
 #if ENABLE_BGP_VNC
index ae70ea0690f2f00e207de98b88349920c9685351..5f4486b8004252115eed66fa33771999d78f33ca 100644 (file)
@@ -1695,7 +1695,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
        }
 
        /* For modify attribute, copy it to temporary structure. */
-       bgp_attr_dup(attr, piattr);
+       *attr = *piattr;
 
        /* If local-preference is not set. */
        if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED)
@@ -1815,7 +1815,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
                if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
                    && !bgp_flag_check(bgp,
                                       BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
-                       bgp_attr_dup(&dummy_attr, attr);
+                       dummy_attr = *attr;
                        rmap_path.attr = &dummy_attr;
                }
 
@@ -3178,7 +3178,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                        goto filtered;
                }
 
-       bgp_attr_dup(&new_attr, attr);
+       new_attr = *attr;
 
        /* Apply incoming route-map.
         * NB: new_attr may now contain newly allocated values from route-map
@@ -4357,6 +4357,9 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
 
 int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 {
+       if (peer->sort == BGP_PEER_IBGP)
+               return 1;
+
        if (peer->sort == BGP_PEER_EBGP
            && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter)
                || FILTER_LIST_OUT_NAME(filter)
@@ -4367,6 +4370,9 @@ int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 
 int bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 {
+       if (peer->sort == BGP_PEER_IBGP)
+               return 1;
+
        if (peer->sort == BGP_PEER_EBGP
            && (ROUTE_MAP_IN_NAME(filter) || PREFIX_LIST_IN_NAME(filter)
                || FILTER_LIST_IN_NAME(filter)
@@ -4509,7 +4515,8 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                        if (pnt + BGP_ADDPATH_ID_LEN >= lim)
                                return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
-                       addpath_id = ntohl(*((uint32_t *)pnt));
+                       memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+                       addpath_id = ntohl(addpath_id);
                        pnt += BGP_ADDPATH_ID_LEN;
                }
 
@@ -6829,7 +6836,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
                struct attr attr_new;
 
                /* Copy attribute for modification. */
-               bgp_attr_dup(&attr_new, &attr);
+               attr_new = attr;
 
                if (red->redist_metric_flag)
                        attr_new.med = red->redist_metric;
@@ -7148,9 +7155,10 @@ static void route_vty_short_status_out(struct vty *vty,
                vty_out(vty, " ");
 }
 
-static char *bgp_nexthop_fqdn(struct peer *peer)
+static char *bgp_nexthop_hostname(struct peer *peer, struct attr *attr)
 {
-       if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME))
+       if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)
+           && !(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))
                return peer->hostname;
        return NULL;
 }
@@ -7160,7 +7168,7 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                   struct bgp_path_info *path, int display, safi_t safi,
                   json_object *json_paths)
 {
-       struct attr *attr;
+       struct attr *attr = path->attr;
        json_object *json_path = NULL;
        json_object *json_nexthops = NULL;
        json_object *json_nexthop_global = NULL;
@@ -7172,7 +7180,7 @@ void route_vty_out(struct vty *vty, struct prefix *p,
        bool nexthop_othervrf = false;
        vrf_id_t nexthop_vrfid = VRF_DEFAULT;
        const char *nexthop_vrfname = VRF_DEFAULT_NAME;
-       char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
+       char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr);
 
        if (json_paths)
                json_path = json_object_new_object();
@@ -7190,9 +7198,6 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                route_vty_out_route(p, vty, json_path);
        }
 
-       /* Print attribute */
-       attr = path->attr;
-
        /*
         * If vrf id of nexthop is different from that of prefix,
         * set up printable string to append
@@ -7260,57 +7265,67 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(
-                               json_nexthop_global, "afi",
-                               nexthop_fqdn ? "fqdn"
-                                            : (af == AF_INET) ? "ip" : "ipv6");
-                       json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn"
-                                            : (af == AF_INET) ? "ip" : "ipv6",
-                               nexthop_fqdn ? nexthop_fqdn : nexthop);
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              nexthop);
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              (af == AF_INET) ? "ipv4"
+                                                              : "ipv6");
                        json_object_boolean_true_add(json_nexthop_global,
                                                     "used");
                } else
                        vty_out(vty, "%s%s",
-                               nexthop_fqdn ? nexthop_fqdn : nexthop,
+                               nexthop_hostname ? nexthop_hostname : nexthop,
                                vrf_id_str);
        } else if (safi == SAFI_EVPN) {
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn ? nexthop_fqdn
-                                            : inet_ntoa(attr->nexthop));
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv4");
                        json_object_boolean_true_add(json_nexthop_global,
                                                     "used");
                } else
                        vty_out(vty, "%-16s%s",
-                               nexthop_fqdn ?: inet_ntoa(attr->nexthop),
+                               nexthop_hostname ? nexthop_hostname
+                                                : inet_ntoa(attr->nexthop),
                                vrf_id_str);
        } else if (safi == SAFI_FLOWSPEC) {
                if (attr->nexthop.s_addr != 0) {
                        if (json_paths) {
                                json_nexthop_global = json_object_new_object();
-                               json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(attr->nexthop));
+
                                json_object_string_add(json_nexthop_global,
                                                       "afi", "ipv4");
+                               json_object_string_add(
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+
                                json_object_boolean_true_add(
                                                        json_nexthop_global,
                                                             "used");
                        } else {
                                vty_out(vty, "%-16s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(attr->nexthop));
                        }
                }
@@ -7318,11 +7333,13 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(json_nexthop_global,
-                                              nexthop_fqdn ? "fqdn" : "ip",
-                                              nexthop_fqdn
-                                              ? nexthop_fqdn
-                                              : inet_ntoa(attr->nexthop));
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
 
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv4");
@@ -7332,8 +7349,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                        char buf[BUFSIZ];
 
                        snprintf(buf, sizeof(buf), "%s%s",
-                                nexthop_fqdn ? nexthop_fqdn
-                                             : inet_ntoa(attr->nexthop),
+                                nexthop_hostname ? nexthop_hostname
+                                                 : inet_ntoa(attr->nexthop),
                                 vrf_id_str);
                        vty_out(vty, "%-16s", buf);
                }
@@ -7347,13 +7364,15 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
                        json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_global,
-                                                   buf, BUFSIZ));
+                               json_nexthop_global, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, BUFSIZ));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv6");
                        json_object_string_add(json_nexthop_global, "scope",
@@ -7366,14 +7385,16 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                            || (path->peer->conf_if)) {
                                json_nexthop_ll = json_object_new_object();
                                json_object_string_add(
-                                       json_nexthop_ll,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntop(
-                                                         AF_INET6,
-                                                         &attr->mp_nexthop_local,
-                                                         buf, BUFSIZ));
+                                       json_nexthop_ll, "ip",
+                                       inet_ntop(AF_INET6,
+                                                 &attr->mp_nexthop_local, buf,
+                                                 BUFSIZ));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_ll, "hostname",
+                                               nexthop_hostname);
+
                                json_object_string_add(json_nexthop_ll, "afi",
                                                       "ipv6");
                                json_object_string_add(json_nexthop_ll, "scope",
@@ -7413,8 +7434,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                                } else {
                                        len = vty_out(
                                                vty, "%s%s",
-                                               nexthop_fqdn
-                                                       ? nexthop_fqdn
+                                               nexthop_hostname
+                                                       ? nexthop_hostname
                                                        : inet_ntop(
                                                                  AF_INET6,
                                                                  &attr->mp_nexthop_local,
@@ -7430,8 +7451,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                        } else {
                                len = vty_out(
                                        vty, "%s%s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntop(
                                                          AF_INET6,
                                                          &attr->mp_nexthop_global,
@@ -8262,7 +8283,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        char buf[INET6_ADDRSTRLEN];
        char buf1[BUFSIZ];
        char buf2[EVPN_ROUTE_STRLEN];
-       struct attr *attr;
+       struct attr *attr = path->attr;
        int sockunion_vty_out(struct vty *, union sockunion *);
        time_t tbuf;
        json_object *json_bestpath = NULL;
@@ -8287,7 +8308,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        bool nexthop_self =
                CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
        int i;
-       char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
+       char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr);
 
        if (json_paths) {
                json_path = json_object_new_object();
@@ -8340,8 +8361,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                }
        }
 
-       attr = path->attr;
-
        /* Line1 display AS-path, Aggregator */
        if (attr->aspath) {
                if (json_paths) {
@@ -8429,32 +8448,35 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
                if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
                    || safi == SAFI_EVPN) {
-                       if (json_paths)
+                       if (json_paths) {
                                json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(
-                                                       attr->mp_nexthop_global_in));
-                       else
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->mp_nexthop_global_in));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+                       } else
                                vty_out(vty, "    %s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(
-                                                       attr->mp_nexthop_global_in));
+                                                         attr->mp_nexthop_global_in));
                } else {
-                       if (json_paths)
+                       if (json_paths) {
                                json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(attr->nexthop));
-                       else
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+                       } else
                                vty_out(vty, "    %s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(attr->nexthop));
                }
 
@@ -8464,21 +8486,23 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        } else {
                if (json_paths) {
                        json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_global,
-                                                   buf, INET6_ADDRSTRLEN));
+                               json_nexthop_global, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, INET6_ADDRSTRLEN));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv6");
                        json_object_string_add(json_nexthop_global, "scope",
                                               "global");
                } else {
                        vty_out(vty, "    %s",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
+                               nexthop_hostname
+                                       ? nexthop_hostname
                                        : inet_ntop(AF_INET6,
                                                    &attr->mp_nexthop_global,
                                                    buf, INET6_ADDRSTRLEN));
@@ -8650,12 +8674,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                if (json_paths) {
                        json_nexthop_ll = json_object_new_object();
                        json_object_string_add(
-                               json_nexthop_ll, nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_local,
-                                                   buf, INET6_ADDRSTRLEN));
+                               json_nexthop_ll, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_local,
+                                         buf, INET6_ADDRSTRLEN));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_ll,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_ll, "afi", "ipv6");
                        json_object_string_add(json_nexthop_ll, "scope",
                                               "link-local");
@@ -9121,7 +9148,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
                                  const char *prefix, afi_t afi, safi_t safi,
                                  enum bgp_show_type type);
 static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
-                          afi_t afi, safi_t safi, enum bgp_show_type type);
+                          afi_t afi, safi_t safi, enum bgp_show_type type,
+                          bool use_json);
 static int bgp_show_community(struct vty *vty, struct bgp *bgp,
                              const char *comstr, int exact, afi_t afi,
                              safi_t safi, bool use_json);
@@ -9218,7 +9246,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                struct attr dummy_attr;
                                route_map_result_t ret;
 
-                               bgp_attr_dup(&dummy_attr, pi->attr);
+                               dummy_attr = *pi->attr;
 
                                path.peer = pi->peer;
                                path.attr = &dummy_attr;
@@ -9412,7 +9440,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                        vty_out(vty, ",\"%s\": ", buf2);
                        }
                        vty_out(vty, "%s",
-                               json_object_to_json_string(json_paths));
+                               json_object_to_json_string_ext(
+                                       json_paths, JSON_C_TO_STRING_PRETTY));
                        json_object_free(json_paths);
                        json_paths = NULL;
                        first = 0;
@@ -10438,7 +10467,7 @@ DEFUN (show_ip_bgp_route,
 
 DEFUN (show_ip_bgp_regexp,
        show_ip_bgp_regexp_cmd,
-       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX...",
+       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]",
        SHOW_STR
        IP_STR
        BGP_STR
@@ -10446,11 +10475,14 @@ DEFUN (show_ip_bgp_regexp,
        BGP_AFI_HELP_STR
        BGP_SAFI_WITH_LABEL_HELP_STR
        "Display routes matching the AS path regular expression\n"
-       "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
+       "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n"
+       JSON_STR)
 {
        afi_t afi = AFI_IP6;
        safi_t safi = SAFI_UNICAST;
        struct bgp *bgp = NULL;
+       bool uj = use_json(argc, argv);
+       char *regstr = NULL;
 
        int idx = 0;
        bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
@@ -10459,14 +10491,11 @@ DEFUN (show_ip_bgp_regexp,
                return CMD_WARNING;
 
        // get index of regex
-       argv_find(argv, argc, "regexp", &idx);
-       idx++;
+       if (argv_find(argv, argc, "REGEX", &idx))
+               regstr = argv[idx]->arg;
 
-       char *regstr = argv_concat(argv, argc, idx);
-       int rc = bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi,
-                                bgp_show_type_regexp);
-       XFREE(MTYPE_TMP, regstr);
-       return rc;
+       return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi,
+                                bgp_show_type_regexp, uj);
 }
 
 DEFUN (show_ip_bgp_instance_all,
@@ -10499,13 +10528,14 @@ DEFUN (show_ip_bgp_instance_all,
 }
 
 static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
-                          afi_t afi, safi_t safi, enum bgp_show_type type)
+                          afi_t afi, safi_t safi, enum bgp_show_type type,
+                          bool use_json)
 {
        regex_t *regex;
        int rc;
 
        if (!config_bgp_aspath_validate(regstr)) {
-               vty_out(vty, "Invalid character in as-path access-list %s\n",
+               vty_out(vty, "Invalid character in REGEX %s\n",
                        regstr);
                return CMD_WARNING_CONFIG_FAILED;
        }
@@ -10516,7 +10546,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, 0);
+       rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json);
        bgp_regex_free(regex);
        return rc;
 }
@@ -10921,56 +10951,76 @@ struct peer_pcounts {
        unsigned int count[PCOUNT_MAX];
        const struct peer *peer;
        const struct bgp_table *table;
+       safi_t safi;
 };
 
-static int bgp_peer_count_walker(struct thread *t)
+static void bgp_peer_count_proc(struct bgp_node *rn,
+                               struct peer_pcounts *pc)
 {
-       struct bgp_node *rn;
-       struct peer_pcounts *pc = THREAD_ARG(t);
+       const struct bgp_adj_in *ain;
+       const struct bgp_path_info *pi;
        const struct peer *peer = pc->peer;
 
-       for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) {
-               struct bgp_adj_in *ain;
-               struct bgp_path_info *pi;
-
-               for (ain = rn->adj_in; ain; ain = ain->next)
-                       if (ain->peer == peer)
-                               pc->count[PCOUNT_ADJ_IN]++;
+       for (ain = rn->adj_in; ain; ain = ain->next)
+               if (ain->peer == peer)
+                       pc->count[PCOUNT_ADJ_IN]++;
 
-               for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
+       for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
 
-                       if (pi->peer != peer)
-                               continue;
+               if (pi->peer != peer)
+                       continue;
 
-                       pc->count[PCOUNT_ALL]++;
+               pc->count[PCOUNT_ALL]++;
 
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED))
-                               pc->count[PCOUNT_DAMPED]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
-                               pc->count[PCOUNT_HISTORY]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
-                               pc->count[PCOUNT_REMOVED]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
-                               pc->count[PCOUNT_STALE]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_VALID))
-                               pc->count[PCOUNT_VALID]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED))
+                       pc->count[PCOUNT_DAMPED]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+                       pc->count[PCOUNT_HISTORY]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+                       pc->count[PCOUNT_REMOVED]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
+                       pc->count[PCOUNT_STALE]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+                       pc->count[PCOUNT_VALID]++;
+               if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+                       pc->count[PCOUNT_PFCNT]++;
+
+               if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
+                       pc->count[PCOUNT_COUNTED]++;
+                       if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+                               flog_err(
+                                       EC_LIB_DEVELOPMENT,
+                                       "Attempting to count but flags say it is unusable");
+               } else {
                        if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                               pc->count[PCOUNT_PFCNT]++;
-
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
-                               pc->count[PCOUNT_COUNTED]++;
-                               if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                                       flog_err(
-                                               EC_LIB_DEVELOPMENT,
-                                               "Attempting to count but flags say it is unusable");
-                       } else {
-                               if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                                       flog_err(
-                                               EC_LIB_DEVELOPMENT,
-                                               "Not counted but flags say we should");
-                       }
+                               flog_err(
+                                       EC_LIB_DEVELOPMENT,
+                                       "Not counted but flags say we should");
                }
        }
+}
+
+static int bgp_peer_count_walker(struct thread *t)
+{
+       struct bgp_node *rn, *rm;
+       const struct bgp_table *table;
+       struct peer_pcounts *pc = THREAD_ARG(t);
+
+       if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP
+           || pc->safi == SAFI_EVPN) {
+               /* Special handling for 2-level routing tables. */
+               for (rn = bgp_table_top(pc->table); rn;
+                    rn = bgp_route_next(rn)) {
+                       table = bgp_node_get_bgp_table_info(rn);
+                       if (table != NULL)
+                               for (rm = bgp_table_top(table); rm;
+                                    rm = bgp_route_next(rm))
+                                       bgp_peer_count_proc(rm, pc);
+               }
+       } else
+               for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn))
+                       bgp_peer_count_proc(rn, pc);
+
        return 0;
 }
 
@@ -11004,6 +11054,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,
        memset(&pcounts, 0, sizeof(pcounts));
        pcounts.peer = peer;
        pcounts.table = peer->bgp->rib[afi][safi];
+       pcounts.safi = safi;
 
        /* in-place call via thread subsystem so as to record execution time
         * stats for the thread-walk (i.e. ensure this can't be blamed on
@@ -11361,7 +11412,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
                                        header2 = 0;
                                }
 
-                               bgp_attr_dup(&attr, ain->attr);
+                               attr = *ain->attr;
                                route_filtered = false;
 
                                /* Filter prefix using distribute list,
@@ -11463,7 +11514,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
                                                header2 = 0;
                                        }
 
-                                       bgp_attr_dup(&attr, adj->attr);
+                                       attr = *adj->attr;
                                        ret = bgp_output_modifier(
                                                peer, &rn->p, &attr, afi, safi,
                                                rmap_name);
index 5f3f5cde9a62cfe010ba750fd8192496854ccc59..5b3eb2c719b730226f6b59777e8b95bf758f77c9 100644 (file)
@@ -143,6 +143,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
        dst->v_routeadv = src->v_routeadv;
        dst->flags = src->flags;
        dst->af_flags[afi][safi] = src->af_flags[afi][safi];
+       dst->pmax_out[afi][safi] = src->pmax_out[afi][safi];
        XFREE(MTYPE_BGP_PEER_HOST, dst->host);
 
        dst->host = XSTRDUP(MTYPE_BGP_PEER_HOST, src->host);
index bb547454f21fa55963e56e4aea1fb9fa3bc3e7f6..fe654bb3e30cd7fc3cfd2636f568e069134c9e96 100644 (file)
@@ -208,7 +208,7 @@ struct update_subgroup {
        struct bgp_synchronize *sync;
 
        /* send prefix count */
-       unsigned long scount;
+       uint32_t scount;
 
        /* announcement attribute hash */
        struct hash *hash;
index 5c1483a768e512300f1eb60baad2956af4271586..26dda8ebda449453b5aaec77d78251f5848f6f22 100644 (file)
@@ -64,6 +64,12 @@ static int bgp_adj_out_compare(const struct bgp_adj_out *o1,
        if (o1->subgroup > o2->subgroup)
                return 1;
 
+       if (o1->addpath_tx_id < o2->addpath_tx_id)
+               return -1;
+
+       if (o1->addpath_tx_id > o2->addpath_tx_id)
+               return 1;
+
        return 0;
 }
 RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare);
@@ -72,32 +78,17 @@ static inline struct bgp_adj_out *adj_lookup(struct bgp_node *rn,
                                             struct update_subgroup *subgrp,
                                             uint32_t addpath_tx_id)
 {
-       struct bgp_adj_out *adj, lookup;
-       struct peer *peer;
-       afi_t afi;
-       safi_t safi;
-       int addpath_capable;
+       struct bgp_adj_out lookup;
 
        if (!rn || !subgrp)
                return NULL;
 
-       peer = SUBGRP_PEER(subgrp);
-       afi = SUBGRP_AFI(subgrp);
-       safi = SUBGRP_SAFI(subgrp);
-       addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
-
        /* update-groups that do not support addpath will pass 0 for
-        * addpath_tx_id so do not both matching against it */
+        * addpath_tx_id. */
        lookup.subgroup = subgrp;
-       adj = RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup);
-       if (adj) {
-               if (addpath_capable) {
-                       if (adj->addpath_tx_id == addpath_tx_id)
-                               return adj;
-               } else
-                       return adj;
-       }
-       return NULL;
+       lookup.addpath_tx_id = addpath_tx_id;
+
+       return RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup);
 }
 
 static void adj_free(struct bgp_adj_out *adj)
@@ -403,13 +394,14 @@ struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp,
 
        adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out));
        adj->subgroup = subgrp;
+       adj->addpath_tx_id = addpath_tx_id;
+
        if (rn) {
                RB_INSERT(bgp_adj_out_rb, &rn->adj_out, adj);
                bgp_lock_node(rn);
                adj->rn = rn;
        }
 
-       adj->addpath_tx_id = addpath_tx_id;
        TAILQ_INSERT_TAIL(&(subgrp->adjq), adj, subgrp_adj_train);
        SUBGRP_INCR_STAT(subgrp, adj_count);
        return adj;
@@ -710,12 +702,11 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 {
        struct bgp *bgp;
        struct attr attr;
+       struct attr *new_attr = &attr;
        struct aspath *aspath;
-       struct bgp_path_info tmp_info;
        struct prefix p;
        struct peer *from;
        struct bgp_node *rn;
-       struct bgp_path_info *ri;
        struct peer *peer;
        route_map_result_t ret = RMAP_DENYMATCH;
        afi_t afi;
@@ -755,37 +746,33 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
        }
 
        if (peer->default_rmap[afi][safi].name) {
+               struct attr attr_tmp = attr;
+               struct bgp_path_info bpi_rmap = {0};
+
+               bpi_rmap.peer = bgp->peer_self;
+               bpi_rmap.attr = &attr_tmp;
+
                SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
+
+               /* Iterate over the RIB to see if we can announce
+                * the default route. We announce the default
+                * route only if route-map has a match.
+                */
                for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
                     rn = bgp_route_next(rn)) {
-                       for (ri = bgp_node_get_bgp_path_info(rn);
-                            ri; ri = ri->next) {
-                               struct attr dummy_attr;
-
-                               /* Provide dummy so the route-map can't modify
-                                * the attributes */
-                               bgp_attr_dup(&dummy_attr, ri->attr);
-                               tmp_info.peer = ri->peer;
-                               tmp_info.attr = &dummy_attr;
-
-                               ret = route_map_apply(
-                                       peer->default_rmap[afi][safi].map,
-                                       &rn->p, RMAP_BGP, &tmp_info);
-
-                               /* The route map might have set attributes. If
-                                * we don't flush them
-                                * here, they will be leaked. */
-                               bgp_attr_flush(&dummy_attr);
-                               if (ret != RMAP_DENYMATCH)
-                                       break;
-                       }
+                       ret = route_map_apply(peer->default_rmap[afi][safi].map,
+                                             &rn->p, RMAP_BGP, &bpi_rmap);
+
                        if (ret != RMAP_DENYMATCH)
                                break;
                }
                bgp->peer_self->rmap_type = 0;
+               new_attr = bgp_attr_intern(&attr_tmp);
 
-               if (ret == RMAP_DENYMATCH)
+               if (ret == RMAP_DENYMATCH) {
+                       bgp_attr_flush(&attr_tmp);
                        withdraw = 1;
+               }
        }
 
        if (withdraw) {
@@ -797,12 +784,12 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
                                SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
 
                        if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
-                               bgp_attr_add_gshut_community(&attr);
+                               bgp_attr_add_gshut_community(new_attr);
                        }
 
                        SET_FLAG(subgrp->sflags,
                                 SUBGRP_STATUS_DEFAULT_ORIGINATE);
-                       subgroup_default_update_packet(subgrp, &attr, from);
+                       subgroup_default_update_packet(subgrp, new_attr, from);
 
                        /* The 'neighbor x.x.x.x default-originate' default will
                         * act as an
index 9329c8d8922af4978a693f44b3397318098cd42b..39eb065288fc4a3f4a384014a458fb43acc6ae4c 100644 (file)
@@ -744,6 +744,22 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
                addpath_tx_id = adj->addpath_tx_id;
                path = adv->pathi;
 
+               /* Check if we need to add a prefix to the packet if
+                * maximum-prefix-out is set for the peer.
+                */
+               if (CHECK_FLAG(peer->af_flags[afi][safi],
+                              PEER_FLAG_MAX_PREFIX_OUT)
+                   && subgrp->scount >= peer->pmax_out[afi][safi]) {
+                       if (BGP_DEBUG(update, UPDATE_OUT)
+                           || BGP_DEBUG(update, UPDATE_PREFIX)) {
+                               zlog_debug(
+                                       "%s reached maximum prefix to be send (%" PRIu32
+                                       ")",
+                                       peer->host, peer->pmax_out[afi][safi]);
+                       }
+                       goto next;
+               }
+
                space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
                                  - BGP_MAX_PACKET_SIZE_OVERFLOW;
                space_needed =
@@ -894,7 +910,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
                        subgrp->scount++;
 
                adj->attr = bgp_attr_intern(adv->baa->attr);
-
+next:
                adv = bgp_advertise_clean_subgroup(subgrp, adj);
        }
 
index fa236a24b74cbac45537f8de53f4c23fe759c169..9dc6549d9c40c8df295bd0dccc37f733d5cdbf5e 100644 (file)
@@ -32,7 +32,7 @@
 #include "thread.h"
 #include "log.h"
 #include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 #include "hash.h"
 #include "queue.h"
 #include "filter.h"
 #include "bgpd/bgp_bfd.h"
 #include "bgpd/bgp_io.h"
 #include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_vty.h"
 #include "bgpd/bgp_addpath.h"
 #include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_flowspec.h"
+#if ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#endif
+
+FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY,
+       { .val_ulong = 10, .match_profile = "datacenter", },
+       { .val_ulong = 120 },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME,
+       { .val_ulong = 9, .match_profile = "datacenter", },
+       { .val_ulong = 180 },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE,
+       { .val_ulong = 3, .match_profile = "datacenter", },
+       { .val_ulong = 60 },
+)
+
+DEFINE_HOOK(bgp_inst_config_write,
+               (struct bgp *bgp, struct vty *vty),
+               (bgp, vty))
 
 static struct peer_group *listen_range_exists(struct bgp *bgp,
                                              struct prefix *range, int exact);
@@ -347,6 +385,29 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index,
        return ret;
 }
 
+int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+               enum bgp_instance_type inst_type)
+{
+       int ret = bgp_get(bgp, as, name, inst_type);
+
+       if (ret == BGP_CREATED) {
+               bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+                              DFLT_BGP_CONNECT_RETRY);
+
+               if (DFLT_BGP_IMPORT_CHECK)
+                       bgp_flag_set(*bgp, BGP_FLAG_IMPORT_CHECK);
+               if (DFLT_BGP_SHOW_HOSTNAME)
+                       bgp_flag_set(*bgp, BGP_FLAG_SHOW_HOSTNAME);
+               if (DFLT_BGP_LOG_NEIGHBOR_CHANGES)
+                       bgp_flag_set(*bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+               if (DFLT_BGP_DETERMINISTIC_MED)
+                       bgp_flag_set(*bgp, BGP_FLAG_DETERMINISTIC_MED);
+
+               ret = BGP_SUCCESS;
+       }
+       return ret;
+}
+
 /*
  * bgp_vty_find_and_parse_afi_safi_bgp
  *
@@ -1068,7 +1129,7 @@ DEFUN_NOSH (router_bgp,
                if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
                        is_new_bgp = (bgp_lookup(as, name) == NULL);
 
-               ret = bgp_get(&bgp, &as, name, inst_type);
+               ret = bgp_get_vty(&bgp, &as, name, inst_type);
                switch (ret) {
                case BGP_ERR_AS_MISMATCH:
                        vty_out(vty, "BGP is already running; AS is %u\n", as);
@@ -1777,8 +1838,8 @@ ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd,
             "Number of paths\n"
             "Match the cluster length\n")
 
-void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi,
-                              safi_t safi)
+static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp,
+                                     afi_t afi, safi_t safi)
 {
        if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) {
                vty_out(vty, "  maximum-paths %d\n",
@@ -1821,7 +1882,7 @@ DEFUN (bgp_timers,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       bgp_timers_set(bgp, keepalive, holdtime);
+       bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY);
 
        return CMD_SUCCESS;
 }
@@ -1836,7 +1897,8 @@ DEFUN (no_bgp_timers,
        "Holdtime\n")
 {
        VTY_DECLVAR_CONTEXT(bgp, bgp);
-       bgp_timers_unset(bgp);
+       bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+                      DFLT_BGP_CONNECT_RETRY);
 
        return CMD_SUCCESS;
 }
@@ -2629,8 +2691,8 @@ DEFUN (bgp_listen_limit,
        bgp_listen_limit_cmd,
        "bgp listen limit (1-5000)",
        "BGP specific commands\n"
-       "Configure BGP defaults\n"
-       "maximum number of BGP Dynamic Neighbors that can be created\n"
+       "BGP Dynamic Neighbors listen commands\n"
+       "Maximum number of BGP Dynamic Neighbors that can be created\n"
        "Configure Dynamic Neighbors listen limit value\n")
 {
        VTY_DECLVAR_CONTEXT(bgp, bgp);
@@ -2647,10 +2709,10 @@ DEFUN (bgp_listen_limit,
 DEFUN (no_bgp_listen_limit,
        no_bgp_listen_limit_cmd,
        "no bgp listen limit [(1-5000)]",
+       NO_STR
        "BGP specific commands\n"
-       "Configure BGP defaults\n"
-       "unset maximum number of BGP Dynamic Neighbors that can be created\n"
-       "Configure Dynamic Neighbors listen limit value to default\n"
+       "BGP Dynamic Neighbors listen commands\n"
+       "Maximum number of BGP Dynamic Neighbors that can be created\n"
        "Configure Dynamic Neighbors listen limit value\n")
 {
        VTY_DECLVAR_CONTEXT(bgp, bgp);
@@ -2781,7 +2843,7 @@ DEFUN (no_bgp_listen_range,
        argv_find(argv, argc, "A.B.C.D/M", &idx);
        argv_find(argv, argc, "X:X::X:X/M", &idx);
        char *prefix = argv[idx]->arg;
-       argv_find(argv, argc, "WORD", &idx);
+       argv_find(argv, argc, "PGNAME", &idx);
        char *peergroup = argv[idx]->arg;
 
        /* Convert IP prefix string to struct prefix. */
@@ -5996,6 +6058,56 @@ static int peer_maximum_prefix_unset_vty(struct vty *vty, const char *ip_str,
        return bgp_vty_return(vty, ret);
 }
 
+/* Maximum number of prefix to be sent to the neighbor. */
+DEFUN(neighbor_maximum_prefix_out,
+      neighbor_maximum_prefix_out_cmd,
+      "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out (1-4294967295)",
+      NEIGHBOR_STR
+      NEIGHBOR_ADDR_STR2
+      "Maximum number of prefixes to be sent to this peer\n"
+      "Maximum no. of prefix limit\n")
+{
+       int idx_peer = 1;
+       int idx_number = 3;
+       struct peer *peer;
+       uint32_t max;
+       afi_t afi = bgp_node_afi(vty);
+       safi_t safi = bgp_node_safi(vty);
+
+       peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+       if (!peer)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       max = strtoul(argv[idx_number]->arg, NULL, 10);
+
+       SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT);
+       peer->pmax_out[afi][safi] = max;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_neighbor_maximum_prefix_out,
+      no_neighbor_maximum_prefix_out_cmd,
+      "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out",
+      NO_STR
+      NEIGHBOR_STR
+      NEIGHBOR_ADDR_STR2
+      "Maximum number of prefixes to be sent to this peer\n")
+{
+       int idx_peer = 2;
+       struct peer *peer;
+       afi_t afi = bgp_node_afi(vty);
+       safi_t safi = bgp_node_safi(vty);
+
+       peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+       if (!peer)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       peer->pmax_out[afi][safi] = 0;
+
+       return CMD_SUCCESS;
+}
+
 /* Maximum number of prefix configuration.  prefix count is different
    for each peer configuration.  So this configuration can be set for
    each peer configuration. */
@@ -6956,8 +7068,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd,
                as_t as = bgp->as;
 
                /* Auto-create assuming the same AS */
-               ret = bgp_get(&bgp_default, &as, NULL,
-                             BGP_INSTANCE_TYPE_DEFAULT);
+               ret = bgp_get_vty(&bgp_default, &as, NULL,
+                                 BGP_INSTANCE_TYPE_DEFAULT);
 
                if (ret) {
                        vty_out(vty,
@@ -7042,8 +7154,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd,
        bgp_default = bgp_get_default();
        if (!bgp_default) {
                /* Auto-create assuming the same AS */
-               ret = bgp_get(&bgp_default, &as, NULL,
-                             BGP_INSTANCE_TYPE_DEFAULT);
+               ret = bgp_get_vty(&bgp_default, &as, NULL,
+                                 BGP_INSTANCE_TYPE_DEFAULT);
 
                if (ret) {
                        vty_out(vty,
@@ -7058,7 +7170,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd,
                        vrf_bgp = bgp_default;
                else
                        /* Auto-create assuming the same AS */
-                       ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type);
+                       ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type);
 
                if (ret) {
                        vty_out(vty,
@@ -7631,7 +7743,7 @@ DEFUN (show_bgp_vrfs,
                if (!uj && count == 1) {
                        vty_out(vty,
                                "%4s  %-5s  %-16s  %9s  %10s  %-37s\n",
-                               "Type", "Id", "routerId", "#PeersVfg",
+                               "Type", "Id", "routerId", "#PeersCfg",
                                "#PeersEstb", "Name");
                        vty_out(vty, "%11s  %-16s  %-21s  %-6s\n", " ",
                                "L3-VNI", "RouterMAC", "Interface");
@@ -9128,6 +9240,11 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
                        json_object_int_add(json_addr, "sentPrefixCounter",
                                                (PAF_SUBGRP(paf))->scount);
 
+               /* Maximum prefix */
+               if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT))
+                       json_object_int_add(json_addr, "prefixOutAllowedMax",
+                                           p->pmax_out[afi][safi]);
+
                /* Maximum prefix */
                if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
                        json_object_int_add(json_addr, "prefixAllowedMax",
@@ -9414,6 +9531,13 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
                vty_out(vty, "  %" PRIu32 " 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",
+                               p->pmax_out[afi][safi]);
+
                /* Maximum prefix */
                if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
                        vty_out(vty,
@@ -9649,23 +9773,6 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
                        uptime -= p->uptime;
                        epoch_tbuf = time(NULL) - uptime;
 
-#if CONFDATE > 20200101
-                       CPP_NOTICE(
-                               "bgpTimerUp should be deprecated and can be removed now");
-#endif
-                       /*
-                        * bgpTimerUp was miliseconds that was accurate
-                        * up to 1 day, then the value returned
-                        * became garbage.  So in order to provide
-                        * some level of backwards compatability,
-                        * we still provde the data, but now
-                        * we are returning the correct value
-                        * and also adding a new bgpTimerUpMsec
-                        * which will allow us to deprecate
-                        * this eventually
-                        */
-                       json_object_int_add(json_neigh, "bgpTimerUp",
-                                           uptime * 1000);
                        json_object_int_add(json_neigh, "bgpTimerUpMsec",
                                            uptime * 1000);
                        json_object_string_add(json_neigh, "bgpTimerUpString",
@@ -9725,9 +9832,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
                                json_neigh,
                                "bgpTimerConfiguredKeepAliveIntervalMsecs",
                                p->keepalive * 1000);
-               } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
-                          || (bgp->default_keepalive
-                              != BGP_DEFAULT_KEEPALIVE)) {
+               } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+                          || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) {
                        json_object_int_add(json_neigh,
                                            "bgpTimerConfiguredHoldTimeMsecs",
                                            bgp->default_holdtime);
@@ -9789,9 +9895,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
                                p->holdtime);
                        vty_out(vty, ", keepalive interval is %d seconds\n",
                                p->keepalive);
-               } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
-                          || (bgp->default_keepalive
-                              != BGP_DEFAULT_KEEPALIVE)) {
+               } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+                          || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) {
                        vty_out(vty, "  Configured hold time is %d",
                                bgp->default_holdtime);
                        vty_out(vty, ", keepalive interval is %d seconds\n",
@@ -12809,8 +12914,8 @@ DEFUN (no_bgp_redistribute_ipv6,
        return bgp_redistribute_unset(bgp, AFI_IP6, type, 0);
 }
 
-void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi,
-                                  safi_t safi)
+static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
+                                         afi_t afi, safi_t safi)
 {
        int i;
 
@@ -12847,8 +12952,86 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi,
        }
 }
 
+/* peer-group helpers for config-write */
+
+static bool peergroup_flag_check(struct peer *peer, uint32_t flag)
+{
+       if (!peer_group_active(peer)) {
+               if (CHECK_FLAG(peer->flags_invert, flag))
+                       return !CHECK_FLAG(peer->flags, flag);
+               else
+                       return !!CHECK_FLAG(peer->flags, flag);
+       }
+
+       return !!CHECK_FLAG(peer->flags_override, flag);
+}
+
+static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
+                                   uint32_t flag)
+{
+       if (!peer_group_active(peer)) {
+               if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
+                       return !peer_af_flag_check(peer, afi, safi, flag);
+               else
+                       return !!peer_af_flag_check(peer, afi, safi, flag);
+       }
+
+       return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
+}
+
+static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
+                                  uint8_t type, int direct)
+{
+       struct bgp_filter *filter;
+
+       if (peer_group_active(peer))
+               return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
+                                   type);
+
+       filter = &peer->filter[afi][safi];
+       switch (type) {
+       case PEER_FT_DISTRIBUTE_LIST:
+               return !!(filter->dlist[direct].name);
+       case PEER_FT_FILTER_LIST:
+               return !!(filter->aslist[direct].name);
+       case PEER_FT_PREFIX_LIST:
+               return !!(filter->plist[direct].name);
+       case PEER_FT_ROUTE_MAP:
+               return !!(filter->map[direct].name);
+       case PEER_FT_UNSUPPRESS_MAP:
+               return !!(filter->usmap.name);
+       default:
+               return false;
+       }
+}
+
+/* Return true if the addpath type is set for peer and different from
+ * peer-group.
+ */
+static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
+{
+       enum bgp_addpath_strat type, g_type;
+
+       type = peer->addpath_type[afi][safi];
+
+       if (type != BGP_ADDPATH_NONE) {
+               if (peer_group_active(peer)) {
+                       g_type = peer->group->conf->addpath_type[afi][safi];
+
+                       if (type != g_type)
+                               return 1;
+                       else
+                               return 0;
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
 /* This is part of the address-family block (unicast only) */
-void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
+static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
                                            afi_t afi)
 {
        int indent = 2;
@@ -12948,6 +13131,975 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
        }
 }
 
+static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
+                                   afi_t afi, safi_t safi)
+{
+       struct bgp_filter *filter;
+       char *addr;
+
+       addr = peer->host;
+       filter = &peer->filter[afi][safi];
+
+       /* distribute-list. */
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+                                  FILTER_IN))
+               vty_out(vty, "  neighbor %s distribute-list %s in\n", addr,
+                       filter->dlist[FILTER_IN].name);
+
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+                                  FILTER_OUT))
+               vty_out(vty, "  neighbor %s distribute-list %s out\n", addr,
+                       filter->dlist[FILTER_OUT].name);
+
+       /* prefix-list. */
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+                                  FILTER_IN))
+               vty_out(vty, "  neighbor %s prefix-list %s in\n", addr,
+                       filter->plist[FILTER_IN].name);
+
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+                                  FILTER_OUT))
+               vty_out(vty, "  neighbor %s prefix-list %s out\n", addr,
+                       filter->plist[FILTER_OUT].name);
+
+       /* route-map. */
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN))
+               vty_out(vty, "  neighbor %s route-map %s in\n", addr,
+                       filter->map[RMAP_IN].name);
+
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP,
+                                  RMAP_OUT))
+               vty_out(vty, "  neighbor %s route-map %s out\n", addr,
+                       filter->map[RMAP_OUT].name);
+
+       /* unsuppress-map */
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0))
+               vty_out(vty, "  neighbor %s unsuppress-map %s\n", addr,
+                       filter->usmap.name);
+
+       /* filter-list. */
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+                                  FILTER_IN))
+               vty_out(vty, "  neighbor %s filter-list %s in\n", addr,
+                       filter->aslist[FILTER_IN].name);
+
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+                                  FILTER_OUT))
+               vty_out(vty, "  neighbor %s filter-list %s out\n", addr,
+                       filter->aslist[FILTER_OUT].name);
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
+                                        struct peer *peer)
+{
+       struct peer *g_peer = NULL;
+       char buf[SU_ADDRSTRLEN];
+       char *addr;
+       int if_pg_printed = false;
+       int if_ras_printed = false;
+
+       /* Skip dynamic neighbors. */
+       if (peer_dynamic_neighbor(peer))
+               return;
+
+       if (peer->conf_if)
+               addr = peer->conf_if;
+       else
+               addr = peer->host;
+
+       /************************************
+        ****** Global to the neighbor ******
+        ************************************/
+       if (peer->conf_if) {
+               if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+                       vty_out(vty, " neighbor %s interface v6only", addr);
+               else
+                       vty_out(vty, " neighbor %s interface", addr);
+
+               if (peer_group_active(peer)) {
+                       vty_out(vty, " peer-group %s", peer->group->name);
+                       if_pg_printed = true;
+               } else if (peer->as_type == AS_SPECIFIED) {
+                       vty_out(vty, " remote-as %u", peer->as);
+                       if_ras_printed = true;
+               } else if (peer->as_type == AS_INTERNAL) {
+                       vty_out(vty, " remote-as internal");
+                       if_ras_printed = true;
+               } else if (peer->as_type == AS_EXTERNAL) {
+                       vty_out(vty, " remote-as external");
+                       if_ras_printed = true;
+               }
+
+               vty_out(vty, "\n");
+       }
+
+       /* remote-as and peer-group */
+       /* peer is a member of a peer-group */
+       if (peer_group_active(peer)) {
+               g_peer = peer->group->conf;
+
+               if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) {
+                       if (peer->as_type == AS_SPECIFIED) {
+                               vty_out(vty, " neighbor %s remote-as %u\n",
+                                       addr, peer->as);
+                       } else if (peer->as_type == AS_INTERNAL) {
+                               vty_out(vty,
+                                       " neighbor %s remote-as internal\n",
+                                       addr);
+                       } else if (peer->as_type == AS_EXTERNAL) {
+                               vty_out(vty,
+                                       " neighbor %s remote-as external\n",
+                                       addr);
+                       }
+               }
+
+               /* For swpX peers we displayed the peer-group
+                * via 'neighbor swpX interface peer-group PGNAME' */
+               if (!if_pg_printed)
+                       vty_out(vty, " neighbor %s peer-group %s\n", addr,
+                               peer->group->name);
+       }
+
+       /* peer is NOT a member of a peer-group */
+       else {
+               /* peer is a peer-group, declare the peer-group */
+               if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+                       vty_out(vty, " neighbor %s peer-group\n", addr);
+               }
+
+               if (!if_ras_printed) {
+                       if (peer->as_type == AS_SPECIFIED) {
+                               vty_out(vty, " neighbor %s remote-as %u\n",
+                                       addr, peer->as);
+                       } else if (peer->as_type == AS_INTERNAL) {
+                               vty_out(vty,
+                                       " neighbor %s remote-as internal\n",
+                                       addr);
+                       } else if (peer->as_type == AS_EXTERNAL) {
+                               vty_out(vty,
+                                       " neighbor %s remote-as external\n",
+                                       addr);
+                       }
+               }
+       }
+
+       /* local-as */
+       if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) {
+               vty_out(vty, " neighbor %s local-as %u", addr,
+                       peer->change_local_as);
+               if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+                       vty_out(vty, " no-prepend");
+               if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS))
+                       vty_out(vty, " replace-as");
+               vty_out(vty, "\n");
+       }
+
+       /* description */
+       if (peer->desc) {
+               vty_out(vty, " neighbor %s description %s\n", addr, peer->desc);
+       }
+
+       /* shutdown */
+       if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) {
+               if (peer->tx_shutdown_message)
+                       vty_out(vty, " neighbor %s shutdown message %s\n", addr,
+                               peer->tx_shutdown_message);
+               else
+                       vty_out(vty, " neighbor %s shutdown\n", addr);
+       }
+
+       /* bfd */
+       if (peer->bfd_info) {
+               if (!peer_group_active(peer) || !g_peer->bfd_info) {
+                       bgp_bfd_peer_config_write(vty, peer, addr);
+               }
+       }
+
+       /* password */
+       if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))
+               vty_out(vty, " neighbor %s password %s\n", addr,
+                       peer->password);
+
+       /* neighbor solo */
+       if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) {
+               if (!peer_group_active(peer)) {
+                       vty_out(vty, " neighbor %s solo\n", addr);
+               }
+       }
+
+       /* BGP port */
+       if (peer->port != BGP_PORT_DEFAULT) {
+               vty_out(vty, " neighbor %s port %d\n", addr, peer->port);
+       }
+
+       /* Local interface name */
+       if (peer->ifname) {
+               vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname);
+       }
+
+       /* passive */
+       if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE))
+               vty_out(vty, " neighbor %s passive\n", addr);
+
+       /* ebgp-multihop */
+       if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL
+           && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) {
+               if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) {
+                       vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr,
+                               peer->ttl);
+               }
+       }
+
+       /* ttl-security hops */
+       if (peer->gtsm_hops != 0) {
+               if (!peer_group_active(peer)
+                   || g_peer->gtsm_hops != peer->gtsm_hops) {
+                       vty_out(vty, " neighbor %s ttl-security hops %d\n",
+                               addr, peer->gtsm_hops);
+               }
+       }
+
+       /* disable-connected-check */
+       if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+               vty_out(vty, " neighbor %s disable-connected-check\n", addr);
+
+       /* enforce-first-as */
+       if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS))
+               vty_out(vty, " neighbor %s enforce-first-as\n", addr);
+
+       /* update-source */
+       if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) {
+               if (peer->update_source)
+                       vty_out(vty, " neighbor %s update-source %s\n", addr,
+                               sockunion2str(peer->update_source, buf,
+                                             SU_ADDRSTRLEN));
+               else if (peer->update_if)
+                       vty_out(vty, " neighbor %s update-source %s\n", addr,
+                               peer->update_if);
+       }
+
+       /* advertisement-interval */
+       if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV))
+               vty_out(vty, " neighbor %s advertisement-interval %u\n", addr,
+                       peer->routeadv);
+
+       /* timers */
+       if (peergroup_flag_check(peer, PEER_FLAG_TIMER))
+               vty_out(vty, " neighbor %s timers %u %u\n", addr,
+                       peer->keepalive, peer->holdtime);
+
+       /* timers connect */
+       if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT))
+               vty_out(vty, " neighbor %s timers connect %u\n", addr,
+                       peer->connect);
+       /* need special-case handling for changed default values due to
+        * config profile / version (because there is no "timers bgp connect"
+        * command, we need to save this per-peer :/)
+        */
+       else if (!peer_group_active(peer) && !peer->connect &&
+                peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY)
+               vty_out(vty, " neighbor %s timers connect %u\n", addr,
+                       peer->bgp->default_connect_retry);
+
+       /* capability dynamic */
+       if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
+               vty_out(vty, " neighbor %s capability dynamic\n", addr);
+
+       /* capability extended-nexthop */
+       if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) {
+               if (!peer->conf_if) {
+                       if (CHECK_FLAG(peer->flags_invert,
+                                      PEER_FLAG_CAPABILITY_ENHE))
+                               vty_out(vty,
+                                       " no neighbor %s capability extended-nexthop\n",
+                                       addr);
+                       else
+                               vty_out(vty,
+                                       " neighbor %s capability extended-nexthop\n",
+                                       addr);
+               }
+       }
+
+       /* dont-capability-negotiation */
+       if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
+               vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
+
+       /* override-capability */
+       if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY))
+               vty_out(vty, " neighbor %s override-capability\n", addr);
+
+       /* strict-capability-match */
+       if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
+               vty_out(vty, " neighbor %s strict-capability-match\n", addr);
+
+       /* Sender side AS path loop detection. */
+       if (peer->as_path_loop_detection)
+               vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
+                       addr);
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
+                                    struct peer *peer, afi_t afi, safi_t safi)
+{
+       struct peer *g_peer = NULL;
+       char *addr;
+       bool flag_scomm, flag_secomm, flag_slcomm;
+
+       /* Skip dynamic neighbors. */
+       if (peer_dynamic_neighbor(peer))
+               return;
+
+       if (peer->conf_if)
+               addr = peer->conf_if;
+       else
+               addr = peer->host;
+
+       /************************************
+        ****** Per AF to the neighbor ******
+        ************************************/
+       if (peer_group_active(peer)) {
+               g_peer = peer->group->conf;
+
+               /* If the peer-group is active but peer is not, print a 'no
+                * activate' */
+               if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) {
+                       vty_out(vty, "  no neighbor %s activate\n", addr);
+               }
+
+               /* If the peer-group is not active but peer is, print an
+                  'activate' */
+               else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) {
+                       vty_out(vty, "  neighbor %s activate\n", addr);
+               }
+       } else {
+               if (peer->afc[afi][safi]) {
+                       if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
+                               if (bgp_flag_check(bgp,
+                                                  BGP_FLAG_NO_DEFAULT_IPV4)) {
+                                       vty_out(vty, "  neighbor %s activate\n",
+                                               addr);
+                               }
+                       } else
+                               vty_out(vty, "  neighbor %s activate\n", addr);
+               } else {
+                       if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
+                               if (!bgp_flag_check(bgp,
+                                                   BGP_FLAG_NO_DEFAULT_IPV4)) {
+                                       vty_out(vty,
+                                               "  no neighbor %s activate\n",
+                                               addr);
+                               }
+                       }
+               }
+       }
+
+       /* addpath TX knobs */
+       if (peergroup_af_addpath_check(peer, afi, safi)) {
+               switch (peer->addpath_type[afi][safi]) {
+               case BGP_ADDPATH_ALL:
+                       vty_out(vty, "  neighbor %s addpath-tx-all-paths\n",
+                               addr);
+                       break;
+               case BGP_ADDPATH_BEST_PER_AS:
+                       vty_out(vty,
+                               "  neighbor %s addpath-tx-bestpath-per-AS\n",
+                               addr);
+                       break;
+               case BGP_ADDPATH_MAX:
+               case BGP_ADDPATH_NONE:
+                       break;
+               }
+       }
+
+       /* ORF capability.  */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
+           || peergroup_af_flag_check(peer, afi, safi,
+                                      PEER_FLAG_ORF_PREFIX_RM)) {
+               vty_out(vty, "  neighbor %s capability orf prefix-list", addr);
+
+               if (peergroup_af_flag_check(peer, afi, safi,
+                                           PEER_FLAG_ORF_PREFIX_SM)
+                   && peergroup_af_flag_check(peer, afi, safi,
+                                              PEER_FLAG_ORF_PREFIX_RM))
+                       vty_out(vty, " both");
+               else if (peergroup_af_flag_check(peer, afi, safi,
+                                                PEER_FLAG_ORF_PREFIX_SM))
+                       vty_out(vty, " send");
+               else
+                       vty_out(vty, " receive");
+               vty_out(vty, "\n");
+       }
+
+       /* BGP flag dampening. */
+       if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                      BGP_CONFIG_DAMPENING))
+               bgp_config_write_damp(vty, afi, safi);
+
+       /* Route reflector client. */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_REFLECTOR_CLIENT)) {
+               vty_out(vty, "  neighbor %s route-reflector-client\n", addr);
+       }
+
+       /* next-hop-self force */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_FORCE_NEXTHOP_SELF)) {
+               vty_out(vty, "  neighbor %s next-hop-self force\n", addr);
+       }
+
+       /* next-hop-self */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) {
+               vty_out(vty, "  neighbor %s next-hop-self\n", addr);
+       }
+
+       /* remove-private-AS */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) {
+               vty_out(vty, "  neighbor %s remove-private-AS all replace-AS\n",
+                       addr);
+       }
+
+       else if (peergroup_af_flag_check(peer, afi, safi,
+                                        PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) {
+               vty_out(vty, "  neighbor %s remove-private-AS replace-AS\n",
+                       addr);
+       }
+
+       else if (peergroup_af_flag_check(peer, afi, safi,
+                                        PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
+               vty_out(vty, "  neighbor %s remove-private-AS all\n", addr);
+       }
+
+       else if (peergroup_af_flag_check(peer, afi, safi,
+                                        PEER_FLAG_REMOVE_PRIVATE_AS)) {
+               vty_out(vty, "  neighbor %s remove-private-AS\n", addr);
+       }
+
+       /* as-override */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
+               vty_out(vty, "  neighbor %s as-override\n", addr);
+       }
+
+       /* send-community print. */
+       flag_scomm = peergroup_af_flag_check(peer, afi, safi,
+                                            PEER_FLAG_SEND_COMMUNITY);
+       flag_secomm = peergroup_af_flag_check(peer, afi, safi,
+                                             PEER_FLAG_SEND_EXT_COMMUNITY);
+       flag_slcomm = peergroup_af_flag_check(peer, afi, safi,
+                                             PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+       if (flag_scomm && flag_secomm && flag_slcomm) {
+               vty_out(vty, "  no neighbor %s send-community all\n", addr);
+       } else {
+               if (flag_scomm)
+                       vty_out(vty, "  no neighbor %s send-community\n", addr);
+               if (flag_secomm)
+                       vty_out(vty,
+                               "  no neighbor %s send-community extended\n",
+                               addr);
+
+               if (flag_slcomm)
+                       vty_out(vty, "  no neighbor %s send-community large\n",
+                               addr);
+       }
+
+       /* Default information */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_DEFAULT_ORIGINATE)) {
+               vty_out(vty, "  neighbor %s default-originate", addr);
+
+               if (peer->default_rmap[afi][safi].name)
+                       vty_out(vty, " route-map %s",
+                               peer->default_rmap[afi][safi].name);
+
+               vty_out(vty, "\n");
+       }
+
+       /* Soft reconfiguration inbound. */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) {
+               vty_out(vty, "  neighbor %s soft-reconfiguration inbound\n",
+                       addr);
+       }
+
+       /* maximum-prefix. */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
+               vty_out(vty, "  neighbor %s maximum-prefix %" PRIu32, addr,
+                       peer->pmax[afi][safi]);
+
+               if (peer->pmax_threshold[afi][safi]
+                   != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
+                       vty_out(vty, " %u", peer->pmax_threshold[afi][safi]);
+               if (peer_af_flag_check(peer, afi, safi,
+                                      PEER_FLAG_MAX_PREFIX_WARNING))
+                       vty_out(vty, " warning-only");
+               if (peer->pmax_restart[afi][safi])
+                       vty_out(vty, " restart %u",
+                               peer->pmax_restart[afi][safi]);
+
+               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",
+                       addr, peer->pmax_out[afi][safi]);
+
+       /* Route server client. */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_RSERVER_CLIENT)) {
+               vty_out(vty, "  neighbor %s route-server-client\n", addr);
+       }
+
+       /* Nexthop-local unchanged. */
+       if (peergroup_af_flag_check(peer, afi, safi,
+                                   PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) {
+               vty_out(vty, "  neighbor %s nexthop-local unchanged\n", addr);
+       }
+
+       /* allowas-in <1-10> */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) {
+               if (peer_af_flag_check(peer, afi, safi,
+                                      PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+                       vty_out(vty, "  neighbor %s allowas-in origin\n", addr);
+               } else if (peer->allowas_in[afi][safi] == 3) {
+                       vty_out(vty, "  neighbor %s allowas-in\n", addr);
+               } else {
+                       vty_out(vty, "  neighbor %s allowas-in %d\n", addr,
+                               peer->allowas_in[afi][safi]);
+               }
+       }
+
+       /* weight */
+       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
+               vty_out(vty, "  neighbor %s weight %lu\n", addr,
+                       peer->weight[afi][safi]);
+
+       /* Filter. */
+       bgp_config_write_filter(vty, peer, afi, safi);
+
+       /* atribute-unchanged. */
+       if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
+           || (safi != SAFI_EVPN
+               && peer_af_flag_check(peer, afi, safi,
+                                     PEER_FLAG_NEXTHOP_UNCHANGED))
+           || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
+
+               if (!peer_group_active(peer)
+                   || peergroup_af_flag_check(peer, afi, safi,
+                                              PEER_FLAG_AS_PATH_UNCHANGED)
+                   || peergroup_af_flag_check(peer, afi, safi,
+                                              PEER_FLAG_NEXTHOP_UNCHANGED)
+                   || peergroup_af_flag_check(peer, afi, safi,
+                                              PEER_FLAG_MED_UNCHANGED)) {
+
+                       vty_out(vty,
+                               "  neighbor %s attribute-unchanged%s%s%s\n",
+                               addr,
+                               peer_af_flag_check(peer, afi, safi,
+                                                  PEER_FLAG_AS_PATH_UNCHANGED)
+                                       ? " as-path"
+                                       : "",
+                               peer_af_flag_check(peer, afi, safi,
+                                                  PEER_FLAG_NEXTHOP_UNCHANGED)
+                                       ? " next-hop"
+                                       : "",
+                               peer_af_flag_check(peer, afi, safi,
+                                                  PEER_FLAG_MED_UNCHANGED)
+                                       ? " med"
+                                       : "");
+               }
+       }
+}
+
+/* Address family based peer configuration display.  */
+static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
+                                   safi_t safi)
+{
+       struct peer *peer;
+       struct peer_group *group;
+       struct listnode *node, *nnode;
+
+
+       vty_frame(vty, " !\n address-family ");
+       if (afi == AFI_IP) {
+               if (safi == SAFI_UNICAST)
+                       vty_frame(vty, "ipv4 unicast");
+               else if (safi == SAFI_LABELED_UNICAST)
+                       vty_frame(vty, "ipv4 labeled-unicast");
+               else if (safi == SAFI_MULTICAST)
+                       vty_frame(vty, "ipv4 multicast");
+               else if (safi == SAFI_MPLS_VPN)
+                       vty_frame(vty, "ipv4 vpn");
+               else if (safi == SAFI_ENCAP)
+                       vty_frame(vty, "ipv4 encap");
+               else if (safi == SAFI_FLOWSPEC)
+                       vty_frame(vty, "ipv4 flowspec");
+       } else if (afi == AFI_IP6) {
+               if (safi == SAFI_UNICAST)
+                       vty_frame(vty, "ipv6 unicast");
+               else if (safi == SAFI_LABELED_UNICAST)
+                       vty_frame(vty, "ipv6 labeled-unicast");
+               else if (safi == SAFI_MULTICAST)
+                       vty_frame(vty, "ipv6 multicast");
+               else if (safi == SAFI_MPLS_VPN)
+                       vty_frame(vty, "ipv6 vpn");
+               else if (safi == SAFI_ENCAP)
+                       vty_frame(vty, "ipv6 encap");
+               else if (safi == SAFI_FLOWSPEC)
+                       vty_frame(vty, "ipv6 flowspec");
+       } else if (afi == AFI_L2VPN) {
+               if (safi == SAFI_EVPN)
+                       vty_frame(vty, "l2vpn evpn");
+       }
+       vty_frame(vty, "\n");
+
+       bgp_config_write_distance(vty, bgp, afi, safi);
+
+       bgp_config_write_network(vty, bgp, afi, safi);
+
+       bgp_config_write_redistribute(vty, bgp, afi, safi);
+
+       for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
+               bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
+
+       for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+               /* Skip dynamic neighbors. */
+               if (peer_dynamic_neighbor(peer))
+                       continue;
+
+               /* Do not display doppelganger peers */
+               if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+                       bgp_config_write_peer_af(vty, bgp, peer, afi, safi);
+       }
+
+       bgp_config_write_maxpaths(vty, bgp, afi, safi);
+       bgp_config_write_table_map(vty, bgp, afi, safi);
+
+       if (safi == SAFI_EVPN)
+               bgp_config_write_evpn_info(vty, bgp, afi, safi);
+
+       if (safi == SAFI_FLOWSPEC)
+               bgp_fs_config_write_pbr(vty, bgp, afi, safi);
+
+       if (safi == SAFI_UNICAST) {
+               bgp_vpn_policy_config_write_afi(vty, bgp, afi);
+               if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                              BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+
+                       vty_out(vty, "  export vpn\n");
+               }
+               if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                              BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+
+                       vty_out(vty, "  import vpn\n");
+               }
+               if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                              BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+                       char *name;
+
+                       for (ALL_LIST_ELEMENTS_RO(
+                                    bgp->vpn_policy[afi].import_vrf, node,
+                                    name))
+                               vty_out(vty, "  import vrf %s\n", name);
+               }
+       }
+
+       vty_endframe(vty, " exit-address-family\n");
+}
+
+int bgp_config_write(struct vty *vty)
+{
+       struct bgp *bgp;
+       struct peer_group *group;
+       struct peer *peer;
+       struct listnode *node, *nnode;
+       struct listnode *mnode, *mnnode;
+
+       if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER)
+               vty_out(vty, "bgp route-map delay-timer %u\n",
+                       bm->rmap_update_timer);
+
+       /* BGP configuration. */
+       for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+               /* skip all auto created vrf as they dont have user config */
+               if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
+                       continue;
+
+               /* Router bgp ASN */
+               vty_out(vty, "router bgp %u", bgp->as);
+
+               if (bgp->name)
+                       vty_out(vty, " %s %s",
+                               (bgp->inst_type  == BGP_INSTANCE_TYPE_VIEW)
+                               ? "view" : "vrf", bgp->name);
+               vty_out(vty, "\n");
+
+               /* BGP fast-external-failover. */
+               if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
+                       vty_out(vty, " no bgp fast-external-failover\n");
+
+               /* BGP router ID. */
+               if (bgp->router_id_static.s_addr != 0)
+                       vty_out(vty, " bgp router-id %s\n",
+                               inet_ntoa(bgp->router_id_static));
+
+               /* BGP log-neighbor-changes. */
+               if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+                   != SAVE_BGP_LOG_NEIGHBOR_CHANGES)
+                       vty_out(vty, " %sbgp log-neighbor-changes\n",
+                               bgp_flag_check(bgp,
+                                              BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+                                       ? ""
+                                       : "no ");
+
+               /* BGP configuration. */
+               if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
+                       vty_out(vty, " bgp always-compare-med\n");
+
+               /* RFC8212 default eBGP policy. */
+               if (bgp->ebgp_requires_policy
+                   == DEFAULT_EBGP_POLICY_ENABLED)
+                       vty_out(vty, " bgp ebgp-requires-policy\n");
+
+               /* draft-ietf-idr-deprecate-as-set-confed-set */
+               if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
+                       vty_out(vty, " bgp reject-as-sets\n");
+
+               /* BGP default ipv4-unicast. */
+               if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+                       vty_out(vty, " no bgp default ipv4-unicast\n");
+
+               /* BGP default local-preference. */
+               if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
+                       vty_out(vty, " bgp default local-preference %u\n",
+                               bgp->default_local_pref);
+
+               /* BGP default show-hostname */
+               if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
+                   != SAVE_BGP_SHOW_HOSTNAME)
+                       vty_out(vty, " %sbgp default show-hostname\n",
+                               bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
+                                       ? ""
+                                       : "no ");
+
+               /* BGP default subgroup-pkt-queue-max. */
+               if (bgp->default_subgroup_pkt_queue_max
+                   != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
+                       vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
+                               bgp->default_subgroup_pkt_queue_max);
+
+               /* BGP client-to-client reflection. */
+               if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
+                       vty_out(vty, " no bgp client-to-client reflection\n");
+
+               /* BGP cluster ID. */
+               if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID))
+                       vty_out(vty, " bgp cluster-id %s\n",
+                               inet_ntoa(bgp->cluster_id));
+
+               /* Disable ebgp connected nexthop check */
+               if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+                       vty_out(vty,
+                               " bgp disable-ebgp-connected-route-check\n");
+
+               /* Confederation identifier*/
+               if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+                       vty_out(vty, " bgp confederation identifier %u\n",
+                               bgp->confed_id);
+
+               /* Confederation peer */
+               if (bgp->confed_peers_cnt > 0) {
+                       int i;
+
+                       vty_out(vty, " bgp confederation peers");
+
+                       for (i = 0; i < bgp->confed_peers_cnt; i++)
+                               vty_out(vty, " %u", bgp->confed_peers[i]);
+
+                       vty_out(vty, "\n");
+               }
+
+               /* BGP deterministic-med. */
+               if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
+                   != SAVE_BGP_DETERMINISTIC_MED)
+                       vty_out(vty, " %sbgp deterministic-med\n",
+                               bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
+                                       ? ""
+                                       : "no ");
+
+               /* BGP update-delay. */
+               bgp_config_write_update_delay(vty, bgp);
+
+               if (bgp->v_maxmed_onstartup
+                   != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) {
+                       vty_out(vty, " bgp max-med on-startup %u",
+                               bgp->v_maxmed_onstartup);
+                       if (bgp->maxmed_onstartup_value
+                           != BGP_MAXMED_VALUE_DEFAULT)
+                               vty_out(vty, " %u",
+                                       bgp->maxmed_onstartup_value);
+                       vty_out(vty, "\n");
+               }
+               if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) {
+                       vty_out(vty, " bgp max-med administrative");
+                       if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT)
+                               vty_out(vty, " %u", bgp->maxmed_admin_value);
+                       vty_out(vty, "\n");
+               }
+
+               /* write quanta */
+               bgp_config_write_wpkt_quanta(vty, bgp);
+               /* read quanta */
+               bgp_config_write_rpkt_quanta(vty, bgp);
+
+               /* coalesce time */
+               bgp_config_write_coalesce_time(vty, bgp);
+
+               /* BGP graceful-restart. */
+               if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
+                       vty_out(vty,
+                               " bgp graceful-restart stalepath-time %u\n",
+                               bgp->stalepath_time);
+               if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
+                       vty_out(vty, " bgp graceful-restart restart-time %u\n",
+                               bgp->restart_time);
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART))
+                       vty_out(vty, " bgp graceful-restart\n");
+
+               /* BGP graceful-shutdown */
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+                       vty_out(vty, " bgp graceful-shutdown\n");
+
+               /* BGP graceful-restart Preserve State F bit. */
+               if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD))
+                       vty_out(vty,
+                               " bgp graceful-restart preserve-fw-state\n");
+
+               /* BGP bestpath method. */
+               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE))
+                       vty_out(vty, " bgp bestpath as-path ignore\n");
+               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED))
+                       vty_out(vty, " bgp bestpath as-path confed\n");
+
+               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+                       if (bgp_flag_check(bgp,
+                                          BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
+                               vty_out(vty,
+                                       " bgp bestpath as-path multipath-relax as-set\n");
+                       } else {
+                               vty_out(vty,
+                                       " bgp bestpath as-path multipath-relax\n");
+                       }
+               }
+
+               if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+                       vty_out(vty,
+                               " bgp route-reflector allow-outbound-policy\n");
+               }
+               if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID))
+                       vty_out(vty, " bgp bestpath compare-routerid\n");
+               if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)
+                   || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) {
+                       vty_out(vty, " bgp bestpath med");
+                       if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED))
+                               vty_out(vty, " confed");
+                       if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST))
+                               vty_out(vty, " missing-as-worst");
+                       vty_out(vty, "\n");
+               }
+
+               /* BGP network import check. */
+               if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
+                   != SAVE_BGP_IMPORT_CHECK)
+                       vty_out(vty, " %sbgp network import-check\n",
+                               bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
+                                       ? ""
+                                       : "no ");
+
+               /* BGP timers configuration. */
+               if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE
+                   && bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+                       vty_out(vty, " timers bgp %u %u\n",
+                               bgp->default_keepalive, bgp->default_holdtime);
+
+               /* peer-group */
+               for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+                       bgp_config_write_peer_global(vty, bgp, group->conf);
+               }
+
+               /* Normal neighbor configuration. */
+               for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+                       if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+                               bgp_config_write_peer_global(vty, bgp, peer);
+               }
+
+               /* listen range and limit for dynamic BGP neighbors */
+               bgp_config_write_listen(vty, bgp);
+
+               /*
+                * BGP default autoshutdown neighbors
+                *
+                * This must be placed after any peer and peer-group
+                * configuration, to avoid setting all peers to shutdown after
+                * a daemon restart, which is undesired behavior. (see #2286)
+                */
+               if (bgp->autoshutdown)
+                       vty_out(vty, " bgp default shutdown\n");
+
+               /* IPv4 unicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
+
+               /* IPv4 multicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST);
+
+               /* IPv4 labeled-unicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST);
+
+               /* IPv4 VPN configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN);
+
+               /* ENCAPv4 configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP);
+
+               /* FLOWSPEC v4 configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC);
+
+               /* IPv6 unicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST);
+
+               /* IPv6 multicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST);
+
+               /* IPv6 labeled-unicast configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6,
+                                       SAFI_LABELED_UNICAST);
+
+               /* IPv6 VPN configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
+
+               /* ENCAPv6 configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP);
+
+               /* FLOWSPEC v6 configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC);
+
+               /* EVPN configuration.  */
+               bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
+
+               hook_call(bgp_inst_config_write, bgp, vty);
+
+#if ENABLE_BGP_VNC
+               bgp_rfapi_cfg_write(vty, bgp);
+#endif
+
+               vty_out(vty, "!\n");
+       }
+       return 0;
+}
+
 
 /* BGP node structure. */
 static struct cmd_node bgp_node = {
@@ -14030,6 +15182,26 @@ void bgp_vty_init(void)
        install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
        install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
 
+       /* neighbor maximum-prefix-out commands. */
+       install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_out_cmd);
+       install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+
        /* "neighbor maximum-prefix" commands. */
        install_element(BGP_NODE, &neighbor_maximum_prefix_hidden_cmd);
        install_element(BGP_NODE,
index 27b5ea47b27d461fa617fa17a6460892fdd1aaa7..5f3ce9cd8e54284a68083e53dfbb138a838e9596 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef _QUAGGA_BGP_VTY_H
 #define _QUAGGA_BGP_VTY_H
 
+#include "bgpd/bgpd.h"
+
 struct bgp;
 
 #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n"
@@ -46,6 +48,8 @@ struct bgp;
 
 extern void bgp_vty_init(void);
 extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
+extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+                      enum bgp_instance_type inst_type);
 extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);
 extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
 extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp);
@@ -72,6 +76,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
                                               bool use_json);
 extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
                                safi_t safi, bool show_failed, bool use_json);
-extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
-                                           afi_t afi);
+
 #endif /* _QUAGGA_BGP_VTY_H */
index e886733ced51e3ab2edfe595b434d2635dc61155..c99ddaf0a6dcbcb2c0696a4f829de43e95e495a0 100644 (file)
@@ -1072,7 +1072,7 @@ static int update_ipv4nh_for_route_install(int nh_othervrf,
         */
        if (is_evpn) {
                api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
-               api_nh->onlink = true;
+               SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
                api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
        } else if (nh_othervrf &&
                 api_nh->gate.ipv4.s_addr == INADDR_ANY) {
@@ -1098,7 +1098,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
 
        if (is_evpn) {
                api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
-               api_nh->onlink = true;
+               SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
                api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
        } else if (nh_othervrf) {
                if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
@@ -1347,6 +1347,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                        has_valid_label = 1;
                        label = label_pton(&mpinfo->extra->label[0]);
 
+                       SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
                        api_nh->label_num = 1;
                        api_nh->labels[0] = label;
                }
@@ -1355,11 +1357,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                valid_nh_count++;
        }
 
-
-       /* if this is a evpn route we don't have to include the label */
-       if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
-               SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
-
        /*
         * When we create an aggregate route we must also
         * install a Null0 route in the RIB, so overwrite
@@ -1387,6 +1384,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
        if (bgp_debug_zebra(p)) {
                char prefix_buf[PREFIX_STRLEN];
                char nh_buf[INET6_ADDRSTRLEN];
+               char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'};
+               char buf1[ETHER_ADDR_STRLEN];
                char label_buf[20];
                int i;
 
@@ -1424,13 +1423,19 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                        }
 
                        label_buf[0] = '\0';
+                       eth_buf[0] = '\0';
                        if (has_valid_label
                            && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
-                               sprintf(label_buf, "label %u",
-                                       api_nh->labels[0]);
-                       zlog_debug("  nhop [%d]: %s if %u VRF %u %s",
+                               snprintf(label_buf, sizeof(label_buf),
+                                       "label %u", api_nh->labels[0]);
+                       if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)
+                           && !is_zero_mac(&api_nh->rmac))
+                               snprintf(eth_buf, sizeof(eth_buf), " RMAC %s",
+                                        prefix_mac2str(&api_nh->rmac,
+                                                       buf1, sizeof(buf1)));
+                       zlog_debug("  nhop [%d]: %s if %u VRF %u %s %s",
                                   i + 1, nh_buf, api_nh->ifindex,
-                                  api_nh->vrf_id, label_buf);
+                                  api_nh->vrf_id, label_buf, eth_buf);
                }
        }
 
@@ -1693,7 +1698,7 @@ int bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red,
                                struct attr *old_attr;
                                struct attr new_attr;
 
-                               bgp_attr_dup(&new_attr, pi->attr);
+                               new_attr = *pi->attr;
                                new_attr.med = red->redist_metric;
                                old_attr = pi->attr;
                                pi->attr = bgp_attr_intern(&new_attr);
index b912870b80dbf94e0e5f58dd0f3d87c672b70c81..62c311cc1d086c83d0f20cff0167c5cc96f692a6 100644 (file)
@@ -32,10 +32,6 @@ extern void bgp_zebra_destroy(void);
 extern int bgp_zebra_get_table_range(uint32_t chunk_size,
                                     uint32_t *start, uint32_t *end);
 extern int bgp_if_update_all(void);
-extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t,
-                                     safi_t);
-extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t,
-                                         safi_t);
 extern void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                               struct bgp_path_info *path, struct bgp *bgp,
                               afi_t afi, safi_t safi);
index 4283fe169ecf7381ab1b27b271a80ab0da198a78..9b0e81491a89bebc63f785bf963c2ebf93b744c8 100644 (file)
@@ -95,9 +95,6 @@ DEFINE_QOBJ_TYPE(bgp_master)
 DEFINE_QOBJ_TYPE(bgp)
 DEFINE_QOBJ_TYPE(peer)
 DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp))
-DEFINE_HOOK(bgp_inst_config_write,
-               (struct bgp *bgp, struct vty *vty),
-               (bgp, vty))
 
 /* BGP process wide configuration.  */
 static struct bgp_master bgp_master;
@@ -407,19 +404,23 @@ time_t bgp_clock(void)
 }
 
 /* BGP timer configuration.  */
-int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime)
+int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime,
+                  uint32_t connect_retry)
 {
        bgp->default_keepalive =
                (keepalive < holdtime / 3 ? keepalive : holdtime / 3);
        bgp->default_holdtime = holdtime;
+       bgp->default_connect_retry = connect_retry;
 
        return 0;
 }
 
+/* mostly for completeness - CLI uses its own defaults */
 int bgp_timers_unset(struct bgp *bgp)
 {
        bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
        bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+       bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY;
 
        return 0;
 }
@@ -883,82 +884,6 @@ void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi,
                COND_FLAG(peer->af_flags[afi][safi], flag, group_val);
 }
 
-static bool peergroup_flag_check(struct peer *peer, uint32_t flag)
-{
-       if (!peer_group_active(peer)) {
-               if (CHECK_FLAG(peer->flags_invert, flag))
-                       return !CHECK_FLAG(peer->flags, flag);
-               else
-                       return !!CHECK_FLAG(peer->flags, flag);
-       }
-
-       return !!CHECK_FLAG(peer->flags_override, flag);
-}
-
-static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
-                                   uint32_t flag)
-{
-       if (!peer_group_active(peer)) {
-               if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
-                       return !peer_af_flag_check(peer, afi, safi, flag);
-               else
-                       return !!peer_af_flag_check(peer, afi, safi, flag);
-       }
-
-       return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
-}
-
-static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
-                                  uint8_t type, int direct)
-{
-       struct bgp_filter *filter;
-
-       if (peer_group_active(peer))
-               return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
-                                   type);
-
-       filter = &peer->filter[afi][safi];
-       switch (type) {
-       case PEER_FT_DISTRIBUTE_LIST:
-               return !!(filter->dlist[direct].name);
-       case PEER_FT_FILTER_LIST:
-               return !!(filter->aslist[direct].name);
-       case PEER_FT_PREFIX_LIST:
-               return !!(filter->plist[direct].name);
-       case PEER_FT_ROUTE_MAP:
-               return !!(filter->map[direct].name);
-       case PEER_FT_UNSUPPRESS_MAP:
-               return !!(filter->usmap.name);
-       default:
-               return false;
-       }
-}
-
-/* Return true if the addpath type is set for peer and different from
- * peer-group.
- */
-static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
-{
-       enum bgp_addpath_strat type, g_type;
-
-       type = peer->addpath_type[afi][safi];
-
-       if (type != BGP_ADDPATH_NONE) {
-               if (peer_group_active(peer)) {
-                       g_type = peer->group->conf->addpath_type[afi][safi];
-
-                       if (type != g_type)
-                               return 1;
-                       else
-                               return 0;
-               }
-
-               return 1;
-       }
-
-       return 0;
-}
-
 /* Check peer's AS number and determines if this peer is IBGP or EBGP */
 static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer)
 {
@@ -1197,7 +1122,7 @@ struct peer *peer_new(struct bgp *bgp)
        /* Set default value. */
        peer->fd = -1;
        peer->v_start = BGP_INIT_START_TIMER;
-       peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+       peer->v_connect = bgp->default_connect_retry;
        peer->status = Idle;
        peer->ostatus = Idle;
        peer->cur_event = peer->last_event = peer->last_major_event = 0;
@@ -2496,7 +2421,7 @@ static void peer_group2peer_config_copy(struct peer_group *group,
                if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT))
                        peer->v_connect = conf->connect;
                else
-                       peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+                       peer->v_connect = peer->bgp->default_connect_retry;
        }
 
        /* advertisement-interval apply */
@@ -2983,26 +2908,13 @@ static struct bgp *bgp_create(as_t *as, const char *name,
        bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
        bgp->default_subgroup_pkt_queue_max =
                BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
-       bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
-       bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+       bgp_timers_unset(bgp);
        bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
        bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
        bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
        bgp->dynamic_neighbors_count = 0;
        bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED;
        bgp->reject_as_sets = BGP_REJECT_AS_SETS_DISABLED;
-#if DFLT_BGP_IMPORT_CHECK
-       bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK);
-#endif
-#if DFLT_BGP_SHOW_HOSTNAME
-       bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME);
-#endif
-#if DFLT_BGP_LOG_NEIGHBOR_CHANGES
-       bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
-#endif
-#if DFLT_BGP_DETERMINISTIC_MED
-       bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED);
-#endif
        bgp_addpath_init_bgp_data(&bgp->tx_addpath);
 
        bgp->as = *as;
@@ -3255,7 +3167,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
                bgp_zebra_instance_register(bgp);
        }
 
-       return BGP_SUCCESS;
+       return BGP_CREATED;
 }
 
 /*
@@ -5059,7 +4971,7 @@ int peer_timers_connect_unset(struct peer *peer)
        if (peer->connect)
                peer->v_connect = peer->connect;
        else
-               peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+               peer->v_connect = peer->bgp->default_connect_retry;
 
        /* Skip peer-group mechanics for regular peers. */
        if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
@@ -5077,7 +4989,7 @@ int peer_timers_connect_unset(struct peer *peer)
                /* Remove flag and configuration on peer-group member. */
                UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
                member->connect = 0;
-               member->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+               member->v_connect = peer->bgp->default_connect_retry;
        }
 
        return 0;
@@ -6899,962 +6811,6 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
        return buf;
 }
 
-static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
-                                   afi_t afi, safi_t safi)
-{
-       struct bgp_filter *filter;
-       char *addr;
-
-       addr = peer->host;
-       filter = &peer->filter[afi][safi];
-
-       /* distribute-list. */
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
-                                  FILTER_IN))
-               vty_out(vty, "  neighbor %s distribute-list %s in\n", addr,
-                       filter->dlist[FILTER_IN].name);
-
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
-                                  FILTER_OUT))
-               vty_out(vty, "  neighbor %s distribute-list %s out\n", addr,
-                       filter->dlist[FILTER_OUT].name);
-
-       /* prefix-list. */
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
-                                  FILTER_IN))
-               vty_out(vty, "  neighbor %s prefix-list %s in\n", addr,
-                       filter->plist[FILTER_IN].name);
-
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
-                                  FILTER_OUT))
-               vty_out(vty, "  neighbor %s prefix-list %s out\n", addr,
-                       filter->plist[FILTER_OUT].name);
-
-       /* route-map. */
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN))
-               vty_out(vty, "  neighbor %s route-map %s in\n", addr,
-                       filter->map[RMAP_IN].name);
-
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP,
-                                  RMAP_OUT))
-               vty_out(vty, "  neighbor %s route-map %s out\n", addr,
-                       filter->map[RMAP_OUT].name);
-
-       /* unsuppress-map */
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0))
-               vty_out(vty, "  neighbor %s unsuppress-map %s\n", addr,
-                       filter->usmap.name);
-
-       /* filter-list. */
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
-                                  FILTER_IN))
-               vty_out(vty, "  neighbor %s filter-list %s in\n", addr,
-                       filter->aslist[FILTER_IN].name);
-
-       if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
-                                  FILTER_OUT))
-               vty_out(vty, "  neighbor %s filter-list %s out\n", addr,
-                       filter->aslist[FILTER_OUT].name);
-}
-
-/* BGP peer configuration display function. */
-static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
-                                        struct peer *peer)
-{
-       struct peer *g_peer = NULL;
-       char buf[SU_ADDRSTRLEN];
-       char *addr;
-       int if_pg_printed = false;
-       int if_ras_printed = false;
-
-       /* Skip dynamic neighbors. */
-       if (peer_dynamic_neighbor(peer))
-               return;
-
-       if (peer->conf_if)
-               addr = peer->conf_if;
-       else
-               addr = peer->host;
-
-       /************************************
-        ****** Global to the neighbor ******
-        ************************************/
-       if (peer->conf_if) {
-               if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
-                       vty_out(vty, " neighbor %s interface v6only", addr);
-               else
-                       vty_out(vty, " neighbor %s interface", addr);
-
-               if (peer_group_active(peer)) {
-                       vty_out(vty, " peer-group %s", peer->group->name);
-                       if_pg_printed = true;
-               } else if (peer->as_type == AS_SPECIFIED) {
-                       vty_out(vty, " remote-as %u", peer->as);
-                       if_ras_printed = true;
-               } else if (peer->as_type == AS_INTERNAL) {
-                       vty_out(vty, " remote-as internal");
-                       if_ras_printed = true;
-               } else if (peer->as_type == AS_EXTERNAL) {
-                       vty_out(vty, " remote-as external");
-                       if_ras_printed = true;
-               }
-
-               vty_out(vty, "\n");
-       }
-
-       /* remote-as and peer-group */
-       /* peer is a member of a peer-group */
-       if (peer_group_active(peer)) {
-               g_peer = peer->group->conf;
-
-               if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) {
-                       if (peer->as_type == AS_SPECIFIED) {
-                               vty_out(vty, " neighbor %s remote-as %u\n",
-                                       addr, peer->as);
-                       } else if (peer->as_type == AS_INTERNAL) {
-                               vty_out(vty,
-                                       " neighbor %s remote-as internal\n",
-                                       addr);
-                       } else if (peer->as_type == AS_EXTERNAL) {
-                               vty_out(vty,
-                                       " neighbor %s remote-as external\n",
-                                       addr);
-                       }
-               }
-
-               /* For swpX peers we displayed the peer-group
-                * via 'neighbor swpX interface peer-group PGNAME' */
-               if (!if_pg_printed)
-                       vty_out(vty, " neighbor %s peer-group %s\n", addr,
-                               peer->group->name);
-       }
-
-       /* peer is NOT a member of a peer-group */
-       else {
-               /* peer is a peer-group, declare the peer-group */
-               if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-                       vty_out(vty, " neighbor %s peer-group\n", addr);
-               }
-
-               if (!if_ras_printed) {
-                       if (peer->as_type == AS_SPECIFIED) {
-                               vty_out(vty, " neighbor %s remote-as %u\n",
-                                       addr, peer->as);
-                       } else if (peer->as_type == AS_INTERNAL) {
-                               vty_out(vty,
-                                       " neighbor %s remote-as internal\n",
-                                       addr);
-                       } else if (peer->as_type == AS_EXTERNAL) {
-                               vty_out(vty,
-                                       " neighbor %s remote-as external\n",
-                                       addr);
-                       }
-               }
-       }
-
-       /* local-as */
-       if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) {
-               vty_out(vty, " neighbor %s local-as %u", addr,
-                       peer->change_local_as);
-               if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND))
-                       vty_out(vty, " no-prepend");
-               if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS))
-                       vty_out(vty, " replace-as");
-               vty_out(vty, "\n");
-       }
-
-       /* description */
-       if (peer->desc) {
-               vty_out(vty, " neighbor %s description %s\n", addr, peer->desc);
-       }
-
-       /* shutdown */
-       if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) {
-               if (peer->tx_shutdown_message)
-                       vty_out(vty, " neighbor %s shutdown message %s\n", addr,
-                               peer->tx_shutdown_message);
-               else
-                       vty_out(vty, " neighbor %s shutdown\n", addr);
-       }
-
-       /* bfd */
-       if (peer->bfd_info) {
-               if (!peer_group_active(peer) || !g_peer->bfd_info) {
-                       bgp_bfd_peer_config_write(vty, peer, addr);
-               }
-       }
-
-       /* password */
-       if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))
-               vty_out(vty, " neighbor %s password %s\n", addr,
-                       peer->password);
-
-       /* neighbor solo */
-       if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) {
-               if (!peer_group_active(peer)) {
-                       vty_out(vty, " neighbor %s solo\n", addr);
-               }
-       }
-
-       /* BGP port */
-       if (peer->port != BGP_PORT_DEFAULT) {
-               vty_out(vty, " neighbor %s port %d\n", addr, peer->port);
-       }
-
-       /* Local interface name */
-       if (peer->ifname) {
-               vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname);
-       }
-
-       /* passive */
-       if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE))
-               vty_out(vty, " neighbor %s passive\n", addr);
-
-       /* ebgp-multihop */
-       if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL
-           && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) {
-               if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) {
-                       vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr,
-                               peer->ttl);
-               }
-       }
-
-       /* ttl-security hops */
-       if (peer->gtsm_hops != 0) {
-               if (!peer_group_active(peer)
-                   || g_peer->gtsm_hops != peer->gtsm_hops) {
-                       vty_out(vty, " neighbor %s ttl-security hops %d\n",
-                               addr, peer->gtsm_hops);
-               }
-       }
-
-       /* disable-connected-check */
-       if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK))
-               vty_out(vty, " neighbor %s disable-connected-check\n", addr);
-
-       /* enforce-first-as */
-       if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS))
-               vty_out(vty, " neighbor %s enforce-first-as\n", addr);
-
-       /* update-source */
-       if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) {
-               if (peer->update_source)
-                       vty_out(vty, " neighbor %s update-source %s\n", addr,
-                               sockunion2str(peer->update_source, buf,
-                                             SU_ADDRSTRLEN));
-               else if (peer->update_if)
-                       vty_out(vty, " neighbor %s update-source %s\n", addr,
-                               peer->update_if);
-       }
-
-       /* advertisement-interval */
-       if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV))
-               vty_out(vty, " neighbor %s advertisement-interval %u\n", addr,
-                       peer->routeadv);
-
-       /* timers */
-       if (peergroup_flag_check(peer, PEER_FLAG_TIMER))
-               vty_out(vty, " neighbor %s timers %u %u\n", addr,
-                       peer->keepalive, peer->holdtime);
-
-       /* timers connect */
-       if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT))
-               vty_out(vty, " neighbor %s timers connect %u\n", addr,
-                       peer->connect);
-
-       /* capability dynamic */
-       if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
-               vty_out(vty, " neighbor %s capability dynamic\n", addr);
-
-       /* capability extended-nexthop */
-       if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) {
-               if (!peer->conf_if) {
-                       if (CHECK_FLAG(peer->flags_invert,
-                                      PEER_FLAG_CAPABILITY_ENHE))
-                               vty_out(vty,
-                                       " no neighbor %s capability extended-nexthop\n",
-                                       addr);
-                       else
-                               vty_out(vty,
-                                       " neighbor %s capability extended-nexthop\n",
-                                       addr);
-               }
-       }
-
-       /* dont-capability-negotiation */
-       if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
-               vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
-
-       /* override-capability */
-       if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY))
-               vty_out(vty, " neighbor %s override-capability\n", addr);
-
-       /* strict-capability-match */
-       if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
-               vty_out(vty, " neighbor %s strict-capability-match\n", addr);
-
-       /* Sender side AS path loop detection. */
-       if (peer->as_path_loop_detection)
-               vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
-                       addr);
-}
-
-/* BGP peer configuration display function. */
-static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
-                                    struct peer *peer, afi_t afi, safi_t safi)
-{
-       struct peer *g_peer = NULL;
-       char *addr;
-       bool flag_scomm, flag_secomm, flag_slcomm;
-
-       /* Skip dynamic neighbors. */
-       if (peer_dynamic_neighbor(peer))
-               return;
-
-       if (peer->conf_if)
-               addr = peer->conf_if;
-       else
-               addr = peer->host;
-
-       /************************************
-        ****** Per AF to the neighbor ******
-        ************************************/
-       if (peer_group_active(peer)) {
-               g_peer = peer->group->conf;
-
-               /* If the peer-group is active but peer is not, print a 'no
-                * activate' */
-               if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) {
-                       vty_out(vty, "  no neighbor %s activate\n", addr);
-               }
-
-               /* If the peer-group is not active but peer is, print an
-                  'activate' */
-               else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) {
-                       vty_out(vty, "  neighbor %s activate\n", addr);
-               }
-       } else {
-               if (peer->afc[afi][safi]) {
-                       if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
-                               if (bgp_flag_check(bgp,
-                                                  BGP_FLAG_NO_DEFAULT_IPV4)) {
-                                       vty_out(vty, "  neighbor %s activate\n",
-                                               addr);
-                               }
-                       } else
-                               vty_out(vty, "  neighbor %s activate\n", addr);
-               } else {
-                       if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
-                               if (!bgp_flag_check(bgp,
-                                                   BGP_FLAG_NO_DEFAULT_IPV4)) {
-                                       vty_out(vty,
-                                               "  no neighbor %s activate\n",
-                                               addr);
-                               }
-                       }
-               }
-       }
-
-       /* addpath TX knobs */
-       if (peergroup_af_addpath_check(peer, afi, safi)) {
-               switch (peer->addpath_type[afi][safi]) {
-               case BGP_ADDPATH_ALL:
-                       vty_out(vty, "  neighbor %s addpath-tx-all-paths\n",
-                               addr);
-                       break;
-               case BGP_ADDPATH_BEST_PER_AS:
-                       vty_out(vty,
-                               "  neighbor %s addpath-tx-bestpath-per-AS\n",
-                               addr);
-                       break;
-               case BGP_ADDPATH_MAX:
-               case BGP_ADDPATH_NONE:
-                       break;
-               }
-       }
-
-       /* ORF capability.  */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
-           || peergroup_af_flag_check(peer, afi, safi,
-                                      PEER_FLAG_ORF_PREFIX_RM)) {
-               vty_out(vty, "  neighbor %s capability orf prefix-list", addr);
-
-               if (peergroup_af_flag_check(peer, afi, safi,
-                                           PEER_FLAG_ORF_PREFIX_SM)
-                   && peergroup_af_flag_check(peer, afi, safi,
-                                              PEER_FLAG_ORF_PREFIX_RM))
-                       vty_out(vty, " both");
-               else if (peergroup_af_flag_check(peer, afi, safi,
-                                                PEER_FLAG_ORF_PREFIX_SM))
-                       vty_out(vty, " send");
-               else
-                       vty_out(vty, " receive");
-               vty_out(vty, "\n");
-       }
-
-       /* BGP flag dampening. */
-       if (CHECK_FLAG(bgp->af_flags[afi][safi],
-                      BGP_CONFIG_DAMPENING))
-               bgp_config_write_damp(vty, afi, safi);
-
-       /* Route reflector client. */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_REFLECTOR_CLIENT)) {
-               vty_out(vty, "  neighbor %s route-reflector-client\n", addr);
-       }
-
-       /* next-hop-self force */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_FORCE_NEXTHOP_SELF)) {
-               vty_out(vty, "  neighbor %s next-hop-self force\n", addr);
-       }
-
-       /* next-hop-self */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) {
-               vty_out(vty, "  neighbor %s next-hop-self\n", addr);
-       }
-
-       /* remove-private-AS */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) {
-               vty_out(vty, "  neighbor %s remove-private-AS all replace-AS\n",
-                       addr);
-       }
-
-       else if (peergroup_af_flag_check(peer, afi, safi,
-                                        PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) {
-               vty_out(vty, "  neighbor %s remove-private-AS replace-AS\n",
-                       addr);
-       }
-
-       else if (peergroup_af_flag_check(peer, afi, safi,
-                                        PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
-               vty_out(vty, "  neighbor %s remove-private-AS all\n", addr);
-       }
-
-       else if (peergroup_af_flag_check(peer, afi, safi,
-                                        PEER_FLAG_REMOVE_PRIVATE_AS)) {
-               vty_out(vty, "  neighbor %s remove-private-AS\n", addr);
-       }
-
-       /* as-override */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
-               vty_out(vty, "  neighbor %s as-override\n", addr);
-       }
-
-       /* send-community print. */
-       flag_scomm = peergroup_af_flag_check(peer, afi, safi,
-                                            PEER_FLAG_SEND_COMMUNITY);
-       flag_secomm = peergroup_af_flag_check(peer, afi, safi,
-                                             PEER_FLAG_SEND_EXT_COMMUNITY);
-       flag_slcomm = peergroup_af_flag_check(peer, afi, safi,
-                                             PEER_FLAG_SEND_LARGE_COMMUNITY);
-
-       if (flag_scomm && flag_secomm && flag_slcomm) {
-               vty_out(vty, "  no neighbor %s send-community all\n", addr);
-       } else {
-               if (flag_scomm)
-                       vty_out(vty, "  no neighbor %s send-community\n", addr);
-               if (flag_secomm)
-                       vty_out(vty,
-                               "  no neighbor %s send-community extended\n",
-                               addr);
-
-               if (flag_slcomm)
-                       vty_out(vty, "  no neighbor %s send-community large\n",
-                               addr);
-       }
-
-       /* Default information */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_DEFAULT_ORIGINATE)) {
-               vty_out(vty, "  neighbor %s default-originate", addr);
-
-               if (peer->default_rmap[afi][safi].name)
-                       vty_out(vty, " route-map %s",
-                               peer->default_rmap[afi][safi].name);
-
-               vty_out(vty, "\n");
-       }
-
-       /* Soft reconfiguration inbound. */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) {
-               vty_out(vty, "  neighbor %s soft-reconfiguration inbound\n",
-                       addr);
-       }
-
-       /* maximum-prefix. */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
-               vty_out(vty, "  neighbor %s maximum-prefix %" PRIu32, addr,
-                       peer->pmax[afi][safi]);
-
-               if (peer->pmax_threshold[afi][safi]
-                   != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
-                       vty_out(vty, " %u", peer->pmax_threshold[afi][safi]);
-               if (peer_af_flag_check(peer, afi, safi,
-                                      PEER_FLAG_MAX_PREFIX_WARNING))
-                       vty_out(vty, " warning-only");
-               if (peer->pmax_restart[afi][safi])
-                       vty_out(vty, " restart %u",
-                               peer->pmax_restart[afi][safi]);
-
-               vty_out(vty, "\n");
-       }
-
-       /* Route server client. */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_RSERVER_CLIENT)) {
-               vty_out(vty, "  neighbor %s route-server-client\n", addr);
-       }
-
-       /* Nexthop-local unchanged. */
-       if (peergroup_af_flag_check(peer, afi, safi,
-                                   PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) {
-               vty_out(vty, "  neighbor %s nexthop-local unchanged\n", addr);
-       }
-
-       /* allowas-in <1-10> */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) {
-               if (peer_af_flag_check(peer, afi, safi,
-                                      PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
-                       vty_out(vty, "  neighbor %s allowas-in origin\n", addr);
-               } else if (peer->allowas_in[afi][safi] == 3) {
-                       vty_out(vty, "  neighbor %s allowas-in\n", addr);
-               } else {
-                       vty_out(vty, "  neighbor %s allowas-in %d\n", addr,
-                               peer->allowas_in[afi][safi]);
-               }
-       }
-
-       /* weight */
-       if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
-               vty_out(vty, "  neighbor %s weight %lu\n", addr,
-                       peer->weight[afi][safi]);
-
-       /* Filter. */
-       bgp_config_write_filter(vty, peer, afi, safi);
-
-       /* atribute-unchanged. */
-       if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
-           || (safi != SAFI_EVPN
-               && peer_af_flag_check(peer, afi, safi,
-                                     PEER_FLAG_NEXTHOP_UNCHANGED))
-           || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
-
-               if (!peer_group_active(peer)
-                   || peergroup_af_flag_check(peer, afi, safi,
-                                              PEER_FLAG_AS_PATH_UNCHANGED)
-                   || peergroup_af_flag_check(peer, afi, safi,
-                                              PEER_FLAG_NEXTHOP_UNCHANGED)
-                   || peergroup_af_flag_check(peer, afi, safi,
-                                              PEER_FLAG_MED_UNCHANGED)) {
-
-                       vty_out(vty,
-                               "  neighbor %s attribute-unchanged%s%s%s\n",
-                               addr,
-                               peer_af_flag_check(peer, afi, safi,
-                                                  PEER_FLAG_AS_PATH_UNCHANGED)
-                                       ? " as-path"
-                                       : "",
-                               peer_af_flag_check(peer, afi, safi,
-                                                  PEER_FLAG_NEXTHOP_UNCHANGED)
-                                       ? " next-hop"
-                                       : "",
-                               peer_af_flag_check(peer, afi, safi,
-                                                  PEER_FLAG_MED_UNCHANGED)
-                                       ? " med"
-                                       : "");
-               }
-       }
-}
-
-/* Address family based peer configuration display.  */
-static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
-                                   safi_t safi)
-{
-       struct peer *peer;
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-
-
-       vty_frame(vty, " !\n address-family ");
-       if (afi == AFI_IP) {
-               if (safi == SAFI_UNICAST)
-                       vty_frame(vty, "ipv4 unicast");
-               else if (safi == SAFI_LABELED_UNICAST)
-                       vty_frame(vty, "ipv4 labeled-unicast");
-               else if (safi == SAFI_MULTICAST)
-                       vty_frame(vty, "ipv4 multicast");
-               else if (safi == SAFI_MPLS_VPN)
-                       vty_frame(vty, "ipv4 vpn");
-               else if (safi == SAFI_ENCAP)
-                       vty_frame(vty, "ipv4 encap");
-               else if (safi == SAFI_FLOWSPEC)
-                       vty_frame(vty, "ipv4 flowspec");
-       } else if (afi == AFI_IP6) {
-               if (safi == SAFI_UNICAST)
-                       vty_frame(vty, "ipv6 unicast");
-               else if (safi == SAFI_LABELED_UNICAST)
-                       vty_frame(vty, "ipv6 labeled-unicast");
-               else if (safi == SAFI_MULTICAST)
-                       vty_frame(vty, "ipv6 multicast");
-               else if (safi == SAFI_MPLS_VPN)
-                       vty_frame(vty, "ipv6 vpn");
-               else if (safi == SAFI_ENCAP)
-                       vty_frame(vty, "ipv6 encap");
-               else if (safi == SAFI_FLOWSPEC)
-                       vty_frame(vty, "ipv6 flowspec");
-       } else if (afi == AFI_L2VPN) {
-               if (safi == SAFI_EVPN)
-                       vty_frame(vty, "l2vpn evpn");
-       }
-       vty_frame(vty, "\n");
-
-       bgp_config_write_distance(vty, bgp, afi, safi);
-
-       bgp_config_write_network(vty, bgp, afi, safi);
-
-       bgp_config_write_redistribute(vty, bgp, afi, safi);
-
-       for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
-               bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
-
-       for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
-               /* Skip dynamic neighbors. */
-               if (peer_dynamic_neighbor(peer))
-                       continue;
-
-               /* Do not display doppelganger peers */
-               if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
-                       bgp_config_write_peer_af(vty, bgp, peer, afi, safi);
-       }
-
-       bgp_config_write_maxpaths(vty, bgp, afi, safi);
-       bgp_config_write_table_map(vty, bgp, afi, safi);
-
-       if (safi == SAFI_EVPN)
-               bgp_config_write_evpn_info(vty, bgp, afi, safi);
-
-       if (safi == SAFI_FLOWSPEC)
-               bgp_fs_config_write_pbr(vty, bgp, afi, safi);
-
-       if (safi == SAFI_UNICAST) {
-               bgp_vpn_policy_config_write_afi(vty, bgp, afi);
-               if (CHECK_FLAG(bgp->af_flags[afi][safi],
-                              BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
-
-                       vty_out(vty, "  export vpn\n");
-               }
-               if (CHECK_FLAG(bgp->af_flags[afi][safi],
-                              BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
-
-                       vty_out(vty, "  import vpn\n");
-               }
-               if (CHECK_FLAG(bgp->af_flags[afi][safi],
-                              BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
-                       char *name;
-
-                       for (ALL_LIST_ELEMENTS_RO(
-                                    bgp->vpn_policy[afi].import_vrf, node,
-                                    name))
-                               vty_out(vty, "  import vrf %s\n", name);
-               }
-       }
-
-       vty_endframe(vty, " exit-address-family\n");
-}
-
-int bgp_config_write(struct vty *vty)
-{
-       struct bgp *bgp;
-       struct peer_group *group;
-       struct peer *peer;
-       struct listnode *node, *nnode;
-       struct listnode *mnode, *mnnode;
-
-       if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER)
-               vty_out(vty, "bgp route-map delay-timer %u\n",
-                       bm->rmap_update_timer);
-
-       /* BGP configuration. */
-       for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
-
-               /* skip all auto created vrf as they dont have user config */
-               if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
-                       continue;
-
-               /* Router bgp ASN */
-               vty_out(vty, "router bgp %u", bgp->as);
-
-               if (bgp->name)
-                       vty_out(vty, " %s %s",
-                               (bgp->inst_type  == BGP_INSTANCE_TYPE_VIEW)
-                               ? "view" : "vrf", bgp->name);
-               vty_out(vty, "\n");
-
-               /* BGP fast-external-failover. */
-               if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
-                       vty_out(vty, " no bgp fast-external-failover\n");
-
-               /* BGP router ID. */
-               if (bgp->router_id_static.s_addr != 0)
-                       vty_out(vty, " bgp router-id %s\n",
-                               inet_ntoa(bgp->router_id_static));
-
-               /* BGP log-neighbor-changes. */
-               if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)
-                   != DFLT_BGP_LOG_NEIGHBOR_CHANGES)
-                       vty_out(vty, " %sbgp log-neighbor-changes\n",
-                               bgp_flag_check(bgp,
-                                              BGP_FLAG_LOG_NEIGHBOR_CHANGES)
-                                       ? ""
-                                       : "no ");
-
-               /* BGP configuration. */
-               if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
-                       vty_out(vty, " bgp always-compare-med\n");
-
-               /* RFC8212 default eBGP policy. */
-               if (bgp->ebgp_requires_policy
-                   == DEFAULT_EBGP_POLICY_ENABLED)
-                       vty_out(vty, " bgp ebgp-requires-policy\n");
-
-               /* draft-ietf-idr-deprecate-as-set-confed-set */
-               if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
-                       vty_out(vty, " bgp reject-as-sets\n");
-
-               /* BGP default ipv4-unicast. */
-               if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4))
-                       vty_out(vty, " no bgp default ipv4-unicast\n");
-
-               /* BGP default local-preference. */
-               if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
-                       vty_out(vty, " bgp default local-preference %u\n",
-                               bgp->default_local_pref);
-
-               /* BGP default show-hostname */
-               if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
-                   != DFLT_BGP_SHOW_HOSTNAME)
-                       vty_out(vty, " %sbgp default show-hostname\n",
-                               bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
-                                       ? ""
-                                       : "no ");
-
-               /* BGP default subgroup-pkt-queue-max. */
-               if (bgp->default_subgroup_pkt_queue_max
-                   != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
-                       vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
-                               bgp->default_subgroup_pkt_queue_max);
-
-               /* BGP client-to-client reflection. */
-               if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
-                       vty_out(vty, " no bgp client-to-client reflection\n");
-
-               /* BGP cluster ID. */
-               if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID))
-                       vty_out(vty, " bgp cluster-id %s\n",
-                               inet_ntoa(bgp->cluster_id));
-
-               /* Disable ebgp connected nexthop check */
-               if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
-                       vty_out(vty,
-                               " bgp disable-ebgp-connected-route-check\n");
-
-               /* Confederation identifier*/
-               if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
-                       vty_out(vty, " bgp confederation identifier %u\n",
-                               bgp->confed_id);
-
-               /* Confederation peer */
-               if (bgp->confed_peers_cnt > 0) {
-                       int i;
-
-                       vty_out(vty, " bgp confederation peers");
-
-                       for (i = 0; i < bgp->confed_peers_cnt; i++)
-                               vty_out(vty, " %u", bgp->confed_peers[i]);
-
-                       vty_out(vty, "\n");
-               }
-
-               /* BGP deterministic-med. */
-               if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
-                   != DFLT_BGP_DETERMINISTIC_MED)
-                       vty_out(vty, " %sbgp deterministic-med\n",
-                               bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
-                                       ? ""
-                                       : "no ");
-
-               /* BGP update-delay. */
-               bgp_config_write_update_delay(vty, bgp);
-
-               if (bgp->v_maxmed_onstartup
-                   != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) {
-                       vty_out(vty, " bgp max-med on-startup %u",
-                               bgp->v_maxmed_onstartup);
-                       if (bgp->maxmed_onstartup_value
-                           != BGP_MAXMED_VALUE_DEFAULT)
-                               vty_out(vty, " %u",
-                                       bgp->maxmed_onstartup_value);
-                       vty_out(vty, "\n");
-               }
-               if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) {
-                       vty_out(vty, " bgp max-med administrative");
-                       if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT)
-                               vty_out(vty, " %u", bgp->maxmed_admin_value);
-                       vty_out(vty, "\n");
-               }
-
-               /* write quanta */
-               bgp_config_write_wpkt_quanta(vty, bgp);
-               /* read quanta */
-               bgp_config_write_rpkt_quanta(vty, bgp);
-
-               /* coalesce time */
-               bgp_config_write_coalesce_time(vty, bgp);
-
-               /* BGP graceful-restart. */
-               if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
-                       vty_out(vty,
-                               " bgp graceful-restart stalepath-time %u\n",
-                               bgp->stalepath_time);
-               if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
-                       vty_out(vty, " bgp graceful-restart restart-time %u\n",
-                               bgp->restart_time);
-               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART))
-                       vty_out(vty, " bgp graceful-restart\n");
-
-               /* BGP graceful-shutdown */
-               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
-                       vty_out(vty, " bgp graceful-shutdown\n");
-
-               /* BGP graceful-restart Preserve State F bit. */
-               if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD))
-                       vty_out(vty,
-                               " bgp graceful-restart preserve-fw-state\n");
-
-               /* BGP bestpath method. */
-               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE))
-                       vty_out(vty, " bgp bestpath as-path ignore\n");
-               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED))
-                       vty_out(vty, " bgp bestpath as-path confed\n");
-
-               if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
-                       if (bgp_flag_check(bgp,
-                                          BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
-                               vty_out(vty,
-                                       " bgp bestpath as-path multipath-relax as-set\n");
-                       } else {
-                               vty_out(vty,
-                                       " bgp bestpath as-path multipath-relax\n");
-                       }
-               }
-
-               if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
-                       vty_out(vty,
-                               " bgp route-reflector allow-outbound-policy\n");
-               }
-               if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID))
-                       vty_out(vty, " bgp bestpath compare-routerid\n");
-               if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)
-                   || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) {
-                       vty_out(vty, " bgp bestpath med");
-                       if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED))
-                               vty_out(vty, " confed");
-                       if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST))
-                               vty_out(vty, " missing-as-worst");
-                       vty_out(vty, "\n");
-               }
-
-               /* BGP network import check. */
-               if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
-                   != DFLT_BGP_IMPORT_CHECK)
-                       vty_out(vty, " %sbgp network import-check\n",
-                               bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
-                                       ? ""
-                                       : "no ");
-
-               /* BGP timers configuration. */
-               if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE
-                   && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
-                       vty_out(vty, " timers bgp %u %u\n",
-                               bgp->default_keepalive, bgp->default_holdtime);
-
-               /* peer-group */
-               for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
-                       bgp_config_write_peer_global(vty, bgp, group->conf);
-               }
-
-               /* Normal neighbor configuration. */
-               for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
-                       if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
-                               bgp_config_write_peer_global(vty, bgp, peer);
-               }
-
-               /* listen range and limit for dynamic BGP neighbors */
-               bgp_config_write_listen(vty, bgp);
-
-               /*
-                * BGP default autoshutdown neighbors
-                *
-                * This must be placed after any peer and peer-group
-                * configuration, to avoid setting all peers to shutdown after
-                * a daemon restart, which is undesired behavior. (see #2286)
-                */
-               if (bgp->autoshutdown)
-                       vty_out(vty, " bgp default shutdown\n");
-
-               /* IPv4 unicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
-
-               /* IPv4 multicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST);
-
-               /* IPv4 labeled-unicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST);
-
-               /* IPv4 VPN configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN);
-
-               /* ENCAPv4 configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP);
-
-               /* FLOWSPEC v4 configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC);
-
-               /* IPv6 unicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST);
-
-               /* IPv6 multicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST);
-
-               /* IPv6 labeled-unicast configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6,
-                                       SAFI_LABELED_UNICAST);
-
-               /* IPv6 VPN configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
-
-               /* ENCAPv6 configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP);
-
-               /* FLOWSPEC v6 configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC);
-
-               /* EVPN configuration.  */
-               bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
-
-               hook_call(bgp_inst_config_write, bgp, vty);
-
-#if ENABLE_BGP_VNC
-               bgp_rfapi_cfg_write(vty, bgp);
-#endif
-
-               vty_out(vty, "!\n");
-       }
-       return 0;
-}
-
 void bgp_master_init(struct thread_master *master, const int buffer_size)
 {
        qobj_init();
index 568438b0cea97a799c2e91b6ee1fb49653406758..4c4787ed5b9902d0c03ce88733238569c9fecf1b 100644 (file)
@@ -457,6 +457,7 @@ struct bgp {
        /* BGP default timer.  */
        uint32_t default_holdtime;
        uint32_t default_keepalive;
+       uint32_t default_connect_retry;
 
        /* BGP graceful restart */
        uint32_t restart_time;
@@ -987,6 +988,7 @@ struct peer {
 #define PEER_FLAG_WEIGHT                    (1 << 24) /* weight */
 #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 */
 
        enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
 
@@ -1119,9 +1121,6 @@ struct peer {
        /* timestamp when the last msg was written */
        _Atomic time_t last_update;
 
-       /* Send prefix count. */
-       unsigned long scount[AFI_MAX][SAFI_MAX];
-
        /* Notify data. */
        struct bgp_notify notify;
 
@@ -1172,6 +1171,9 @@ struct peer {
        uint16_t pmax_restart[AFI_MAX][SAFI_MAX];
 #define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
 
+       /* Send prefix count. */
+       uint32_t pmax_out[AFI_MAX][SAFI_MAX];
+
        /* allowas-in. */
        char allowas_in[AFI_MAX][SAFI_MAX];
 
@@ -1429,13 +1431,17 @@ struct bgp_nlri {
 #define BGP_EVENTS_MAX                          15
 
 /* BGP timers default value.  */
-/* note: the DFLT_ ones depend on compile-time "defaults" selection */
 #define BGP_INIT_START_TIMER                     1
-#define BGP_DEFAULT_HOLDTIME                      DFLT_BGP_HOLDTIME
-#define BGP_DEFAULT_KEEPALIVE                     DFLT_BGP_KEEPALIVE
+/* The following 3 are RFC defaults that are overridden in bgp_vty.c with
+ * version-/profile-specific values.  The values here do not matter, they only
+ * exist to provide a clear layering separation between core and CLI.
+ */
+#define BGP_DEFAULT_HOLDTIME                   180
+#define BGP_DEFAULT_KEEPALIVE                   60
+#define BGP_DEFAULT_CONNECT_RETRY              120
+
 #define BGP_DEFAULT_EBGP_ROUTEADV                0
 #define BGP_DEFAULT_IBGP_ROUTEADV                0
-#define BGP_DEFAULT_CONNECT_RETRY                 DFLT_BGP_TIMERS_CONNECT
 
 /* BGP default local preference.  */
 #define BGP_DEFAULT_LOCAL_PREF                 100
@@ -1480,6 +1486,7 @@ enum bgp_clear_type {
 
 /* BGP error codes.  */
 #define BGP_SUCCESS                               0
+#define BGP_CREATED                               1
 #define BGP_ERR_INVALID_VALUE                    -1
 #define BGP_ERR_INVALID_FLAG                     -2
 #define BGP_ERR_INVALID_AS                       -3
@@ -1626,7 +1633,8 @@ extern int bgp_confederation_peers_check(struct bgp *, as_t);
 extern int bgp_confederation_peers_add(struct bgp *, as_t);
 extern int bgp_confederation_peers_remove(struct bgp *, as_t);
 
-extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime);
+extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime,
+                         uint32_t connect_retry);
 extern int bgp_timers_unset(struct bgp *);
 
 extern int bgp_default_local_preference_set(struct bgp *, uint32_t);
index b97c8c3030f890374055c51f66ee0c05e112bf9e..352f5e8328c5b4ce6fc6b1416f5e7920bb5d9b3e 100644 (file)
@@ -78,7 +78,7 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig,
         * Make "new" a ghost attr copy of "orig"
         */
        memset(new, 0, sizeof(struct attr));
-       bgp_attr_dup(new, orig);
+       *new = *orig;
 
        /*
         * Set nexthop
@@ -616,7 +616,7 @@ encap_attr_export(struct attr *new, struct attr *orig,
         * Make "new" a ghost attr copy of "orig"
         */
        memset(new, 0, sizeof(struct attr));
-       bgp_attr_dup(new, orig);
+       *new = *orig;
 
        /*
         * Set nexthop
index 6e8969ad18c8b62de10c22e11ae27c610c1b8ece..ba6ef142579d450f3e67eb1d081c48c292928871 100644 (file)
@@ -356,7 +356,8 @@ static int process_unicast_route(struct bgp *bgp,            /* in */
         * all of the possible returns above.
         */
        memset(&hattr, 0, sizeof(struct attr));
-       bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+       /* hattr becomes a ghost attr */
+       hattr = *attr;
 
        if (rmap) {
                struct bgp_path_info info;
@@ -798,7 +799,8 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp,
         * all of the possible returns above.
         */
        memset(&hattr, 0, sizeof(struct attr));
-       bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+       /* hattr becomes a ghost attr */
+       hattr = *attr;
 
        if (rmap) {
                struct bgp_path_info info;
@@ -1000,7 +1002,8 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix,
         * all of the possible returns above.
         */
        memset(&hattr, 0, sizeof(struct attr));
-       bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+       /* hattr becomes a ghost attr */
+       hattr = *attr;
 
        if (rmap) {
                struct bgp_path_info path;
@@ -1779,7 +1782,7 @@ static void vnc_import_bgp_exterior_add_route_it(
 
                                /* use local_pref from unicast route */
                                memset(&new_attr, 0, sizeof(struct attr));
-                               bgp_attr_dup(&new_attr, bpi_interior->attr);
+                               new_attr = *bpi_interior->attr;
                                if (info->attr->flag
                                    & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
                                        new_attr.local_pref =
@@ -2104,7 +2107,7 @@ void vnc_import_bgp_exterior_add_route_interior(
 
                        /* use local_pref from unicast route */
                        memset(&new_attr, 0, sizeof(struct attr));
-                       bgp_attr_dup(&new_attr, bpi_interior->attr);
+                       new_attr = *bpi_interior->attr;
                        if (bpi_exterior
                            && (bpi_exterior->attr->flag
                                & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
@@ -2240,7 +2243,7 @@ void vnc_import_bgp_exterior_add_route_interior(
 
                                /* use local_pref from unicast route */
                                memset(&new_attr, 0, sizeof(struct attr));
-                               bgp_attr_dup(&new_attr, bpi_interior->attr);
+                               new_attr = *bpi_interior->attr;
                                if (bpi_exterior
                                    && (bpi_exterior->attr->flag
                                        & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
@@ -2360,7 +2363,7 @@ void vnc_import_bgp_exterior_add_route_interior(
 
                        /* use local_pref from unicast route */
                        memset(&new_attr, 0, sizeof(struct attr));
-                       bgp_attr_dup(&new_attr, bpi_interior->attr);
+                       new_attr = *bpi_interior->attr;
                        if (bpi_exterior
                            && (bpi_exterior->attr->flag
                                & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
@@ -2550,7 +2553,7 @@ void vnc_import_bgp_exterior_del_route_interior(
 
                                /* use local_pref from unicast route */
                                memset(&new_attr, 0, sizeof(struct attr));
-                               bgp_attr_dup(&new_attr, bpi->attr);
+                               new_attr = *bpi->attr;
                                if (bpi_exterior
                                    && (bpi_exterior->attr->flag
                                        & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
index b338fd4f3dbc6081c02a3d9950180ecb52a58dc3..ff15248a9837da174f7a577c5301c0b39d27721a 100644 (file)
@@ -46,7 +46,7 @@ endif
 if BGP_BMP
 module_LTLIBRARIES += bgpd/bgpd_bmp.la
 endif
-man8 += $(MANBUILD)/bgpd.8
+man8 += $(MANBUILD)/frr-bgpd.8
 endif
 
 bgpd_libbgp_a_SOURCES = \
@@ -199,14 +199,6 @@ bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c
 bgpd_bgpd_CFLAGS = $(AM_CFLAGS)
 bgpd_bgp_btoa_CFLAGS = $(AM_CFLAGS)
 
-if ENABLE_BGP_VNC
-bgpd_bgpd_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c
-bgpd_bgpd_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC)
-
-bgpd_bgp_btoa_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c
-bgpd_bgp_btoa_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC)
-endif
-
 # RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am
 bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM)
 bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM)
index 6147ebf0d8db79d4609572f40f5925c2047a31e4..c8371f304e5177a1a475e56ec3508cb7f69f55aa 100755 (executable)
@@ -7,7 +7,7 @@
 ##
 AC_PREREQ([2.60])
 
-AC_INIT([frr], [7.3-dev], [https://github.com/frrouting/frr/issues])
+AC_INIT([frr], [7.4-dev], [https://github.com/frrouting/frr/issues])
 PACKAGE_URL="https://frrouting.org/"
 AC_SUBST([PACKAGE_URL])
 PACKAGE_FULLNAME="FRRouting"
@@ -30,7 +30,7 @@ build_clippy="true"
 
 dnl case 1: external clippy
 if test -n "$with_clippy" -a "$with_clippy" != "no" -a "$with_clippy" != "yes"; then
-  if test "$enable_clippy_only" == "yes"; then
+  if test "$enable_clippy_only" = "yes"; then
     AC_MSG_ERROR([--enable-clippy-only does not make sense with --with-clippy])
   fi
 
@@ -249,7 +249,18 @@ if test "x${enable_gcov}" = "xyes"; then
    fi
 
    LDFLAGS="${LDFLAGS} -lgcov"
-elif test "x${enable_dev_build}" = "xyes"; then
+fi
+
+if test "x${enable_clang_coverage}" = "xyes"; then
+   AC_C_FLAG([-fprofile-instr-generate], [
+     AC_MSG_ERROR([$CC does not support -fprofile-instr-generate.])
+   ])
+   AC_C_FLAG([-fcoverage-mapping], [
+     AC_MSG_ERROR([$CC does not support -fcoverage-mapping.])
+   ])
+fi
+
+if test "x${enable_dev_build}" = "xyes"; then
    AC_DEFINE([DEV_BUILD], [1], [Build for development])
    if test "z$orig_cflags" = "z"; then
       AC_C_FLAG([-g3])
@@ -331,7 +342,14 @@ if test "$enable_memory_sanitizer" = "yes"; then
   AC_C_FLAG([-fsanitize=memory -fPIE -pie], [
     AC_MSG_ERROR([$CC does not support Memory Sanitizer.])
   ], [
-    SAN_FLAGS="-fsanitize=memory -fPIE -pie"
+    SAN_FLAGS="$SAN_FLAGS -fsanitize=memory -fPIE -pie"
+  ])
+fi
+if test "$enable_undefined_sanitizer" = "yes"; then
+  AC_C_FLAG([-fsanitize=undefined], [
+    AC_MSG_ERROR([$CC does not support UndefinedBehaviorSanitizer.])
+  ], [
+    SAN_FLAGS="$SAN_FLAGS -fsanitize=undefined"
   ])
 fi
 AC_SUBST([SAN_FLAGS])
@@ -567,7 +585,9 @@ AC_ARG_ENABLE([clippy-only],
 AC_ARG_ENABLE([numeric_version],
   AS_HELP_STRING([--enable-numeric-version], [Only numeric digits allowed in version (for Alpine)]))
 AC_ARG_ENABLE([gcov],
-  AS_HELP_STRING([--enable-gcov], [Add code coverage information]))
+  AS_HELP_STRING([--enable-gcov], [Collect coverage information with gcov]))
+AC_ARG_ENABLE([clang_coverage],
+  AS_HELP_STRING([--enable-clang-coverage], [Collect coverage information with Clang Coverage]))
 AC_ARG_ENABLE([bfdd],
   AS_HELP_STRING([--disable-bfdd], [do not build bfdd]))
 AC_ARG_ENABLE([address-sanitizer],
@@ -576,6 +596,8 @@ AC_ARG_ENABLE([thread-sanitizer],
   AS_HELP_STRING([--enable-thread-sanitizer], [enable ThreadSanitizer support for detecting data races]))
 AC_ARG_ENABLE([memory-sanitizer],
   AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads]))
+AC_ARG_ENABLE([undefined-sanitizer],
+  AS_HELP_STRING([--undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior]))
 AC_ARG_WITH([crypto],
   AS_HELP_STRING([--with-crypto=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)]))
 
@@ -643,6 +665,7 @@ fi
 
 if test "${enable_datacenter}" = "yes" ; then
   AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter])
+  AC_MSG_WARN([The --enable-datacenter compile time option is deprecated.  Please modify the init script to pass -F datacenter to the daemons instead.])
   DFLT_NAME="datacenter"
 else
   DFLT_NAME="traditional"
@@ -1060,6 +1083,8 @@ FRR_INCLUDES
 
 dnl V6 headers are checked below, after we check for v6
 
+is_linux=false
+
 AC_MSG_CHECKING([which operating system interface to use])
 case "$host_os" in
   sunos* | solaris2*)
@@ -1088,6 +1113,8 @@ case "$host_os" in
     dnl how to fix it but no real progress on implementation
     dnl when they fix it, remove this
     AC_DEFINE([IPV6_MINHOPCOUNT], [73], [Linux ipv6 Min Hop Count])
+
+    is_linux=true
     ;;
   openbsd*)
     AC_MSG_RESULT([OpenBSD])
@@ -1115,6 +1142,7 @@ case "$host_os" in
     ;;
 esac
 AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"])
+AM_CONDITIONAL([LINUX], [${is_linux}])
 
 AC_SYS_LARGEFILE
 
@@ -1153,6 +1181,7 @@ dnl ##########################################################################
 #
 # Logic for protobuf support.
 #
+PROTO3=false
 if test "$enable_protobuf" = "yes"; then
   # Check for protoc & protoc-c
 
@@ -1167,9 +1196,15 @@ if test "$enable_protobuf" = "yes"; then
   PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [
     AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found.  Install protobuf-c.])
   ])
-  AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], [
-    AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found.  Install protobuf-c.])
-  ])
+
+  PROTO3=true
+  AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h],
+                  [AC_CHECK_DECLS(PROTOBUF_C_LABEL_NONE,
+                                  AC_DEFINE([HAVE_PROTOBUF_VERSION_3],
+                                            [1], [Have Protobuf version 3]),
+                                  [PROTO3=false],
+                                  [#include <google/protobuf-c/protobuf-c.h>])],
+                  [PROTO3=false && AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found.  Install protobuf-c.])])
 
   AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf])
 fi
@@ -2078,6 +2113,19 @@ if test x"${enable_backtrace}" != x"no" ; then
     AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind])
     backtrace_ok=yes
   ], [
+    true
+  ])
+
+  if test "$backtrace_ok" = "no"; then
+    AC_CHECK_HEADER([unwind.h], [
+      AC_SEARCH_LIBS([unw_getcontext], [unwind], [
+        AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind])
+        backtrace_ok=yes
+      ])
+    ])
+  fi
+
+  if test "$backtrace_ok" = "no"; then
     case "$host_os" in
     sunos* | solaris2*)
       AC_CHECK_FUNCS([printstack], [
@@ -2094,7 +2142,7 @@ if test x"${enable_backtrace}" != x"no" ; then
         ],, [-lm])
       ])
     fi
-  ])
+  fi
 
   if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then
     dnl user explicitly requested backtrace but we failed to find support
@@ -2265,6 +2313,7 @@ AM_CONDITIONAL([SNMP], [test "x$SNMP_METHOD" = "xagentx"])
 AM_CONDITIONAL([IRDP], [$IRDP])
 AM_CONDITIONAL([FPM], [test "x$enable_fpm" = "xyes"])
 AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$enable_protobuf" = "xyes"])
+AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3])
 dnl daemons
 AM_CONDITIONAL([VTYSH], [test "x$VTYSH" = "xvtysh"])
 AM_CONDITIONAL([ZEBRA], [test "${enable_zebra}" != "no"])
index 47a353310d9873448628e6adcd21f85b410a1f71..cbd70f82f65c3443e141c29f24d582e2b4781d2e 100644 (file)
@@ -61,7 +61,7 @@ OpenSSL now is not compatible with the GNU GENERAL PUBLIC LICENSE (GPL)
 licence that FRR is distributed under. For more explanation read:
   http://www.gnome.org/~markmc/openssl-and-the-gpl.html
   http://www.gnu.org/licenses/gpl-faq.html#GPLIncompatibleLibs
-Updating the licence to explecitly allow linking against OpenSSL
+Updating the licence to explicitly allow linking against OpenSSL
 would requite the affirmation of all people that ever contributed
 a significant part to Zebra / Quagga or FRR and thus are the collective
 "copyright holder". That's too much work. Using a shrinked down 
index 09bddf0fc66672788615c304b80d365b3ff762d1..5917c0da8447957cef99c76dc7c41de407d1d392 100644 (file)
@@ -8,6 +8,7 @@ usr/lib/frr/*.sh
 usr/lib/frr/*d
 usr/lib/frr/watchfrr
 usr/lib/frr/zebra
+usr/lib/*/frr/modules/zebra_cumulus_mlag.so
 usr/lib/*/frr/modules/zebra_irdp.so
 usr/lib/*/frr/modules/zebra_fpm.so
 usr/lib/*/frr/modules/bgpd_bmp.so
index 1dc9122ac4b5a7c271a1798241f82d204b1f729a..a6b2b22f561cbb0d077b2c433fe562848fa66b41 100644 (file)
@@ -17,7 +17,7 @@
             # open, as well as the daemons, so always signal the daemons.
             # It's safe, a NOP if (only) syslog is being used.
             for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd \
-                pimd ripd ripngd zebra staticd fabricd; do
+                pimd ripd ripngd zebra pbrd staticd bfdd fabricd vrrpd; do
                 if [ -e /var/run/frr/$i.pid ] ; then
                     pids="$pids $(cat /var/run/frr/$i.pid)"
                 fi
index f5aa972304242fafeec4a5874f3cf4c285839521..5075fd763df40d1d29c138b598beb36c0c1f9598 100644 (file)
@@ -1,16 +1,16 @@
+doc/manpages/_build/man/frr-bgpd.8
+doc/manpages/_build/man/frr-eigrpd.8
+doc/manpages/_build/man/frr-fabricd.8
+doc/manpages/_build/man/frr-isisd.8
+doc/manpages/_build/man/frr-ldpd.8
+doc/manpages/_build/man/frr-nhrpd.8
+doc/manpages/_build/man/frr-ospf6d.8
+doc/manpages/_build/man/frr-ospfd.8
+doc/manpages/_build/man/frr-pimd.8
+doc/manpages/_build/man/frr-ripd.8
+doc/manpages/_build/man/frr-ripngd.8
+doc/manpages/_build/man/frr-watchfrr.8
+doc/manpages/_build/man/frr-zebra.8
 doc/manpages/_build/man/frr.1
-doc/manpages/_build/man/bgpd.8
-doc/manpages/_build/man/pimd.8
-doc/manpages/_build/man/eigrpd.8
-doc/manpages/_build/man/ldpd.8
-doc/manpages/_build/man/nhrpd.8
-doc/manpages/_build/man/ospf6d.8
-doc/manpages/_build/man/ospfd.8
-doc/manpages/_build/man/ripd.8
-doc/manpages/_build/man/ripngd.8
-doc/manpages/_build/man/vtysh.1
-doc/manpages/_build/man/zebra.8
-doc/manpages/_build/man/isisd.8
-doc/manpages/_build/man/watchfrr.8
 doc/manpages/_build/man/mtracebis.8
-doc/manpages/_build/man/fabricd.8
+doc/manpages/_build/man/vtysh.1
diff --git a/defaults.h b/defaults.h
deleted file mode 100644 (file)
index fd38aae..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * FRR switchable defaults.
- * Copyright (C) 2017  David Lamparter for NetDEF, Inc.
- *
- * This file is part of FRRouting (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 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_DEFAULTS_H
-#define _FRR_DEFAULTS_H
-
-#include "config.h"
-
-#ifdef HAVE_DATACENTER
-
-#define DFLT_BGP_IMPORT_CHECK                  1
-#define DFLT_BGP_TIMERS_CONNECT                        10
-#define DFLT_BGP_HOLDTIME                      9
-#define DFLT_BGP_KEEPALIVE                     3
-#define DFLT_BGP_LOG_NEIGHBOR_CHANGES          1
-#define DFLT_BGP_SHOW_HOSTNAME                 1
-#define DFLT_BGP_DETERMINISTIC_MED             1
-
-#define DFLT_OSPF_LOG_ADJACENCY_CHANGES                1
-#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES       1
-
-#else  /* !HAVE_DATACENTER */
-
-#define DFLT_BGP_IMPORT_CHECK                  0
-#define DFLT_BGP_TIMERS_CONNECT                        120
-#define DFLT_BGP_HOLDTIME                      180
-#define DFLT_BGP_KEEPALIVE                     60
-#define DFLT_BGP_LOG_NEIGHBOR_CHANGES          0
-#define DFLT_BGP_SHOW_HOSTNAME                 0
-#define DFLT_BGP_DETERMINISTIC_MED             0
-
-#define DFLT_OSPF_LOG_ADJACENCY_CHANGES                0
-#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES       0
-
-#endif /* !HAVE_DATACENTER */
-
-#endif /* _FRR_DEFAULTS_H */
index 3d9edbe3a1b2e3222b073cc3e7640faba1ce846d..04c6b922cec9542ceb00d6c7c764c0e0ed68c54d 100644 (file)
@@ -213,7 +213,7 @@ Install daemon config file
 
 .. code-block:: shell
 
-   sudo install -p -m 644 redhat/daemons /etc/frr/
+   sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/
    sudo chown frr:frr /etc/frr/daemons
 
 Edit /etc/frr/daemons as needed to select the required daemons
@@ -241,12 +241,12 @@ Load the modified sysctl's on the system:
 
    sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
 
-Add init.d startup files
-^^^^^^^^^^^^^^^^^^^^^^^^
+Add init.d startup file
+^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: shell
 
-   sudo install -p -m 755 redhat/frr.init /etc/init.d/frr
+   sudo install -p -m 755 tools/frr /etc/init.d/frr
    sudo chkconfig --add frr
 
 Enable FRR daemon at startup
index cd90d41ffb82d838f50f8f0689846b125873efd0..eb97150d67a31a0857e481c5aa4330bb2c1ac5c2 100644 (file)
@@ -104,7 +104,7 @@ Install daemon config file
 
 ::
 
-    sudo install -p -m 644 redhat/daemons /etc/frr/
+    sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/
     sudo chown frr:frr /etc/frr/daemons
 
 Edit /etc/frr/daemons as needed to select the required daemons
@@ -133,13 +133,12 @@ Load the modified sysctl's on the system:
 
     sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
 
-Install frr Service and redhat init files
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Install frr Service
+^^^^^^^^^^^^^^^^^^^
 
 ::
 
-    sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service
-    sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
+    sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
 
 Register the systemd files
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst
new file mode 100644 (file)
index 0000000..75beb53
--- /dev/null
@@ -0,0 +1,155 @@
+CentOS 8
+========
+
+This document describes installation from source. If you want to build an RPM,
+see :ref:`packaging-redhat`.
+
+Install required packages
+-------------------------
+
+Add packages:
+
+::
+
+    sudo dnf install --enablerepo=PowerTools git autoconf pcre-devel \
+      automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \
+      groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \
+      c-ares-devel python2-devel systemd-devel libcap-devel
+
+.. include:: building-libyang.rst
+
+Get FRR, compile it and install it (from Git)
+---------------------------------------------
+
+**This assumes you want to build and install FRR from source and not
+using any packages**
+
+Add frr groups and user
+^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo groupadd -g 92 frr
+    sudo groupadd -r -g 85 frrvty
+    sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \
+      -c "FRR FRRouting suite" -d /var/run/frr frr
+
+Download Source, configure and compile it
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+(You may prefer different options on configure statement. These are just
+an example.)
+
+::
+
+    git clone https://github.com/frrouting/frr.git frr
+    cd frr
+    ./bootstrap.sh
+    ./configure \
+        --bindir=/usr/bin \
+        --sbindir=/usr/lib/frr \
+        --sysconfdir=/etc/frr \
+        --libdir=/usr/lib/frr \
+        --libexecdir=/usr/lib/frr \
+        --localstatedir=/var/run/frr \
+        --with-moduledir=/usr/lib/frr/modules \
+        --enable-snmp=agentx \
+        --enable-multipath=64 \
+        --enable-user=frr \
+        --enable-group=frr \
+        --enable-vty-group=frrvty \
+       --enable-systemd=yes \
+        --disable-exampledir \
+        --disable-ldpd \
+        --enable-fpm \
+        --with-pkg-git-version \
+        --with-pkg-extra-version=-MyOwnFRRVersion \
+       SPHINXBUILD=/usr/bin/sphinx-build
+    make
+    make check
+    sudo make install
+
+Create empty FRR configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo mkdir /var/log/frr
+    sudo mkdir /etc/frr
+    sudo touch /etc/frr/zebra.conf
+    sudo touch /etc/frr/bgpd.conf
+    sudo touch /etc/frr/ospfd.conf
+    sudo touch /etc/frr/ospf6d.conf
+    sudo touch /etc/frr/isisd.conf
+    sudo touch /etc/frr/ripd.conf
+    sudo touch /etc/frr/ripngd.conf
+    sudo touch /etc/frr/pimd.conf
+    sudo touch /etc/frr/nhrpd.conf
+    sudo touch /etc/frr/eigrpd.conf
+    sudo touch /etc/frr/babeld.conf
+    sudo chown -R frr:frr /etc/frr/
+    sudo touch /etc/frr/vtysh.conf
+    sudo chown frr:frrvty /etc/frr/vtysh.conf
+    sudo chmod 640 /etc/frr/*.conf
+
+Install daemon config file
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/
+    sudo chown frr:frr /etc/frr/daemons
+
+Edit /etc/frr/daemons as needed to select the required daemons
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
+Enable the daemons as required by changing the value to ``yes``
+
+Enable IP & IPv6 forwarding
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the
+following content:
+
+::
+
+    # Sysctl for routing
+    #
+    # Routing: We need to forward packets
+    net.ipv4.conf.all.forwarding=1
+    net.ipv6.conf.all.forwarding=1
+
+Load the modified sysctl's on the system:
+
+::
+
+    sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
+
+Install frr Service
+^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
+
+Register the systemd files
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo systemctl preset frr.service
+
+Enable required frr at startup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo systemctl enable frr
+
+Reboot or start FRR manually
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+    sudo systemctl start frr
index d8405eb3519cde9fd54108dfe5c5bb669cbf330e..4ab59490fd60e7c808e5f7aa69a005b3c4c73dc6 100644 (file)
@@ -110,13 +110,12 @@ And load the kernel modules on the running system:
       sudo systemctl stop firewalld.service
       sudo iptables -F
 
-Install service files
-^^^^^^^^^^^^^^^^^^^^^
+Install frr Service
+^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: console
 
-   sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service
-   sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
+   sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
    sudo systemctl enable frr
 
 Enable daemons
index c13fb10ffc290da4e88d4b4a2615f3b056431a61..859f612313f85caa73aabaae5202a8766a3de5d8 100644 (file)
@@ -7,9 +7,11 @@ Building FRR
 .. toctree::
    :maxdepth: 2
 
+   static-linking
    building-frr-for-alpine
    building-frr-for-centos6
    building-frr-for-centos7
+   building-frr-for-centos8
    building-frr-for-debian8
    building-frr-for-debian9
    building-frr-for-fedora
index d3440461485c1fbe27e72ff7dec0dd342733774b..d9c4c5bf0c15245d4082feb0f9b9fe23f25eb9f7 100644 (file)
@@ -3,7 +3,7 @@
 Packaging Red Hat
 =================
 
-Tested on CentOS 6, CentOS 7 and Fedora 24.
+Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24.
 
 1. On CentOS 6, refer to :ref:`building-centos6` for details on installing
    sufficiently up-to-date package versions to enable building FRR.
@@ -22,13 +22,31 @@ Tested on CentOS 6, CentOS 7 and Fedora 24.
 
       yum install systemd-devel
 
+   .. note::
+
+     For CentOS 8 you need to install ``platform-python-devel`` package
+     to provide ``/usr/bin/pathfix.py``::
+
+       yum install platform-python-devel
+
+   .. warning::
+
+     ``python2-sphinx`` is not shipped for CentOS 8.
+     Development reached the end of life for Python 2.
+     We need to install it using ```pip``::
+
+        pip2 install sphinx
+
    If ``yum`` is not present on your system, use ``dnf`` instead.
 
-3. Checkout FRR::
+   You should enable ``PowerTools`` repo if using CentOS 8 which
+   is disabled by default.
+
+4. Checkout FRR::
 
       git clone https://github.com/frrouting/frr.git frr
 
-4. Run Bootstrap and make distribution tar.gz::
+5. Run Bootstrap and make distribution tar.gz::
 
       cd frr
       ./bootstrap.sh
@@ -40,7 +58,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24.
       The only ``configure`` option respected when building RPMs is
       ``--with-pkg-extra-version``.
 
-5. Create RPM directory structure and populate with sources::
+6. Create RPM directory structure and populate with sources::
 
      mkdir rpmbuild
      mkdir rpmbuild/SOURCES
@@ -48,7 +66,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24.
      cp redhat/*.spec rpmbuild/SPECS/
      cp frr*.tar.gz rpmbuild/SOURCES/
 
-6. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed.
+7. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed.
 
    Look at the beginning of the file and adjust the following parameters to
    enable or disable features as required::
@@ -73,7 +91,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24.
       %{!?with_pimd:          %global  with_pimd          1 }
       %{!?with_rpki:          %global  with_rpki          0 }
 
-7. Build the RPM::
+8. Build the RPM::
 
       rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec
 
diff --git a/doc/developer/static-linking.rst b/doc/developer/static-linking.rst
new file mode 100644 (file)
index 0000000..bc33207
--- /dev/null
@@ -0,0 +1,98 @@
+.. _static-linking:
+
+Static Linking
+==============
+
+This document describes how to build FRR without hard dependencies on shared
+libraries. Note that it's not possible to build FRR *completely* statically.
+This document just covers how to statically link the dependencies that aren't
+likely to be present on a given platform - libfrr and libyang. The resultant
+binaries should still be fairly portable. For example, here is the DSO
+dependency list for `bgpd` after using these steps:
+
+.. code-block::
+
+   $ ldd bgpd
+        linux-vdso.so.1 (0x00007ffe3a989000)
+        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9dc10c0000)
+        libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f9dc0eba000)
+        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9dc0b1c000)
+        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9dc0918000)
+        libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f9dc06e0000)
+        libjson-c.so.3 => /lib/x86_64-linux-gnu/libjson-c.so.3 (0x00007f9dc04d5000)
+        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9dc02cd000)
+        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9dc00ae000)
+        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9dbfe96000)
+        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9dbfaa5000)
+        /lib64/ld-linux-x86-64.so.2 (0x00007f9dc1449000)
+
+Procedure
+---------
+Note that these steps have only been tested with LLVM 9 / clang.
+
+Today, libfrr can already be statically linked by passing these configure
+options::
+
+   --enable-static --enable-static-bin --enable-shared
+
+libyang is more complicated. You must build and install libyang as a static
+library. To do this, follow the usual libyang build procedure as listed in the
+FRR developer docs, but set the ``ENABLE_STATIC`` option in your cmake
+invocation. You also need to build with PIC enabled, which today is disabled
+when building libyang statically.
+
+The resultant cmake command is::
+
+   cmake -DENABLE_STATIC=ON -DENABLE_LYD_PRIV=ON \
+         -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+         -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
+         -DCMAKE_BUILD_TYPE:String="Release" ..
+
+This produces a bunch of ``.a`` static archives that need to ultimately be linked
+into FRR. However, not only is it 6 archives rather than the usual ``libyang.so``,
+you will now also need to link FRR with ``libpcre.a``. Ubuntu's ``libpcre3-dev``
+package provides this, but it hasn't been built with PIC enabled, so it's not
+usable for our purposes. So download ``libpcre`` from
+`SourceForge <https://sourceforge.net/projects/pcre/>`_, and build it
+like this:
+
+.. code-block::
+
+   ./configure --with-pic
+   make
+
+Hopefully you get a nice, usable, PIC ``libpcre.a``.
+
+So now we have to link all these static libraries into FRR. Rather than modify
+FRR to accomodate this, the best option is to create an archive with all of
+libyang's dependencies. Then to avoid making any changes to FRR build foo,
+rename this ``libyang.a`` and copy it over the usual static library location.
+Ugly but it works. To do this, go into your libyang build directory, which
+should have a bunch of ``.a`` files.  Copy ``libpcre.a`` into this directory.
+Write the following into a shell script and run it:
+
+.. code-block:: shell
+
+   #!/bin/bash
+   ar -M <<EOM
+     CREATE libyang_fat.a
+     ADDLIB libyang.a
+     ADDLIB libyangdata.a
+     ADDLIB libmetadata.a
+     ADDLIB libnacm.a
+     ADDLIB libuser_inet_types.a
+     ADDLIB libuser_yang_types.a
+     ADDLIB libpcre.a
+     SAVE
+     END
+   EOM
+   ranlib libyang_fat.a
+
+``libyang_fat.a`` is your archive. Now copy this over your install
+``libyang.a``, which on my machine is located at
+``/usr/lib/x86_64-linux-gnu/libyang.a`` (try ``locate libyang.a`` if not).
+
+Now when you build FRR with the static options enabled as above, clang should
+pick up the static libyang and link it, leaving you with FRR binaries that have
+no hard DSO dependencies beyond common system libraries. To verify, run ``ldd``
+over the resultant binaries.
index 2c49d6b87524792344574288e3bbaf922d2e9ed5..791f7679a6ff98d211584acda9c35b75530d8c74 100644 (file)
@@ -44,6 +44,7 @@ dev_RSTFILES = \
        doc/developer/packaging-redhat.rst \
        doc/developer/packaging.rst \
        doc/developer/rcu.rst \
+       doc/developer/static-linking.rst \
        doc/developer/testing.rst \
        doc/developer/topotests-snippets.rst \
        doc/developer/topotests.rst \
index c2e72e2ec5c1696ec349f334392aaad466331791..33ebe06d2fa4b033bafd47126e1bb6a06b7a716d 100644 (file)
@@ -79,6 +79,7 @@ If you prefer to manually build FRR, then use the following suggested config:
        --sysconfdir=/etc/frr \
        --enable-vtysh \
        --enable-pimd \
+       --enable-sharpd \
        --enable-multipath=64 \
        --enable-user=frr \
        --enable-group=frr \
index 6eef7532b3cc769d0c78250b587c69da91aec4a9..8ce3bdeeb27e2052bd7e8833129b6c8077f8e15a 100644 (file)
@@ -283,7 +283,10 @@ Pre-submission Checklist
    -  ``make test``
 
 - In the case of a major new feature or other significant change, document
-  plans for continued maintenance of the feature
+  plans for continued maintenance of the feature.  In addition it is a
+  requirement that automated testing must be written that exercises
+  the new feature within our existing CI infrastructure.  Also the 
+  addition of automated testing to cover any pull request is encouraged.
 
 .. _signing-off:
 
@@ -293,6 +296,11 @@ Code submitted to FRR must be signed off. We have the same requirements for
 using the signed-off-by process as the Linux kernel. In short, you must include
 a ``Signed-off-by`` tag in every patch.
 
+An easy way to do this is to use ``git commit -s`` where ``-s`` will automatically
+append a signed-off line to the end of your commit message. Also, if you commit
+and forgot to add the line you can use ``git commit --amend -s`` to add the
+signed-off line to the last commit.
+
 ``Signed-off-by`` is a developer's certification that they have the right to
 submit the patch for inclusion into the project. It is an agreement to the
 :ref:`Developer's Certificate of Origin <developers-certificate-of-origin>`.
@@ -447,6 +455,10 @@ Guidelines for code review
   may originate with a reviewer or document agreement reached on Slack,
   the Development mailing list, or the weekly technical meeting.
 
+- Reviewers may ask for new automated testing if they feel that the
+  code change is large enough/significant enough to warrant such
+  a requirement.
+
 
 Coding Practices & Style
 ========================
diff --git a/doc/manpages/bfdd.rst b/doc/manpages/bfdd.rst
deleted file mode 100644 (file)
index 1f8b147..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-****
-BFDD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: bfdd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a communication failure detection component that works with
-the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-.. include:: bfd-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/bgpd.rst b/doc/manpages/bgpd.rst
deleted file mode 100644 (file)
index 079aad8..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-****
-BGPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: bgpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -p, --bgp_port <port>
-
-   Set the bgp protocol's port number. When port number is 0, that means do not
-   listen bgp port.
-
-.. option:: -l, --listenon
-
-   Specify a specific IP address for bgpd to listen on, rather than its default
-   of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
-   address, or to run multiple bgpd processes on one host.
-
-.. option:: -n, --no_kernel
-
-   Do not install learned routes into the linux kernel.  This option is useful
-   for a route-reflector environment or if you are running multiple bgp
-   processes in the same namespace.  This option is different than the --no_zebra
-   option in that a ZAPI connection is made.
-
-.. option:: -S, --skip_runas
-
-   Skip the normal process of checking capabilities and changing user and group
-   information.
-
-.. option:: -e, --ecmp
-
-   Run BGP with a limited ecmp capability, that is different than what BGP
-   was compiled with.  The value specified must be greater than 0 and less
-   than or equal to the MULTIPATH_NUM specified on compilation.
-
-.. option:: -Z, --no_zebra
-
-   Do not communicate with zebra at all.  This is different than the --no_kernel
-   option in that we do not even open a ZAPI connection to the zebra process.
-
-.. option:: -s, --socket_size
-
-   When opening tcp connections to our peers, set the socket send buffer
-   size that the kernel will use for the peers socket.  This option
-   is only really useful at a very large scale.  Experimentation should
-   be done to see if this is helping or not at the scale you are running
-   at.
-
-LABEL MANAGER
--------------
-
-.. option:: -I, --int_num
-
-   Set zclient id. This is required when using Zebra label manager in proxy mode.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
index e7813d81765463c1c2266be12b17ab9143c7cb47..9121d38fe020a36cdc7bdcdfe971640683204305 100644 (file)
@@ -313,28 +313,28 @@ latex_documents = [
 fwfrr = "{0} routing engine for use with FRRouting."
 
 man_pages = [
-    ('bgpd', 'bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4- "), [], 8),
-    ('eigrpd', 'eigrpd', fwfrr.format("an EIGRP "), [], 8),
-    ('ospf6d', 'ospf6d', fwfrr.format("an OSPFv3 "), [], 8),
-    ('ospfd', 'ospfd', fwfrr.format("an OSPFv2 "), [], 8),
-    ('isisd', 'isisd', fwfrr.format("an IS-IS "), [], 8),
-    ('ospfclient', 'ospfclient', 'an example ospf-api client', [], 8),
-    ('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
-    ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
-    ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
-    ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8),
-    ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 8),
-    ('staticd', 'staticd', fwfrr.format("a static route manager "), [], 8),
-    ('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
-    ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
-    ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
-    ('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
-    ('watchfrr', 'watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8),
-    ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1),
+    ('frr-bfdd',       'frr-bfdd', fwfrr.format("a bfd"), [], 8),
+    ('frr-bgpd',       'frr-bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4-"), [], 8),
+    ('frr-eigrpd',     'frr-eigrpd', fwfrr.format("an EIGRP"), [], 8),
+    ('frr-fabricd',    'frr-fabricd', fwfrr.format("an OpenFabric"), [], 8),
+    ('frr-isisd',      'frr-isisd', fwfrr.format("an IS-IS"), [], 8),
+    ('frr-ldpd',       'frr-ldpd', fwfrr.format("an LDP"), [], 8),
+    ('frr-nhrpd',      'frr-nhrpd', fwfrr.format("a Next Hop Routing Protocol"), [], 8),
+    ('frr-ospf6d',     'frr-ospf6d', fwfrr.format("an OSPFv3"), [], 8),
+    ('frr-ospfclient', 'frr-ospfclient', 'an example ospf-api client', [], 8),
+    ('frr-ospfd',      'frr-ospfd', fwfrr.format("an OSPFv2"), [], 8),
+    ('frr-pbrd',       'frr-pbrd', fwfrr.format("a PBR"), [], 8),
+    ('frr-pimd',       'frr-pimd', fwfrr.format("a PIM"), [], 8),
+    ('frr-ripd',       'frr-ripd', fwfrr.format("a RIP"), [], 8),
+    ('frr-ripngd',     'frr-ripngd', fwfrr.format("a RIPNG"), [], 8),
+    ('frr-sharpd',     'frr-sharpd', fwfrr.format("a SHARP"), [], 8),
+    ('frr-staticd',    'frr-staticd', fwfrr.format("a static route manager"), [], 8),
+    ('frr-vrrpd',      'frr-vrrpd', fwfrr.format("a VRRP"), [], 8),
+    ('frr-watchfrr',   'frr-watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8),
+    ('frr-zebra',      'frr-zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
     ('frr', 'frr', 'a systemd interaction script', [], 1),
-    ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8),
-    ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8),
-    ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8),
+    ('mtracebis',  'mtracebis', "a multicast trace client", [], 8),
+    ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1),
 ]
 
 # -- Options for Texinfo output -------------------------------------------
index 2a6a9fd1bdf855bcd9d40109f9539018dd35e5d7..ac24cfa8dc3e78e15e01f5bb95e175331b660622 100644 (file)
@@ -1,3 +1,3 @@
 .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
 .. |synopsis-options-hv| replace:: [-h] [-v]
-.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8)
+.. |seealso-programs| replace:: frr-zebra(8), vtysh(1), frr-ripd(8), frr-ripngd(8), frr-ospfd(8), frr-ospf6d(8), frr-bgpd(8), frr-isisd(8), frr-babeld(8), frr-nhrpd(8), frr-pimd(8), frr-pbrd(8), frr-ldpd(8), frr-eigrpd(8), frr-staticd(8), frr-fabricd(8), frr-vrrpd(8), mtracebis(8)
diff --git a/doc/manpages/eigrpd.rst b/doc/manpages/eigrpd.rst
deleted file mode 100644 (file)
index bc82446..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-******
-EIGRPD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: eigrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/fabricd.rst b/doc/manpages/fabricd.rst
deleted file mode 100644 (file)
index c14c076..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-*******
-FABRICD
-*******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: fabricd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/frr-bfdd.rst b/doc/manpages/frr-bfdd.rst
new file mode 100644 (file)
index 0000000..1f8b147
--- /dev/null
@@ -0,0 +1,40 @@
+****
+BFDD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: bfdd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a communication failure detection component that works with
+the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+.. include:: bfd-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-bgpd.rst b/doc/manpages/frr-bgpd.rst
new file mode 100644 (file)
index 0000000..f7e2026
--- /dev/null
@@ -0,0 +1,82 @@
+****
+BGPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: bgpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -p, --bgp_port <port>
+
+   Set the bgp protocol's port number. When port number is 0, that means do not
+   listen bgp port.
+
+.. option:: -l, --listenon
+
+   Specify a specific IP address for bgpd to listen on, rather than its default
+   of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
+   address, or to run multiple bgpd processes on one host.
+
+.. option:: -n, --no_kernel
+
+   Do not install learned routes into the linux kernel.  This option is useful
+   for a route-reflector environment or if you are running multiple bgp
+   processes in the same namespace.  This option is different than the --no_zebra
+   option in that a ZAPI connection is made.
+
+.. option:: -e, --ecmp
+
+   Run BGP with a limited ecmp capability, that is different than what BGP
+   was compiled with.  The value specified must be greater than 0 and less
+   than or equal to the MULTIPATH_NUM specified on compilation.
+
+.. option:: -Z, --no_zebra
+
+   Do not communicate with zebra at all.  This is different than the --no_kernel
+   option in that we do not even open a ZAPI connection to the zebra process.
+
+.. option:: -s, --socket_size
+
+   When opening tcp connections to our peers, set the socket send buffer
+   size that the kernel will use for the peers socket.  This option
+   is only really useful at a very large scale.  Experimentation should
+   be done to see if this is helping or not at the scale you are running
+   at.
+
+LABEL MANAGER
+-------------
+
+.. option:: -I, --int_num
+
+   Set zclient id. This is required when using Zebra label manager in proxy mode.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-eigrpd.rst b/doc/manpages/frr-eigrpd.rst
new file mode 100644 (file)
index 0000000..bc82446
--- /dev/null
@@ -0,0 +1,38 @@
+******
+EIGRPD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: eigrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-fabricd.rst b/doc/manpages/frr-fabricd.rst
new file mode 100644 (file)
index 0000000..c14c076
--- /dev/null
@@ -0,0 +1,38 @@
+*******
+FABRICD
+*******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: fabricd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-isisd.rst b/doc/manpages/frr-isisd.rst
new file mode 100644 (file)
index 0000000..68761f6
--- /dev/null
@@ -0,0 +1,38 @@
+*****
+ISISD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: isisd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-ldpd.rst b/doc/manpages/frr-ldpd.rst
new file mode 100644 (file)
index 0000000..113f066
--- /dev/null
@@ -0,0 +1,38 @@
+****
+LDPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ldpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-nhrpd.rst b/doc/manpages/frr-nhrpd.rst
new file mode 100644 (file)
index 0000000..cae01c6
--- /dev/null
@@ -0,0 +1,38 @@
+*****
+NHRPD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: nhrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-ospf6d.rst b/doc/manpages/frr-ospf6d.rst
new file mode 100644 (file)
index 0000000..cfc6860
--- /dev/null
@@ -0,0 +1,39 @@
+******
+OSPF6D
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ospf6d
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-ospfclient.rst b/doc/manpages/frr-ospfclient.rst
new file mode 100644 (file)
index 0000000..c52b108
--- /dev/null
@@ -0,0 +1,43 @@
+**********
+OSPFCLIENT
+**********
+
+.. include:: defines.rst
+
+SYNOPSIS
+========
+ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
+
+DESCRIPTION
+===========
+ospfclient is an example ospf-api client to test the ospfd daemon.
+
+OPTIONS
+=======
+
+.. option:: ospfd
+
+   A router where the API-enabled OSPF daemon is running.
+
+.. option:: lsatype
+
+   The value has to be either "9", "10", or "11", depending on the flooding scope.
+
+.. option:: opaquetype
+
+   The value has to be in the range of 0-255 (for example, experimental applications might use opaquetype larger than 128).
+
+.. option:: opaqueid
+
+   Arbitrary application instance (24 bits).
+
+.. option:: ifaddr
+
+   Interface IP address for type 9, otherwise it will be ignored.
+
+.. option:: areaid
+
+   Area in the IP address format for type 10, otherwise it will be ignored.
+
+
+.. include:: epilogue.rst
diff --git a/doc/manpages/frr-ospfd.rst b/doc/manpages/frr-ospfd.rst
new file mode 100644 (file)
index 0000000..951a022
--- /dev/null
@@ -0,0 +1,43 @@
+*****
+OSPFD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ospfd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -a, --apiserver
+
+   Enable the OSPF API server.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-pbrd.rst b/doc/manpages/frr-pbrd.rst
new file mode 100644 (file)
index 0000000..d9a80b1
--- /dev/null
@@ -0,0 +1,38 @@
+****
+PBRD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: pbrd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
diff --git a/doc/manpages/frr-pimd.rst b/doc/manpages/frr-pimd.rst
new file mode 100644 (file)
index 0000000..d758266
--- /dev/null
@@ -0,0 +1,38 @@
+****
+PIMD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: pimd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-ripd.rst b/doc/manpages/frr-ripd.rst
new file mode 100644 (file)
index 0000000..af4590c
--- /dev/null
@@ -0,0 +1,38 @@
+****
+RIPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ripd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-ripngd.rst b/doc/manpages/frr-ripngd.rst
new file mode 100644 (file)
index 0000000..aedd689
--- /dev/null
@@ -0,0 +1,38 @@
+******
+RIPNGD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ripngd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-sharpd.rst b/doc/manpages/frr-sharpd.rst
new file mode 100644 (file)
index 0000000..016f3f9
--- /dev/null
@@ -0,0 +1,38 @@
+******
+SHARPD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: sharpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
diff --git a/doc/manpages/frr-staticd.rst b/doc/manpages/frr-staticd.rst
new file mode 100644 (file)
index 0000000..ccbcf32
--- /dev/null
@@ -0,0 +1,38 @@
+*******
+STATICD
+*******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: staticd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
diff --git a/doc/manpages/frr-vrrpd.rst b/doc/manpages/frr-vrrpd.rst
new file mode 100644 (file)
index 0000000..0e73b07
--- /dev/null
@@ -0,0 +1,40 @@
+*****
+VRRPD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: vrrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
+and VRRPv3 is present.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-watchfrr.rst b/doc/manpages/frr-watchfrr.rst
new file mode 100644 (file)
index 0000000..dceb423
--- /dev/null
@@ -0,0 +1,118 @@
+********
+WATCHFRR
+********
+
+.. include:: defines.rst
+.. |DAEMON| replace:: watchfrr
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| [option...] <daemon>...
+
+
+DESCRIPTION
+===========
+|DAEMON| is a watchdog program that monitors the status of supplied frr daemons and tries to restart them in case they become unresponsive or shut down.
+
+To determine whether a daemon is running, it tries to connect to the daemon's VTY UNIX stream socket, and send echo commands to ensure the daemon responds. When the daemon crashes, EOF is received from the socket, so that |DAEMON| can react immediately.
+
+In order to avoid restarting the daemons in quick succession, you can supply the -m and -M options to set the minimum and maximum delay between the restart commands. The minimum restart delay is recalculated each time a restart is attempted.  If the time since the last restart attempt exceeds twice the value of -M, the restart delay is set to the value of -m, otherwise the interval is doubled (but capped at the value of -M).
+
+OPTIONS
+=======
+
+.. option:: --dry
+
+   Run |DAEMON| in "dry-run" mode, only monitoring the specified daemons but not performing any start/stop/restart actions.
+
+.. option:: -d, --daemon
+
+   Run in daemon mode. When supplied, error messages are sent to Syslog instead of standard output (stdout).
+
+.. option:: -S <directory>, --statedir <directory>
+
+   Set the VTY socket directory (the default value is "/var/run/frr").
+
+.. 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.
+
+.. option:: --min-restart-interval <number>
+
+   Set the minimum number of seconds to wait between invocations of the daemon restart commands (the default value is "60").
+
+.. option:: --max-restart-interval <number>
+
+   Set the maximum number of seconds to wait between invocations of the daemon restart commands (the default value is "600").
+
+.. option:: -i <number>, --interval <number>
+
+   Set the status polling interval in seconds (the default value is "5").
+
+.. option:: -t <number>, --timeout <number>
+
+   Set the unresponsiveness timeout in seconds (the default value is "10").
+
+.. option:: -T <number>, --restart-timeout <number>
+
+   Set the restart (kill) timeout in seconds (the default value is "20"). If any background jobs are still running after this period has elapsed, they will be killed.
+
+.. option:: -p <filename>, --pid-file <filename>
+
+   Set the process identifier filename (the default value is "/var/run/frr/|DAEMON|.pid").
+
+.. option:: -b <string>, --blank-string <string>
+
+   When the supplied string is found in any of the command line option arguments (i.e., -r, -s, or -k), replace it with a space.
+
+   This is an ugly hack to circumvent problems with passing the command line arguments containing embedded spaces.
+
+.. option:: -v, --version
+
+   Display the version information and exit.
+
+.. option:: -h, --help
+
+   Display the usage information and exit.
+
+The following 3 options specify scripts that |DAEMON| uses to perform start/stop/restart actions. Reasonable default values are built into watchfrr, so the use of these options should no longer be necessary:
+
+.. option:: -s command, --start-command command
+
+  Supply a Bourne shell command to start a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+.. option:: -k command, --kill-command command
+
+   Supply a Bourne shell command to stop a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+.. option:: -r command, --restart command
+
+   Supply a Bourne shell command to restart a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+PREVIOUS OPTIONS
+================
+Prior versions of |DAEMON| supported some additional options that no longer exist:::
+
+   -a, -A, -e, -R, -z
+
+The ``-a``, ``-A`` and ``-R`` options were used to select alternate monitoring modes that offered different patterns of restarting daemons. The "correct" mode (phased restart) is now the default. The -e and -z options used to disable some monitoring aspects, |DAEMON| now always has all monitoring features enabled.
+
+Removing these options should result in correct operation, if it does not please file a bug report.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
diff --git a/doc/manpages/frr-zebra.rst b/doc/manpages/frr-zebra.rst
new file mode 100644 (file)
index 0000000..cfb368b
--- /dev/null
@@ -0,0 +1,67 @@
+*****
+ZEBRA
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: zebra
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing manager that implements the zebra route engine. zebra supports all protocol daemons in the FRRouting suite.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -b, --batch
+
+   Runs in batch mode, zebra parses its config and exits.
+
+.. option:: -k, --keep_kernel
+
+   On startup, don't delete self inserted routes.
+
+.. option:: -s, --nl-bufsize <netlink-buffer-size>
+
+   Set netlink receive buffer size. There are cases where zebra daemon can't handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble.
+
+   Solution is to increase receive buffer of netlink socket. Note that kernel < 2.6.14 doesn't allow increasing it over maximum value defined in /proc/sys/net/core/rmem_max. If you want to do it, you have to increase maximum before starting zebra.
+
+   Note that this affects Linux only.
+
+
+.. option:: -n, --vrfwnetns
+
+   Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts.
+
+ROUTES
+------
+
+.. option:: -r, --retain
+
+   When the program terminates, do not flush routes installed by zebra from the kernel.
+
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+   The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+   The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+   If the |DAEMON| process is configured to output logs to a file, then you
+   will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
index 40f06efdfeb519aabfa6d10f1d29cb8d67781973..58a1d9e0db0528f164d7952a179567f2db44f11f 100644 (file)
@@ -6,25 +6,25 @@
 .. toctree::
    :maxdepth: 2
 
-   bfdd
-   bgpd
-   eigrpd
-   isisd
-   fabricd
-   ldpd
-   nhrpd
-   ospf6d
-   ospfclient
-   ospfd
-   pimd
-   pbrd
+   frr
+   frr-bfdd
+   frr-bgpd
+   frr-eigrpd
+   frr-isisd
+   frr-fabricd
+   frr-ldpd
+   frr-nhrpd
+   frr-ospf6d
+   frr-ospfclient
+   frr-ospfd
+   frr-pimd
+   frr-pbrd
+   frr-ripd
+   frr-ripngd
+   frr-sharpd
+   frr-staticd
+   frr-watchfrr
+   frr-zebra
+   frr-vrrpd
    mtracebis
-   ripd
-   ripngd
-   sharpd
-   staticd
-   watchfrr
-   zebra
    vtysh
-   vrrpd
-   frr
diff --git a/doc/manpages/isisd.rst b/doc/manpages/isisd.rst
deleted file mode 100644 (file)
index 68761f6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-*****
-ISISD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: isisd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/ldpd.rst b/doc/manpages/ldpd.rst
deleted file mode 100644 (file)
index 113f066..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-****
-LDPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ldpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/nhrpd.rst b/doc/manpages/nhrpd.rst
deleted file mode 100644 (file)
index cae01c6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-*****
-NHRPD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: nhrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/ospf6d.rst b/doc/manpages/ospf6d.rst
deleted file mode 100644 (file)
index cfc6860..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-******
-OSPF6D
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ospf6d
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/ospfclient.rst b/doc/manpages/ospfclient.rst
deleted file mode 100644 (file)
index c52b108..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-**********
-OSPFCLIENT
-**********
-
-.. include:: defines.rst
-
-SYNOPSIS
-========
-ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
-
-DESCRIPTION
-===========
-ospfclient is an example ospf-api client to test the ospfd daemon.
-
-OPTIONS
-=======
-
-.. option:: ospfd
-
-   A router where the API-enabled OSPF daemon is running.
-
-.. option:: lsatype
-
-   The value has to be either "9", "10", or "11", depending on the flooding scope.
-
-.. option:: opaquetype
-
-   The value has to be in the range of 0-255 (for example, experimental applications might use opaquetype larger than 128).
-
-.. option:: opaqueid
-
-   Arbitrary application instance (24 bits).
-
-.. option:: ifaddr
-
-   Interface IP address for type 9, otherwise it will be ignored.
-
-.. option:: areaid
-
-   Area in the IP address format for type 10, otherwise it will be ignored.
-
-
-.. include:: epilogue.rst
diff --git a/doc/manpages/ospfd.rst b/doc/manpages/ospfd.rst
deleted file mode 100644 (file)
index 951a022..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-*****
-OSPFD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ospfd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -a, --apiserver
-
-   Enable the OSPF API server.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/pbrd.rst b/doc/manpages/pbrd.rst
deleted file mode 100644 (file)
index d9a80b1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-****
-PBRD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: pbrd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
diff --git a/doc/manpages/pimd.rst b/doc/manpages/pimd.rst
deleted file mode 100644 (file)
index d758266..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-****
-PIMD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: pimd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/ripd.rst b/doc/manpages/ripd.rst
deleted file mode 100644 (file)
index af4590c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-****
-RIPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ripd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/ripngd.rst b/doc/manpages/ripngd.rst
deleted file mode 100644 (file)
index aedd689..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-******
-RIPNGD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ripngd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/sharpd.rst b/doc/manpages/sharpd.rst
deleted file mode 100644 (file)
index 016f3f9..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-******
-SHARPD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: sharpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
diff --git a/doc/manpages/staticd.rst b/doc/manpages/staticd.rst
deleted file mode 100644 (file)
index ccbcf32..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-*******
-STATICD
-*******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: staticd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
index 19d2d8d6ae4ace0f41da640420202fc5383d8498..92842120994e1f54ef097b08c50525b90a874d34 100644 (file)
@@ -3,34 +3,34 @@
 #
 
 man_RSTFILES = \
-       doc/manpages/bgpd.rst \
+       doc/manpages/bfd-options.rst \
        doc/manpages/common-options.rst \
        doc/manpages/conf.py \
        doc/manpages/defines.rst \
-       doc/manpages/eigrpd.rst \
        doc/manpages/epilogue.rst \
-       doc/manpages/fabricd.rst \
+       doc/manpages/frr-bfdd.rst \
+       doc/manpages/frr-bgpd.rst \
+       doc/manpages/frr-eigrpd.rst \
+       doc/manpages/frr-fabricd.rst \
+       doc/manpages/frr-isisd.rst \
+       doc/manpages/frr-ldpd.rst \
+       doc/manpages/frr-nhrpd.rst \
+       doc/manpages/frr-ospf6d.rst \
+       doc/manpages/frr-ospfclient.rst \
+       doc/manpages/frr-ospfd.rst \
+       doc/manpages/frr-pbrd.rst \
+       doc/manpages/frr-pimd.rst \
+       doc/manpages/frr-ripd.rst \
+       doc/manpages/frr-ripngd.rst \
+       doc/manpages/frr-sharpd.rst \
+       doc/manpages/frr-staticd.rst \
+       doc/manpages/frr-vrrpd.rst \
+       doc/manpages/frr-watchfrr.rst \
+       doc/manpages/frr-zebra.rst \
        doc/manpages/frr.rst \
        doc/manpages/index.rst \
-       doc/manpages/isisd.rst \
-       doc/manpages/ldpd.rst \
        doc/manpages/mtracebis.rst \
-       doc/manpages/nhrpd.rst \
-       doc/manpages/ospf6d.rst \
-       doc/manpages/ospfclient.rst \
-       doc/manpages/ospfd.rst \
-       doc/manpages/pimd.rst \
-       doc/manpages/ripd.rst \
-       doc/manpages/pbrd.rst \
-       doc/manpages/ripngd.rst \
-       doc/manpages/sharpd.rst \
-       doc/manpages/staticd.rst \
        doc/manpages/vtysh.rst \
-       doc/manpages/watchfrr.rst \
-       doc/manpages/zebra.rst \
-       doc/manpages/bfdd.rst \
-       doc/manpages/bfd-options.rst \
-       doc/manpages/vrrpd.rst \
        # end
 
 EXTRA_DIST += $(man_RSTFILES)
diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/vrrpd.rst
deleted file mode 100644 (file)
index 0e73b07..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-*****
-VRRPD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: vrrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
-and VRRPv3 is present.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/watchfrr.rst b/doc/manpages/watchfrr.rst
deleted file mode 100644 (file)
index dceb423..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-********
-WATCHFRR
-********
-
-.. include:: defines.rst
-.. |DAEMON| replace:: watchfrr
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| [option...] <daemon>...
-
-
-DESCRIPTION
-===========
-|DAEMON| is a watchdog program that monitors the status of supplied frr daemons and tries to restart them in case they become unresponsive or shut down.
-
-To determine whether a daemon is running, it tries to connect to the daemon's VTY UNIX stream socket, and send echo commands to ensure the daemon responds. When the daemon crashes, EOF is received from the socket, so that |DAEMON| can react immediately.
-
-In order to avoid restarting the daemons in quick succession, you can supply the -m and -M options to set the minimum and maximum delay between the restart commands. The minimum restart delay is recalculated each time a restart is attempted.  If the time since the last restart attempt exceeds twice the value of -M, the restart delay is set to the value of -m, otherwise the interval is doubled (but capped at the value of -M).
-
-OPTIONS
-=======
-
-.. option:: --dry
-
-   Run |DAEMON| in "dry-run" mode, only monitoring the specified daemons but not performing any start/stop/restart actions.
-
-.. option:: -d, --daemon
-
-   Run in daemon mode. When supplied, error messages are sent to Syslog instead of standard output (stdout).
-
-.. option:: -S <directory>, --statedir <directory>
-
-   Set the VTY socket directory (the default value is "/var/run/frr").
-
-.. 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.
-
-.. option:: --min-restart-interval <number>
-
-   Set the minimum number of seconds to wait between invocations of the daemon restart commands (the default value is "60").
-
-.. option:: --max-restart-interval <number>
-
-   Set the maximum number of seconds to wait between invocations of the daemon restart commands (the default value is "600").
-
-.. option:: -i <number>, --interval <number>
-
-   Set the status polling interval in seconds (the default value is "5").
-
-.. option:: -t <number>, --timeout <number>
-
-   Set the unresponsiveness timeout in seconds (the default value is "10").
-
-.. option:: -T <number>, --restart-timeout <number>
-
-   Set the restart (kill) timeout in seconds (the default value is "20"). If any background jobs are still running after this period has elapsed, they will be killed.
-
-.. option:: -p <filename>, --pid-file <filename>
-
-   Set the process identifier filename (the default value is "/var/run/frr/|DAEMON|.pid").
-
-.. option:: -b <string>, --blank-string <string>
-
-   When the supplied string is found in any of the command line option arguments (i.e., -r, -s, or -k), replace it with a space.
-
-   This is an ugly hack to circumvent problems with passing the command line arguments containing embedded spaces.
-
-.. option:: -v, --version
-
-   Display the version information and exit.
-
-.. option:: -h, --help
-
-   Display the usage information and exit.
-
-The following 3 options specify scripts that |DAEMON| uses to perform start/stop/restart actions. Reasonable default values are built into watchfrr, so the use of these options should no longer be necessary:
-
-.. option:: -s command, --start-command command
-
-  Supply a Bourne shell command to start a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-.. option:: -k command, --kill-command command
-
-   Supply a Bourne shell command to stop a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-.. option:: -r command, --restart command
-
-   Supply a Bourne shell command to restart a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-PREVIOUS OPTIONS
-================
-Prior versions of |DAEMON| supported some additional options that no longer exist:::
-
-   -a, -A, -e, -R, -z
-
-The ``-a``, ``-A`` and ``-R`` options were used to select alternate monitoring modes that offered different patterns of restarting daemons. The "correct" mode (phased restart) is now the default. The -e and -z options used to disable some monitoring aspects, |DAEMON| now always has all monitoring features enabled.
-
-Removing these options should result in correct operation, if it does not please file a bug report.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
diff --git a/doc/manpages/zebra.rst b/doc/manpages/zebra.rst
deleted file mode 100644 (file)
index cfb368b..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-*****
-ZEBRA
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: zebra
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing manager that implements the zebra route engine. zebra supports all protocol daemons in the FRRouting suite.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -b, --batch
-
-   Runs in batch mode, zebra parses its config and exits.
-
-.. option:: -k, --keep_kernel
-
-   On startup, don't delete self inserted routes.
-
-.. option:: -s, --nl-bufsize <netlink-buffer-size>
-
-   Set netlink receive buffer size. There are cases where zebra daemon can't handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble.
-
-   Solution is to increase receive buffer of netlink socket. Note that kernel < 2.6.14 doesn't allow increasing it over maximum value defined in /proc/sys/net/core/rmem_max. If you want to do it, you have to increase maximum before starting zebra.
-
-   Note that this affects Linux only.
-
-
-.. option:: -n, --vrfwnetns
-
-   Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts.
-
-ROUTES
-------
-
-.. option:: -r, --retain
-
-   When the program terminates, do not flush routes installed by zebra from the kernel.
-
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
-   The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
-   The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
-   If the |DAEMON| process is configured to output logs to a file, then you
-   will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
index f946c37a73a86e171d9e11e72eee2078ae65f9b6..edcfce45ad6d15b655eb247e3fb36a5bb5a0c526 100644 (file)
@@ -286,6 +286,9 @@ Below is a sample configuration file for the zebra daemon.
    !
    ! Zebra configuration file
    !
+   frr version 6.0
+   frr defaults traditional
+   !
    hostname Router
    password zebra
    enable password zebra
@@ -307,6 +310,137 @@ If a comment character is not the first character of the word, it's a normal
 character. So in the above example ``!`` will not be regarded as a comment and
 the password is set to ``zebra!password``.
 
+
+Configuration versioning, profiles and upgrade behavior
+-------------------------------------------------------
+
+All |PACKAGE_NAME| daemons share a mechanism to specify a configuration profile
+and version for loading and saving configuration.  Specific configuration
+settings take different default values depending on the selected profile and
+version.
+
+While the profile can be selected by user configuration and will remain over
+upgrades, |PACKAGE_NAME| will always write configurations using its current
+version.  This means that, after upgrading, a ``write file`` may write out a
+slightly different configuration than what was read in.
+
+Since the previous configuration is loaded with its version's defaults, but
+the new configuration is written with the new defaults, any default that
+changed between versions will result in an appropriate configuration entry
+being written out.  **FRRouting configuration is sticky, staying consistent
+over upgrades.**  Changed defaults will only affect new configuration.
+
+Note that the loaded version persists into interactive configuration
+sessions.  Commands executed in an interactive configuration session are
+no different from configuration loaded at startup.  This means that when,
+say, you configure a new BGP peer, the defaults used for configuration
+are the ones selected by the last ``frr version`` command.
+
+.. warning::
+
+   Saving the configuration does not bump the daemons forward to use the new
+   version for their defaults, but restarting them will, since they will then
+   apply the new ``frr version`` command that was written out.  Manually
+   execute the ``frr version`` command in ``show running-config`` to avoid
+   this intermediate state.
+
+This is visible in ``show running-config``:
+
+.. code-block:: frr
+
+   Current configuration:
+   !
+   ! loaded from 6.0
+   frr version 6.1-dev
+   frr defaults traditional
+   !
+
+If you save and then restart with this configuration, the old defaults will
+no longer apply.  Similarly, you could execute ``frr version 6.1-dev``, causing
+the new defaults to apply and the ``loaded from 6.0`` comment to disappear.
+
+
+Profiles
+^^^^^^^^
+
+|PACKAGE_NAME| provides configuration profiles to adapt its default settings
+to various usage scenarios.  Currently, the following profiles are
+implemented:
+
+* ``traditional`` - reflects defaults adhering mostly to IETF standards or
+  common practices in wide-area internet routing.
+* ``datacenter`` - reflects a single administrative domain with intradomain
+  links using aggressive timers.
+
+Your distribution/installation may pre-set a profile through the ``-F`` command
+line option on all daemons.  All daemons must be configured for the same
+profile.  The value specified on the command line is only a pre-set and any
+``frr defaults`` statement in the configuration will take precedence.
+
+.. note::
+
+   The profile must be the same across all daemons.  Mismatches may result
+   in undefined behavior.
+
+You can freely switch between profiles without causing any interruption or
+configuration changes.  All settings remain at their previous values, and
+``show running-configuration`` output will have new output listing the previous
+default values as explicit configuration.  New configuration, e.g. adding a
+BGP peer, will use the new defaults.  To apply the new defaults for existing
+configuration, the previously-invisible old defaults that are now shown must
+be removed from the configuration.
+
+
+Upgrade practices for interactive configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you configure |PACKAGE_NAME| interactively and use the configuration
+writing functionality to make changes persistent, the following
+recommendations apply in regards to upgrades:
+
+1. Skipping major versions should generally work but is still inadvisable.
+   To avoid unneeded issue, upgrade one major version at a time and write
+   out the configuration after each update.
+
+2. After installing a new |PACKAGE_NAME| version, check the configuration
+   for differences against your old configuration.  If any defaults changed
+   that affect your setup, lines may appear or disappear.  If a new line
+   appears, it was previously the default (or not supported) and is now
+   neccessary to retain previous behavior.  If a line disappears, it
+   previously wasn't the default, but now is, so it is no longer necessary.
+
+3. Check the log files for deprecation warnings by using ``grep -i deprecat``.
+
+4. After completing each upgrade, save the configuration and either restart
+   |PACKAGE_NAME| or execute ``frr version <CURRENT>`` to ensure defaults of
+   the new version are fully applied.
+
+
+Upgrade practices for autogenerated configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When using |PACKAGE_NAME| with generated configurations (e.g. Ansible,
+Puppet, etc.), upgrade considerations differ somewhat:
+
+1. Always write out a ``frr version`` statement in the configurations you
+   generate.  This ensures that defaults are applied consistently.
+
+2. Try to not run more distinct versions of |PACKAGE_NAME| than necessary.
+   Each version may need to be checked individually.  If running a mix of
+   older and newer installations, use the oldest version for the
+   ``frr version`` statement.
+
+3. When rolling out upgrades, generate a configuration as usual with the old
+   version identifier and load it.  Check for any differences or deprecation
+   warnings.  If there are differences in the configuration, propagate these
+   back to the configuration generator to minimize relying on actual default
+   values.
+
+4. After the last installation of an old version is removed, change the
+   configuration generation to a newer ``frr version`` as appropriate.  Perform
+   the same checks as when rolling out upgrades.
+
+
 .. _terminal-mode-commands:
 
 Terminal Mode Commands
index 525a31d4867e2cdc5341b0b98ec4fdc39bb5d50a..e6a3c4977a9ff4f5f34563e88d300b5bd51ebc21 100644 (file)
@@ -94,6 +94,10 @@ BFDd Commands
 
     Show status for a specific BFD peer.
 
+.. index:: show bfd [vrf NAME] peers brief [json]
+.. clicmd:: show bfd [vrf NAME] peers brief [json]
+
+    Show all configured BFD peers information and current status in brief.
 
 .. _bfd-peer-config:
 
@@ -340,11 +344,14 @@ You can inspect the current BFD peer status with the following commands:
                    Uptime: 1 minute(s), 51 second(s)
                    Diagnostics: ok
                    Remote diagnostics: ok
+                   Peer Type: dynamic
                    Local timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: disabled
                    Remote timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: 50ms
@@ -357,11 +364,14 @@ You can inspect the current BFD peer status with the following commands:
                    Uptime: 1 minute(s), 53 second(s)
                    Diagnostics: ok
                    Remote diagnostics: ok
+                   Peer Type: configured
                    Local timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: disabled
                    Remote timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: 50ms
@@ -376,17 +386,31 @@ You can inspect the current BFD peer status with the following commands:
                    Uptime: 3 minute(s), 4 second(s)
                    Diagnostics: ok
                    Remote diagnostics: ok
+                   Peer Type: dynamic
                    Local timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: disabled
                    Remote timers:
+                           Detect-multiplier: 3
                            Receive interval: 300ms
                            Transmission interval: 300ms
                            Echo transmission interval: 50ms
 
    frr# show bfd peer 192.168.0.1 json
-   {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50}
+   {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"}
+
+
+You can inspect the current BFD peer status in brief with the following commands:
+
+::
+
+   frr# show bfd peers brief 
+   Session count: 1
+   SessionId  LocalAddress         PeerAddress      Status
+   =========  ============         ===========      ======
+   1          192.168.0.1          192.168.0.2      up
 
 
 You can also inspect peer session counters with the following commands:
@@ -425,3 +449,40 @@ You can also inspect peer session counters with the following commands:
 
    frr# show bfd peer 192.168.0.1 counters json
    {"multihop":false,"peer":"192.168.0.1","control-packet-input":348,"control-packet-output":685,"echo-packet-input":6815,"echo-packet-output":6816,"session-up":1,"session-down":0,"zebra-notifications":4}
+
+You can also clear packet counters per session with the following commands, only the packet counters will be reset:
+
+::
+
+   frr# clear bfd peers counters
+
+   frr# show bfd peers counters
+   BFD Peers:
+        peer 192.168.2.1 interface r2-eth2
+                Control packet input: 0 packets
+                Control packet output: 0 packets
+                Echo packet input: 0 packets
+                Echo packet output: 0 packets
+                Session up events: 1
+                Session down events: 0
+                Zebra notifications: 2
+
+        peer 192.168.0.1
+                Control packet input: 0 packets
+                Control packet output: 0 packets
+                Echo packet input: 0 packets
+                Echo packet output: 0 packets
+                Session up events: 1
+                Session down events: 0
+                Zebra notifications: 4
+
+Logging / debugging
+===================
+
+There are no fine grained debug controls for bfdd. Just enable debug logs.
+
+::
+
+   config
+   log file /var/log/frr/frr.log debugging
+   log syslog debugging
index de75c12e6212fcb4ee55c8cbd2f65f4e13b9a1d5..d3ac4b22ab0473a349d63983f39f6560f7682ba7 100644 (file)
@@ -417,7 +417,7 @@ Require policy on EBGP
    This command requires incoming and outgoing filters to be applied for eBGP sessions. Without the incoming filter, no routes will be accepted. Without the outgoing filter, no routes will be announced.
 
 Reject routes with AS_SET or AS_CONFED_SET types
--------------------------------
+------------------------------------------------
 
 .. index:: [no] bgp reject-as-sets
 .. clicmd:: [no] bgp reject-as-sets
@@ -1045,6 +1045,15 @@ Configuring Peers
 .. index:: neighbor PEER port PORT
 .. clicmd:: neighbor PEER port PORT
 
+.. index:: [no] neighbor PEER password PASSWORD
+.. clicmd:: [no] neighbor PEER password PASSWORD
+
+   Set a MD5 password to be used with the tcp socket that is being used
+   to connect to the remote peer.  Please note if you are using this
+   command with a large number of peers on linux you should consider
+   modifying the `net.core.optmem_max` sysctl to a larger value to
+   avoid out of memory errors from the linux kernel.
+
 .. index:: neighbor PEER send-community
 .. clicmd:: neighbor PEER send-community
 
@@ -1066,6 +1075,11 @@ Configuring Peers
    granular and offers much smarter matching criterion than number of received
    prefixes, making it more suited to implementing policy.
 
+.. index:: [no] neighbor PEER maximum-prefix-out NUMBER
+.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER
+
+   Sets a maximum number of prefixes we can send to a given peer.
+
 .. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
 .. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
 
index ec6bc56e7685555c22765b8a0862265510bc1486..95ef9cb7ee197ff922b55a0d8dd522bd22fdff12 100644 (file)
@@ -4,8 +4,8 @@
 NHRP
 ****
 
-*nhrpd* is an implementation of the :abbr:NHRP `(Next Hop Routing Protocol)`.
-NHRP is described in :rfc`2332`.
+*nhrpd* is an implementation of the :abbr:`NHRP (Next Hop Routing Protocol)`.
+NHRP is described in :rfc:`2332`.
 
 NHRP is used to improve the efficiency of routing computer network traffic over
 :abbr:`NBMA (Non-Broadcast, Multiple Access)` networks. NHRP provides an
@@ -13,6 +13,13 @@ ARP-like solution that allows a system to dynamically learn the NBMA address of
 the other systems that are part of that network, allowing these systems to
 directly communicate without requiring traffic to use an intermediate hop.
 
+NHRP is a client-server protocol. The server side is called the :abbr:`NHS
+(Next Hop Server)` or the hub, while a client is referred to as the :abbr:`NHC
+(Next Hop Client)` or the spoke. When a node is configured as an NHC, it
+registers its address with the NHS which keeps track of all registered spokes.
+An NHC client can then query the addresses of other clients from NHS allowing
+all spokes to communicate directly with each other.
+
 Cisco Dynamic Multipoint VPN (DMVPN) is based on NHRP, and |PACKAGE_NAME| nhrpd
 implements this scenario.
 
@@ -31,7 +38,9 @@ This is similar to Cisco FlexVPN; but in contrast to opennhrp which uses
 a generic subnet route.
 
 To create NBMA GRE tunnel you might use the following (Linux terminal
-commands):::
+commands):
+
+.. code-block:: console
 
    ip tunnel add gre1 mode gre key 42 ttl 64
    ip addr add 10.255.255.2/32 dev gre1
@@ -68,7 +77,58 @@ command defines the GRE subnet):
 Configuring NHRP
 ================
 
-FIXME
+.. index::  ip nhrp holdtime (1-65000)
+.. clicmd:: ip nhrp holdtime (1-65000)
+
+   Holdtime is the number of seconds that have to pass before stopping to
+   advertise an NHRP NBMA address as valid. It also controls how often NHRP
+   registration requests are sent. By default registrations are sent every one
+   third of the holdtime.
+
+.. index::  ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local
+.. clicmd:: ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local
+
+   Map an IP address of a station to the station's NBMA address.
+
+.. index::  ip nhrp network-id (1-4294967295)
+.. clicmd:: ip nhrp network-id (1-4294967295)
+
+   Enable NHRP on this interface and set the interface's network ID.  The
+   network ID is used to allow creating multiple nhrp domains on a router when
+   multiple interfaces are configured on the router.  Interfaces configured
+   with the same ID are part of the same logical NBMA network. The ID is a
+   local only parameter and is not sent to other NHRP nodes and so IDs on
+   different nodes do not need to match. When NHRP packets are received on an
+   interface they are assigned to the local NHRP domain for that interface.
+
+.. index::  ip nhrp nhs A.B.C.D nbma A.B.C.D|FQDN
+.. clicmd:: ip nhrp nhs A.B.C.D nbma A.B.C.D|FQDN
+
+   Configure the Next Hop Server address and its NBMA address.
+
+.. index::  ip nhrp nhs dynamic nbma A.B.C.D
+.. clicmd:: ip nhrp nhs dynamic nbma A.B.C.D
+
+   Configure the Next Hop Server to have a dynamic address and set its NBMA
+   address.
+
+.. index::  ip nhrp registration no-unique
+.. clicmd:: ip nhrp registration no-unique
+
+   Allow the client to not set the unique flag in the NHRP packets. This is
+   useful when a station has a dynamic IP address that could change over time.
+
+.. index::  ip nhrp shortcut
+.. clicmd:: ip nhrp shortcut
+
+   Enable shortcut (spoke-to-spoke) tunnels to allow NHC to talk to each others
+   directly after establishing a connection without going through the hub.
+
+.. index::  ip nhrp mtu
+.. clicmd:: ip nhrp mtu
+
+   Configure NHRP advertised MTU.
+
 
 .. _hub-functionality:
 
@@ -92,25 +152,25 @@ This can be achieved with the following iptables rule.
        --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
 
 
-You can fine tune the src/dstmask according to the prefix lengths you
-announce internal, add additional IP range matches, or rate limitation
-if needed. However, the above should be good in most cases.
+You can fine tune the src/dstmask according to the prefix lengths you announce
+internal, add additional IP range matches, or rate limitation if needed.
+However, the above should be good in most cases.
 
 This kernel NFLOG target's nflog-group is configured in global nhrp config
 with:
 
-.. code-block:: frr
-
-   nhrp nflog-group 1
+.. index::  nhrp nflog-group (1-65535)
+.. clicmd:: nhrp nflog-group (1-65535)
 
 To start sending these traffic notices out from hubs, use the nhrp
 per-interface directive:
 
-.. code-block:: frr
-
-   interface gre1
-    ip nhrp redirect
+.. index::  ip nhrp redirect
+.. clicmd:: ip nhrp redirect
 
+This enable redirect replies on the NHS similar to ICMP redirects except this
+is managed by the nhrp protocol. This setting allows spokes to communicate with
+each others directly.
 
 .. _integration-with-ike:
 
@@ -134,7 +194,10 @@ git repositories for the patches.
 NHRP Events
 ===========
 
-FIXME
+.. index::  nhrp event socket SOCKET
+.. clicmd:: nhrp event socket SOCKET
+
+   Configure the Unix path for the event socket.
 
 Configuration Example
 =====================
index 03d7299bf73e348db26198a9a2c9af165c319582..262c0117dfe298dc2468bbb72618555606402c28 100644 (file)
@@ -4,65 +4,23 @@
 Overview
 ********
 
-`FRR`_ is a routing software package that provides TCP/IP based routing
-services with routing protocols support such as BGP, RIP, OSPF, IS-IS and more
-(see :ref:`supported-protocols`). FRR also supports
-special BGP Route Reflector and Route Server behavior.  In addition to
-traditional IPv4 routing protocols, FRR also supports IPv6 routing protocols.
-With an SNMP daemon that supports the AgentX protocol, FRR provides routing
-protocol MIB read-only access (:ref:`snmp-support`).
-
-FRR uses an advanced software architecture to provide you with a high quality,
-multi server routing engine. FRR has an interactive user interface for each
-routing protocol and supports common client commands.  Due to this design, you
-can add new protocol daemons to FRR easily.  You can use FRR library as your
-program's client user interface.
-
-FRR is distributed under the GNU General Public License.
+`FRR`_ is a fully featured, high performance, free software IP routing suite.
 
-FRR is a fork of `Quagga <http://www.quagga.net/>`_.
+FRR implements all standard routing protocols such as BGP, RIP, OSPF, IS-IS and
+more (see :ref:`feature-matrix`), as well as many of their extensions.
 
-.. _about-frr:
+FRR is a high performance suite written primarily in C. It can easily handle
+full Internet routing tables and is suitable for use on hardware ranging from
+cheap SBCs to commercial grade routers. It is actively used in production by
+hundreds of companies, universities, research labs and governments.
 
-About FRR
-=========
+FRR is distributed under GPLv2, with development modeled after the Linux
+kernel. Anyone may contribute features, bug fixes, tools, documentation
+updates, or anything else.
 
-Today, TCP/IP networks are covering all of the world.  The Internet has been
-deployed in many countries, companies, and to the home.  When you connect to
-the Internet your packet will pass many routers which have TCP/IP routing
-functionality.
-
-A system with FRR installed acts as a dedicated router.  With FRR, your machine
-exchanges routing information with other routers using routing protocols.  FRR
-uses this information to update the kernel routing table so that the right data
-goes to the right place.  You can dynamically change the configuration and you
-may view routing table information from the FRR terminal interface.
-
-Adding to routing protocol support, FRR can setup interface's flags,
-interface's address, static routes and so on.  If you have a small network, or
-a stub network, or xDSL connection, configuring the FRR routing software is
-very easy.  The only thing you have to do is to set up the interfaces and put a
-few commands about static routes and/or default routes.  If the network is
-rather large, or if the network structure changes frequently, you will want to
-take advantage of FRR's dynamic routing protocol support for protocols such as
-RIP, OSPF, IS-IS or BGP.
-
-Traditionally, UNIX based router configuration is done by *ifconfig* and
-*route* commands.  Status of routing table is displayed by *netstat* utility.
-Almost of these commands work only if the user has root privileges.  FRR has a
-different system administration method.  There are two user modes in FRR.  One
-is normal mode, the other is enable mode.  Normal mode user can only view
-system status, enable mode user can change system configuration.  This UNIX
-account independent feature will be great help to the router administrator.
-
-Currently, FRR supports common unicast routing protocols, that is BGP, OSPF,
-RIP and IS-IS.  Upcoming for MPLS support, an implementation of LDP is
-currently being prepared for merging.  Implementations of BFD and PIM-SSM
-(IPv4) also exist, but are not actively being worked on.
-
-The ultimate goal of the FRR project is making a production-grade, high
-quality, featureful and free IP routing software suite.
+FRR is a fork of `Quagga <http://www.quagga.net/>`_.
 
+.. _how-to-get-frr:
 
 How to get FRR
 ==============
@@ -79,25 +37,46 @@ For instructions on installing from source, refer to the
 `developer documentation <http://docs.frrouting.org/projects/dev-guide/en/latest/>`_.
 
 
+.. _about-frr:
+
+About FRR
+=========
+
+FRR provides IP routing services. Its role in a networking stack is to exchange
+routing information with other routers, make routing and policy decisions, and
+inform other layers of these decisions. In the most common scenario, FRR
+installs routing decisions into the OS kernel, allowing the kernel networking
+stack to make the corresponding forwarding decisions.
+
+In addition to dynamic routing FRR supports the full range of L3 configuration,
+including static routes, addresses, router advertisements etc. It has some
+light L2 functionality as well, but this is mostly left to the platform. This
+makes it suitable for deployments ranging from small home networks with static
+routes to Internet exchanges running full Internet tables.
+
+FRR runs on all modern \*NIX operating systems, including Linux and the BSDs.
+Feature support varies by platform; see the :ref:`feature-matrix`.
+
+
 System Architecture
-===================
+-------------------
 
 .. index:: System architecture
-
 .. index:: Software architecture
-
 .. index:: Software internals
 
 Traditional routing software is made as a one process program which provides
 all of the routing protocol functionalities. FRR takes a different approach.
-FRR is a suite of daemons that work together to build the routing table. There
-is a daemon for each major supported protocol as well as a middleman daemon
-(*Zebra*) which serves as the broker between these daemons and the kernel.
+FRR is a suite of daemons that work together to build the routing table. Each
+major protocol is implemented in its own daemon, and these daemons talk to a
+middleman daemon (*zebra*), which is responsible for coordinating routing
+decisions and talking to the dataplane.
 
 This architecture allows for high resiliency, since an error, crash or exploit
-in one protocol daemon will generally not affect the others.  It is also
+in one protocol daemon will generally not affect the others. It is also
 flexible and extensible since the modularity makes it easy to implement new
-protocols and tie them into the suite.
+protocols and tie them into the suite. Additionally, each daemon implements a
+plugin system allowing new functionality to be loaded at runtime.
 
 An illustration of the large scale architecture is given below.
 
@@ -121,17 +100,23 @@ An illustration of the large scale architecture is given below.
    +-------------+   +------------------+   +-------------+
 
 
-The multi-process architecture brings extensibility, modularity and
-maintainability.  All of the FRR daemons can be managed through a single
-integrated user interface shell called *vtysh*.  *vtysh* connects to each
-daemon through a UNIX domain socket and then works as a proxy for user input.
-In addition to a unified frontend, *vtysh* also provides the ability to
-configure all the daemons using a single configuration file through the
-integrated configuration mode avoiding the problem of having to maintain a
-separate configuration file for each daemon.
+All of the FRR daemons can be managed through a single integrated user
+interface shell called *vtysh*. *vtysh* connects to each daemon through a UNIX
+domain socket and then works as a proxy for user input. In addition to a
+unified frontend, *vtysh* also provides the ability to configure all the
+daemons using a single configuration file through the integrated configuration
+mode. This avoids the overhead of maintaining a separate configuration file for
+each daemon.
+
+FRR is currently currently implementing a new internal configuration system
+based on YANG data models. When this work is completed, FRR will be a fully
+programmable routing stack.
+
+
+.. _supported-platforms:
 
 Supported Platforms
-===================
+-------------------
 
 .. index:: Supported platforms
 .. index:: FRR on other systems
@@ -150,7 +135,7 @@ us know if you can get FRR to run on a platform which is not listed below:
 
 Versions of these platforms that are older than around 2 years from the point
 of their original release (in case of GNU/Linux, this is since the kernel's
-release on https://kernel.org/) may need some work.  Similarly, the following
+release on https://kernel.org/) may need some work. Similarly, the following
 platforms may work with some effort:
 
 - Solaris
@@ -162,14 +147,15 @@ Recent versions of the following compilers are well tested:
 - LLVM's Clang
 - Intel's ICC
 
-.. _supported-protocols:
+.. _feature-matrix:
 
-Supported Protocols vs. Platform
-================================
+Feature Matrix
+^^^^^^^^^^^^^^
 
-The following table lists all protocols cross-refrenced to all operating
-systems that have at least CI build tests.  Note that for features, only
-features with system dependencies are included here.
+The following table lists all protocols cross-referenced to all operating
+systems that have at least CI build tests. Note that for features, only
+features with system dependencies are included here; if you don't see the
+feature you're interested in, it should be supported on your platform.
 
 .. role:: mark
 
@@ -276,78 +262,132 @@ FRR implements the following RFCs:
 
 .. note:: This list is incomplete.
 
-- :rfc:`1058`
-  :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.`
-- :rfc:`2082`
-  :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.`
-- :rfc:`2453`
-  :t:`RIP Version 2. G. Malkin. November 1998.`
-- :rfc:`2080`
-  :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.`
-- :rfc:`2328`
-  :t:`OSPF Version 2. J. Moy. April 1998.`
-- :rfc:`2370`
-  :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.`
-- :rfc:`3101`
-  :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.`
-- :rfc:`2740`
-  :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.`
+BGP
+----
+
 - :rfc:`1771`
   :t:`A Border Gateway Protocol 4 (BGP-4). Y. Rekhter & T. Li. March 1995.`
 - :rfc:`1965`
   :t:`Autonomous System Confederations for BGP. P. Traina. June 1996.`
 - :rfc:`1997`
   :t:`BGP Communities Attribute. R. Chandra, P. Traina & T. Li. August 1996.`
+- :rfc:`2439`
+  :t:`BGP Route Flap Damping. C. Villamizar, R. Chandra, R. Govindan. November 1998.`
 - :rfc:`2545`
   :t:`Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing. P.
   Marques, F. Dupont. March 1999.`
 - :rfc:`2796`
-  :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R.
-  Chandrasekeran. June 1996.`
-- :rfc:`2858`
-  :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D.
-  Katz. June 2000.`
+  :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. Chandrasekeran. June 1996.`
 - :rfc:`2842`
   :t:`Capabilities Advertisement with BGP-4. R. Chandra, J. Scudder. May 2000.`
+- :rfc:`2858`
+  :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D.`
+- :rfc:`3107`
+  :t:`Carrying Label Information in BGP-4. Y. Rekhter & E. Rosen. May 2001.`
+- :rfc:`3765`
+  :t:`NOPEER Community for Border Gateway Protocol (BGP) Route Scope Control. G.Huston, April 2001.`
+- :rfc:`4271`
+  :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:`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:`5004`
+  :t:`Avoid BGP Best Path Transitions from One External to Another. E. Chen & S. Sangli. September 2007 (Partial support).`
+- :rfc:`5082`
+  :t:`The Generalized TTL Security Mechanism (GTSM). V. Gill, J. Heasley, D. Meyer, P. Savola, C. Pingnataro. October 2007.`
+- :rfc:`5575`
+  :t:`Dissemination of Flow Specification Rules. P. Marques, N. Sheth, R. Raszuk, B. Greene, J. Mauch, D. McPherson. August 2009`
+- :rfc:`6810`
+  :t:`The Resource Public Key Infrastructure (RPKI) to Router Protocol. R. Bush, R. Austein. January 2013.`
+- :rfc:`6811`
+  :t:`BGP Prefix Origin Validation. P. Mohapatra, J. Scudder, D. Ward, R. Bush, R. Austein. January 2013.`
+- :rfc:`7611`
+  :t:`BGP ACCEPT_OWN Community Attribute. J. Uttaro, P. Mohapatra, D. Smith, R. Raszuk, J. Scudder. August 2015.`
+- :rfc:`7999`
+  :t:`BLACKHOLE Community. T. King, C. Dietzel, J. Snijders, G. Doering, G. Hankins. Oct 2016.`
+- :rfc:`8092`
+  :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:`8212`
+  :t:`Default External BGP (EBGP) Route Propagation Behavior without Policies. J. Mauch, J. Snijders, G. Hankins. July 2017`
+- :rfc:`8277`
+  :t:`Using BGP to Bind MPLS Labels to Address Prefixes. E. Rosen. October 2017`
+
+
+OSPF
+----
+
+- :rfc:`2328`
+  :t:`OSPF Version 2. J. Moy. April 1998.`
+- :rfc:`2370`
+  :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.`
+- :rfc:`3101`
+  :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.`
+- :rfc:`2740`
+  :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.`
 - :rfc:`3137`
-  :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin,
-  D. McPherson. June 2001`
+  :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, D. McPherson. June 2001`
+
+ISIS
+----
+
+RIP
+----
+
+- :rfc:`1058`
+  :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.`
+- :rfc:`2082`
+  :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.`
+- :rfc:`2453`
+  :t:`RIP Version 2. G. Malkin. November 1998.`
+- :rfc:`2080`
+  :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.`
+
+PIM
+----
+
+BFD
+----
+- :rfc:`5880`
+  :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
+- :rfc:`5881`
+  :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010`
+- :rfc:`5883`
+  :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010`
+
+MPLS
+----
+
+- :rfc:`2858`
+  :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. Katz. June 2000.`
+- :rfc:`4364`
+  :t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.`
 - :rfc:`4447`
-  :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol
-  (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April
-  2006.`
+  :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. 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:`4762`
-  :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol
-  (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.`
+  :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.`
 - :rfc:`5036`
   :t:`LDP Specification, L. Andersson, I. Minei, and B. Thomas. October 2007.`
 - :rfc:`5561`
-  :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and
-  JL. Le Roux. July 2009.`
+  :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and JL. Le Roux. July 2009.`
 - :rfc:`5918`
-  :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence
-  Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.`
+  :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.`
 - :rfc:`5919`
-  :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra,
-  E. Chen, and B. Thomas. August 2010.`
+  :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra, E. Chen, and B. Thomas. August 2010.`
 - :rfc:`6667`
-  :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and
-  Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July
-  2012.`
+  :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July 2012.`
 - :rfc:`6720`
-  :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution
-  Protocol (LDP), C. Pignataro and R. Asati. August 2012.`
+  :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution Protocol (LDP), C. Pignataro and R. Asati. August 2012.`
 - :rfc:`7552`
-  :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral,
-  and R. Papneja. June 2015.`
-- :rfc:`5880`
-  :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
-- :rfc:`5881`
-  :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop),
-  D. Katz, D. Ward. June 2010`
-- :rfc:`5883`
-  :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz,
-  D. Ward. June 2010`
+  :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral, and R. Papneja. June 2015.`
+
+
+SNMP
+----
 
 **When SNMP support is enabled, the following RFCs are also supported:**
 
index 68e460748c0e12ca4007a5778472d207a2041aad..68659fbf504cd192275436c7235adffe38826555 100644 (file)
@@ -39,7 +39,7 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched.
    sub-mode where you can specify individual nexthops.  To exit this mode type
    exit or end as per normal conventions for leaving a sub-mode.
 
-.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
+.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS]
 
    Create a v4 or v6 nexthop.  All normal rules for creating nexthops that you
    are used to are allowed here.  The syntax was intentionally kept the same as
@@ -115,6 +115,12 @@ end destination.
 
    Not supported with NETNS VRF backend.
 
+.. clicmd:: show pbr map [NAME] [detail]
+
+   Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will
+   give information about the rules unique ID used internally and some extra
+   debugging information about install state for the nexthop/nexthop group.
+
 .. _pbr-policy:
 
 PBR Policy
index 4f9c573a246bc685dde28bd1f32fdd11bfda8634..6bda692607e1aad251a3c12f6748e4906b3d2eaf 100644 (file)
@@ -11,6 +11,13 @@ vrf aware and can work within the context of vrf's in order to
 do S,G mrouting.  Additionally PIM can be used in the EVPN underlay
 network for optimizing forwarding of overlay BUM traffic.
 
+.. note::
+
+   On Linux for PIM-SM operation you *must* have kernel version 4.18 or greater.
+   To use PIM for EVPN BUM forwarding, kernels 5.0 or greater are required.
+   OpenBSD has no multicast support and FreeBSD, NetBSD and Solaris only
+   have support for SSM.
+
 .. _starting-and-stopping-pimd:
 
 Starting and Stopping pimd
@@ -197,8 +204,8 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
 
    Set the pim hello and hold interval for a interface.
 
-.. index:: ip pim sm
-.. clicmd:: ip pim sm
+.. index:: ip pim
+.. clicmd:: ip pim
 
    Tell pim that we would like to use this interface to form pim neighbors
    over. Please note that this command does not enable the reception of IGMP
@@ -211,10 +218,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
    Tell pim to receive IGMP reports and Query on this interface. The default
    version is v3. This command is useful on a LHR.
 
-.. index:: ip igmp join A.B.C.D A.B.C.D
-.. clicmd:: ip igmp join A.B.C.D A.B.C.D
+.. index:: ip igmp join A.B.C.D [A.B.C.D]
+.. clicmd:: ip igmp join A.B.C.D [A.B.C.D]
 
-   Join multicast source-group on an interface.
+   Join multicast group or source-group on an interface.
 
 .. index:: ip igmp query-interval (1-1800)
 .. clicmd:: ip igmp query-interval (1-1800)
@@ -252,6 +259,13 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
    10 deciseconds. 'no' form of this command is used to to configure back to the
    default value.
 
+.. index:: ip mroute INTERFACE A.B.C.D [A.B.C.D]
+.. clicmd:: ip mroute INTERFACE A.B.C.D [A.B.C.D]
+
+   Set a static multicast route for a traffic coming on the current interface to
+   be forwarded on the given interface if the traffic matches the group address
+   and optionally the source address.
+
 .. _pim-multicast-rib-insertion:
 
 PIM Multicast RIB insertion:
index 2099dfdd62c0319d7c85a2d92ea922ad889582f7..23a062ab02f00e4211c4d23bb5d16bc59cc4e888 100644 (file)
@@ -916,3 +916,22 @@ zebra Terminal Mode Commands
 
    Display nexthop groups created by zebra.
 
+
+Router-id
+=========
+
+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]
+
+   Configure the router-id of this router.
+
+.. index:: show router-id [vrf NAME]
+.. clicmd:: show router-id [vrf NAME]
+
+   Display the user configured router-id.
+
+
index 88c8f88f81d01b142d0bb50fb4e71b539a3b8d76..ed6453e2b1ecf5f565c15798a2e9a339e1dbbf6f 100644 (file)
@@ -42,6 +42,7 @@ USER builder
 RUN cd /dist \
        && abuild-keygen -a -n \
        && abuild checksum \
+       && git init \
        && abuild -r -P /pkgs/apk
 
 # This stage installs frr from the apk
diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile
new file mode 100644 (file)
index 0000000..a8564bd
--- /dev/null
@@ -0,0 +1,43 @@
+# This stage builds an rpm from the source
+FROM centos:centos7 as centos-7-builder
+
+RUN yum install -y rpm-build autoconf automake libtool make \
+        readline-devel texinfo net-snmp-devel groff pkgconfig \
+        json-c-devel pam-devel bison flex pytest c-ares-devel \
+        python-devel systemd-devel python-sphinx libcap-devel \
+        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+
+COPY . /src
+ARG PKGVER
+
+RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
+    && cd /src \
+    && ./bootstrap.sh \
+    && ./configure \
+        --enable-rpki \
+        --enable-numeric-version \
+        --with-pkg-extra-version="_git$PKGVER" \
+    && make dist \
+    && cd / \
+    && mkdir -p /rpmbuild/{SOURCES,SPECS} \
+    && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
+    && cp /src/redhat/frr.spec /rpmbuild/SPECS \
+    && rpmbuild \
+        --define "_topdir /rpmbuild" \
+        -ba /rpmbuild/SPECS/frr.spec
+
+# This stage installs frr from the rpm
+FROM centos:centos7
+RUN mkdir -p /pkgs/rpm \
+    && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+
+COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/
+
+RUN yum install -y /pkgs/rpm/*/*.rpm \
+    && rm -rf /pkgs
+COPY docker/centos-7/docker-start /usr/lib/frr/docker-start
+ENTRYPOINT [ "/usr/lib/frr/docker-start" ]
diff --git a/docker/centos-7/build.sh b/docker/centos-7/build.sh
new file mode 100755 (executable)
index 0000000..b3022d7
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -e
+
+##
+# Package version needs to be decimal
+##
+GITREV="$(git rev-parse --short=10 HEAD)"
+PKGVER="$(printf '%u\n' 0x$GITREV)"
+
+mkdir -p docker/centos-7/pkgs
+docker build \
+       --file=docker/centos-7/Dockerfile \
+       --build-arg="PKGVER=$PKGVER" \
+       --tag="frr:centos-7-builder-$GITREV" \
+       --target=centos-7-builder \
+       .
+
+# Copy RPM package from container to host
+CONTAINER_ID="$(docker create "frr:centos-7-builder-$GITREV")"
+docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-7/pkgs
+docker rm "${CONTAINER_ID}"
+
+docker build \
+       --cache-from="frr:centos-7-builder-$GITREV" \
+       --file=docker/centos-7/Dockerfile \
+       --build-arg="PKGVER=$PKGVER" \
+       --tag="frr:centos-7-$GITREV" \
+       .
+
+docker rmi "frr:centos-7-builder-$GITREV"
diff --git a/docker/centos-7/docker-start b/docker/centos-7/docker-start
new file mode 100755 (executable)
index 0000000..a391324
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -e
+
+##
+# Change owner for docker volume mount
+##
+chown -R frr:frr /etc/frr
+/usr/lib/frr/frrinit.sh start
+
+# Sleep forever
+exec tail -f /dev/null
diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile
new file mode 100644 (file)
index 0000000..6c1f873
--- /dev/null
@@ -0,0 +1,46 @@
+# This stage builds an rpm from the source
+FROM centos:centos8 as centos-8-builder
+
+RUN dnf install --enablerepo=PowerTools -y rpm-build git autoconf pcre-devel \
+        automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \
+        groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \
+        c-ares-devel python2-devel systemd-devel libcap-devel platform-python-devel \
+        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+
+RUN pip2 install sphinx
+
+COPY . /src
+
+ARG PKGVER
+
+RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
+    && cd /src \
+    && ./bootstrap.sh \
+    && ./configure \
+        --enable-rpki \
+        --enable-numeric-version \
+        --with-pkg-extra-version="_git$PKGVER" \
+    && make dist \
+    && cd / \
+    && mkdir -p /rpmbuild/{SOURCES,SPECS} \
+    && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
+    && cp /src/redhat/frr.spec /rpmbuild/SPECS \
+    && rpmbuild \
+        --define "_topdir /rpmbuild" \
+        -ba /rpmbuild/SPECS/frr.spec
+
+# This stage installs frr from the rpm
+FROM centos:centos8
+RUN mkdir -p /pkgs/rpm \
+    && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+
+COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/
+
+RUN yum install -y /pkgs/rpm/*/*.rpm \
+    && rm -rf /pkgs
+COPY docker/centos-8/docker-start /usr/lib/frr/docker-start
+ENTRYPOINT [ "/usr/lib/frr/docker-start" ]
diff --git a/docker/centos-8/build.sh b/docker/centos-8/build.sh
new file mode 100755 (executable)
index 0000000..4a99184
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -e
+
+##
+# Package version needs to be decimal
+##
+GITREV="$(git rev-parse --short=10 HEAD)"
+PKGVER="$(printf '%u\n' 0x$GITREV)"
+
+mkdir -p docker/centos-8/pkgs
+docker build \
+       --file=docker/centos-8/Dockerfile \
+       --build-arg="PKGVER=$PKGVER" \
+       --tag="frr:centos-8-builder-$GITREV" \
+       --target=centos-8-builder \
+       .
+
+# Copy RPM package from container to host
+CONTAINER_ID="$(docker create "frr:centos-8-builder-$GITREV")"
+docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-8/pkgs
+docker rm "${CONTAINER_ID}"
+
+docker build \
+       --cache-from="frr:centos-8-builder-$GITREV" \
+       --file=docker/centos-8/Dockerfile \
+       --build-arg="PKGVER=$PKGVER" \
+       --tag="frr:centos-8-$GITREV" \
+       .
+
+docker rmi "frr:centos-8-builder-$GITREV"
diff --git a/docker/centos-8/docker-start b/docker/centos-8/docker-start
new file mode 100755 (executable)
index 0000000..935b222
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -e
+
+chown -R frr:frr /etc/frr
+/usr/lib/frr/frrinit.sh start
+
+# Sleep forever
+exec tail -f /dev/null
diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile
deleted file mode 100644 (file)
index 088a320..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-# This stage builds an rpm from the source
-FROM centos:centos7 as centos-builder
-
-RUN yum install -y rpm-build autoconf automake libtool make \
-        readline-devel texinfo net-snmp-devel groff pkgconfig \
-        json-c-devel pam-devel bison flex pytest c-ares-devel \
-        python-devel systemd-devel python-sphinx libcap-devel \
-        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
-        https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \
-        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
-        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
-
-COPY . /src
-ARG PKGVER
-
-RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
-    && cd /src \
-    && ./bootstrap.sh \
-    && ./configure \
-        --enable-rpki \
-        --enable-numeric-version \
-        --with-pkg-extra-version="_git$PKGVER" \
-    && make dist \
-    && cd / \
-    && mkdir -p /rpmbuild/{SOURCES,SPECS} \
-    && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
-    && cp /src/redhat/frr.spec /rpmbuild/SPECS \
-    && rpmbuild \
-        --define "_topdir /rpmbuild" \
-        -ba /rpmbuild/SPECS/frr.spec
-
-# This stage installs frr from the rpm
-FROM centos:centos7
-RUN mkdir -p /pkgs/rpm \
-    && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
-        https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
-
-COPY --from=centos-builder /rpmbuild/RPMS/ /pkgs/rpm/
-
-RUN yum install -y /pkgs/rpm/*/*.rpm \
-    && rm -rf /pkgs
-COPY docker/centos/docker-start /usr/lib/frr/docker-start
-ENTRYPOINT [ "/usr/lib/frr/docker-start" ]
diff --git a/docker/centos/build.sh b/docker/centos/build.sh
deleted file mode 100755 (executable)
index 9cd0f61..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-
-set -e
-
-##
-# Package version needs to be decimal
-##
-GITREV="$(git rev-parse --short=10 HEAD)"
-PKGVER="$(printf '%u\n' 0x$GITREV)"
-
-mkdir -p docker/centos/pkgs
-docker build \
-       --file=docker/centos/Dockerfile \
-       --build-arg="PKGVER=$PKGVER" \
-       --tag="frr:centos-builder-$GITREV" \
-       --target=centos-builder \
-       .
-
-# Copy RPM package from container to host
-CONTAINER_ID="$(docker create "frr:centos-builder-$GITREV")"
-docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos/pkgs
-docker rm "${CONTAINER_ID}"
-
-docker build \
-       --cache-from="frr:centos-builder-$GITREV" \
-       --file=docker/centos/Dockerfile \
-       --build-arg="PKGVER=$PKGVER" \
-       --tag="frr:centos-$GITREV" \
-       .
-
-docker rmi "frr:centos-builder-$GITREV"
diff --git a/docker/centos/docker-start b/docker/centos/docker-start
deleted file mode 100755 (executable)
index a391324..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-set -e
-
-##
-# Change owner for docker volume mount
-##
-chown -R frr:frr /etc/frr
-/usr/lib/frr/frrinit.sh start
-
-# Sleep forever
-exec tail -f /dev/null
index 6294c0dd0fb0c59af252fccfdaa1ef095bc82dbf..ece0b4b0c4c4a4aeecadfc2415b6c056acdd01b4 100644 (file)
@@ -99,6 +99,9 @@ struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp,
        ei->params.auth_type = EIGRP_AUTH_TYPE_NONE;
        ei->params.auth_keychain = NULL;
 
+       ei->curr_bandwidth = ifp->bandwidth;
+       ei->curr_mtu = ifp->mtu;
+
        return ei;
 }
 
@@ -139,45 +142,40 @@ static int eigrp_ifp_create(struct interface *ifp)
 
 static int eigrp_ifp_up(struct interface *ifp)
 {
-       /* Interface is already up. */
-       if (if_is_operative(ifp)) {
-               /* Temporarily keep ifp values. */
-               struct interface if_tmp;
-               memcpy(&if_tmp, ifp, sizeof(struct interface));
+       struct eigrp_interface *ei = ifp->info;
 
-               if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
-                       zlog_debug("Zebra: Interface[%s] state update.",
-                                  ifp->name);
+       if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+               zlog_debug("Zebra: Interface[%s] state change to up.",
+                          ifp->name);
 
-               if (if_tmp.bandwidth != ifp->bandwidth) {
-                       if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
-                               zlog_debug(
-                                       "Zebra: Interface[%s] bandwidth change %d -> %d.",
-                                       ifp->name, if_tmp.bandwidth,
-                                       ifp->bandwidth);
+       if (!ei)
+               return 0;
 
-                       //          eigrp_if_recalculate_output_cost (ifp);
-               }
+       if (ei->curr_bandwidth != ifp->bandwidth) {
+               if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+                       zlog_debug(
+                               "Zebra: Interface[%s] bandwidth change %d -> %d.",
+                               ifp->name, ei->curr_bandwidth,
+                               ifp->bandwidth);
 
-               if (if_tmp.mtu != ifp->mtu) {
-                       if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
-                               zlog_debug(
-                                       "Zebra: Interface[%s] MTU change %u -> %u.",
-                                       ifp->name, if_tmp.mtu, ifp->mtu);
+               ei->curr_bandwidth = ifp->bandwidth;
+               // eigrp_if_recalculate_output_cost (ifp);
+       }
 
-                       /* Must reset the interface (simulate down/up) when MTU
-                        * changes. */
-                       eigrp_if_reset(ifp);
-               }
+       if (ei->curr_mtu != ifp->mtu) {
+               if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+                       zlog_debug(
+                               "Zebra: Interface[%s] MTU change %u -> %u.",
+                               ifp->name, ei->curr_mtu, ifp->mtu);
+
+               ei->curr_mtu = ifp->mtu;
+               /* Must reset the interface (simulate down/up) when MTU
+                * changes. */
+               eigrp_if_reset(ifp);
                return 0;
        }
 
-       if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
-               zlog_debug("Zebra: Interface[%s] state change to up.",
-                          ifp->name);
-
-       if (ifp->info)
-               eigrp_if_up(ifp->info);
+       eigrp_if_up(ifp->info);
 
        return 0;
 }
index e50858f0715288b4627a25c052841f91ad81a16f..82bddaaae3bbd5dbe51ecb6f857776278b5e48a3 100644 (file)
@@ -176,6 +176,8 @@ struct eigrp_interface {
 
        /* To which multicast groups do we currently belong? */
 
+       uint32_t curr_bandwidth;
+       uint32_t curr_mtu;
 
        uint8_t multicast_memberships;
 
index 5a65c654e93c4e5cbdafc98b74912bb9b1d4d81c..e59c88b471de991b32b7b5eb7004ca3514c674da 100644 (file)
@@ -12,7 +12,7 @@ vtysh_scan += \
        $(top_srcdir)/eigrpd/eigrp_vty.c \
        # end
 #      $(top_srcdir)/eigrpd/eigrp_routemap.c
-man8 += $(MANBUILD)/eigrpd.8
+man8 += $(MANBUILD)/frr-eigrpd.8
 endif
 
 eigrpd_libeigrp_a_SOURCES = \
index 7e79fdea1535c7146e5dfd16601f1d04b7747117..364441f79d92f7de34f434b7c8b5f98a1bce6c25 100644 (file)
@@ -29,7 +29,6 @@
 #include "command.h"
 #include "vty.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "stream.h"
 #include "if.h"
 #include "privs.h"
index 820cfaa426dc20505945bf584f06ba38104819c4..d14704b4ee18320a11ef0cfb7f77ea001edf97d8 100644 (file)
@@ -1550,9 +1550,13 @@ int lib_interface_isis_destroy(enum nb_event event,
        circuit = nb_running_unset_entry(dnode);
        if (!circuit)
                return NB_ERR_INCONSISTENCY;
-       if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF)
-               isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area);
 
+       /* disable both AFs for this circuit. this will also update the
+        * CSM state by sending an ISIS_DISABLED signal. If there is no
+        * area associated to the circuit there is nothing to do
+        */
+       if (circuit->area)
+               isis_circuit_af_set(circuit, false, false);
        return NB_OK;
 }
 
index 71249cf658aa027f9590bcfb9a4daf57dc7df531..cc22aa5ffd8650d7da1b9b99d0afd436c940f470 100644 (file)
@@ -1054,6 +1054,8 @@ dontcheckadj:
                                                   circuit->rcv_stream,
                                                   circuit->area, level,
                                                   lsp_confusion);
+                                       if (lsp_confusion)
+                                               isis_free_tlvs(tlvs);
                                        tlvs = NULL;
                                        /* ii */
                                        lsp_flood_or_update(lsp, NULL,
index b4c699ccbb5a05533caaa67af78b1d7c84cb200b..630264768f5ab79431749ca2c652664d9cedf9ba 100644 (file)
@@ -184,7 +184,7 @@ void isis_zebra_route_add_route(struct prefix *prefix,
                        break;
                api_nh = &api.nexthops[count];
                if (fabricd)
-                       api_nh->onlink = true;
+                       SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
                api_nh->vrf_id = VRF_DEFAULT;
 
                switch (nexthop->family) {
index f15d7a9c7eccca6e42c4d9ac567ae36ba391b2ef..47d2e9faabd96d76bccf439cab964c5709776e32 100644 (file)
@@ -107,13 +107,10 @@ struct isis_area *isis_area_create(const char *area_tag)
 
        /*
         * Fabricd runs only as level-2.
-        * For IS-IS, the first instance is level-1-2 rest are level-1,
-        * unless otherwise configured
+        * For IS-IS, the default is level-1-2
         */
-       if (fabricd) {
+       if (fabricd)
                area->is_type = IS_LEVEL_2;
-       } else if (listcount(isis->area_list) == 0)
-               area->is_type = IS_LEVEL_1_AND_2;
        else
                area->is_type = yang_get_default_enum(
                        "/frr-isisd:isis/instance/is-type");
index e77fef41dd6c74d7999cd521dfc2a96e05b59b99..5dddb7d3455fb50756418841ff83f5793aae19d4 100644 (file)
@@ -14,7 +14,7 @@ vtysh_scan += \
        $(top_srcdir)/isisd/isis_vty_fabricd.c \
        $(top_srcdir)/isisd/isisd.c \
        # end
-man8 += $(MANBUILD)/isisd.8
+man8 += $(MANBUILD)/frr-isisd.8
 endif
 
 if FABRICD
index 42c5ad024b2988197a9ef348cf501d1c9550f860..f464bad9e7c199ea039e97b2871af028397a0575 100644 (file)
@@ -7,7 +7,7 @@ noinst_LIBRARIES += ldpd/libldp.a
 sbin_PROGRAMS += ldpd/ldpd
 dist_examples_DATA += ldpd/ldpd.conf.sample
 vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c
-man8 += $(MANBUILD)/ldpd.8
+man8 += $(MANBUILD)/frr-ldpd.8
 endif
 
 ldpd_libldp_a_SOURCES = \
index dad6a13d674a6c445be069d0be6d9cb5b54173dc..22b981e28433be7aaff9250225514e0b0526bb3f 100644 (file)
@@ -41,7 +41,7 @@ static void agg_node_destroy(route_table_delegate_t *delegate,
        XFREE(MTYPE_TMP, anode);
 }
 
-route_table_delegate_t agg_table_delegate = {
+static route_table_delegate_t agg_table_delegate = {
        .create_node = agg_node_create,
        .destroy_node = agg_node_destroy,
 };
index bb2cdb7e5449dd2084e8a32a90df1620a09dc42f..766b9791a5d5034e5856239bb3e2e3ba2bd6ea94 100644 (file)
@@ -114,12 +114,6 @@ char *buffer_getstr(struct buffer *b)
        return s;
 }
 
-/* Return 1 if buffer is empty. */
-int buffer_empty(struct buffer *b)
-{
-       return (b->head == NULL);
-}
-
 /* Clear and free all allocated data. */
 void buffer_reset(struct buffer *b)
 {
index 9238ae412ab9c087be8bf9129be603fc95193ef2..d2145d9f5a03becf2282605bdf3c74b88cac31f9 100644 (file)
@@ -1575,18 +1575,6 @@ DEFUN (show_version,
        return CMD_SUCCESS;
 }
 
-/* "Set" version ... ignore version tags */
-DEFUN (frr_version_defaults,
-       frr_version_defaults_cmd,
-       "frr <version|defaults> LINE...",
-       "FRRouting global parameters\n"
-       "version configuration was written by\n"
-       "set of configuration defaults used\n"
-       "version string\n")
-{
-       return CMD_SUCCESS;
-}
-
 /* Help display function for all node. */
 DEFUN (config_help,
        config_help_cmd,
@@ -1721,8 +1709,10 @@ static int vty_write_config(struct vty *vty)
                vty_out(vty, "!\n");
        }
 
+       if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
+               vty_out(vty, "! loaded from %s\n", frr_defaults_version());
        vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
-       vty_out(vty, "frr defaults %s\n", DFLT_NAME);
+       vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
        vty_out(vty, "!\n");
 
        for (i = 0; i < vector_active(cmdvec); i++)
@@ -2941,7 +2931,6 @@ void cmd_init(int terminal)
        install_element(CONFIG_NODE, &no_hostname_cmd);
        install_element(CONFIG_NODE, &domainname_cmd);
        install_element(CONFIG_NODE, &no_domainname_cmd);
-       install_element(CONFIG_NODE, &frr_version_defaults_cmd);
 
        if (terminal > 0) {
                install_element(CONFIG_NODE, &debug_memstats_cmd);
index f361db78e940d3ea0657b5dd1c4a103543109f48..0556605d637cd5d731b1a692db1b2e1ae99c1e01 100644 (file)
@@ -85,7 +85,7 @@ RANGE           \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
 .               {return yytext[0];}
 %%
 
-YY_BUFFER_STATE buffer;
+static YY_BUFFER_STATE buffer;
 
 void set_lexer_string (yyscan_t *scn, const char *string)
 {
diff --git a/lib/defaults.c b/lib/defaults.c
new file mode 100644 (file)
index 0000000..71ccc73
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * FRR switchable defaults.
+ * Copyright (c) 2017-2019  David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#include "defaults.h"
+#include "version.h"
+
+static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME;
+static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first;
+
+/* these are global for all FRR daemons.  they have to be, since we write an
+ * integrated config with the same value for all daemons.
+ */
+const char *frr_defaults_profiles[] = {
+       "traditional",
+       "datacenter",
+       NULL,
+};
+
+static int version_value(int ch)
+{
+       /* non-ASCII shouldn't happen */
+       if (ch < 0 || ch >= 128)
+               return 2;
+
+       /* ~foo sorts older than nothing */
+       if (ch == '~')
+               return 0;
+       if (ch == '\0')
+               return 1;
+       if (isalpha(ch))
+               return 0x100 + tolower(ch);
+
+       /* punctuation and digits (and everything else) */
+       return 0x200 + ch;
+}
+
+int frr_version_cmp(const char *aa, const char *bb)
+{
+       const char *apos = aa, *bpos = bb;
+
+       /* || is correct, we won't scan past the end of a string since that
+        * doesn't compare equal to anything else */
+       while (apos[0] || bpos[0]) {
+               if (isdigit((unsigned char)apos[0]) &&
+                   isdigit((unsigned char)bpos[0])) {
+                       unsigned long av, bv;
+                       char *aend = NULL, *bend = NULL;
+
+                       av = strtoul(apos, &aend, 10);
+                       bv = strtoul(bpos, &bend, 10);
+                       if (av < bv)
+                               return -1;
+                       if (av > bv)
+                               return 1;
+
+                       apos = aend;
+                       bpos = bend;
+                       continue;
+               }
+
+               int a = version_value(*apos++);
+               int b = version_value(*bpos++);
+
+               if (a < b)
+                       return -1;
+               if (a > b)
+                       return 1;
+       }
+       return 0;
+}
+
+static void frr_default_apply_one(struct frr_default *dflt, bool check);
+
+void frr_default_add(struct frr_default *dflt)
+{
+       dflt->next = NULL;
+       *dflt_next = dflt;
+       dflt_next = &dflt->next;
+
+       frr_default_apply_one(dflt, true);
+}
+
+static bool frr_match_version(const char *name, const char *vspec,
+                             const char *version, bool check)
+{
+       int cmp;
+       static struct spec {
+               const char *str;
+               bool dir, eq;
+       } *s, specs[] = {
+               {"<=", -1, 1},
+               {">=", 1, 1},
+               {"==", 0, 1},
+               {"<", -1, 0},
+               {">", 1, 0},
+               {"=", 0, 1},
+               {NULL, 0, 0},
+       };
+
+       if (!vspec)
+               /* NULL = all versions */
+               return true;
+
+       for (s = specs; s->str; s++)
+               if (!strncmp(s->str, vspec, strlen(s->str)))
+                       break;
+       if (!s->str) {
+               if (check)
+                       fprintf(stderr, "invalid version specifier for %s: %s",
+                               name, vspec);
+               /* invalid version spec, never matches */
+               return false;
+       }
+
+       vspec += strlen(s->str);
+       while (isspace((unsigned char)*vspec))
+               vspec++;
+
+       cmp = frr_version_cmp(version, vspec);
+       if (cmp == s->dir || (s->eq && cmp == 0))
+               return true;
+
+       return false;
+}
+
+static void frr_default_apply_one(struct frr_default *dflt, bool check)
+{
+       struct frr_default_entry *entry = dflt->entries;
+       struct frr_default_entry *dfltentry = NULL, *saveentry = NULL;
+
+       for (; entry->match_version || entry->match_profile; entry++) {
+               if (entry->match_profile
+                       && strcmp(entry->match_profile, df_profile))
+                       continue;
+
+               if (!dfltentry && frr_match_version(dflt->name,
+                               entry->match_version, df_version, check))
+                       dfltentry = entry;
+               if (!saveentry && frr_match_version(dflt->name,
+                               entry->match_version, FRR_VER_SHORT, check))
+                       saveentry = entry;
+
+               if (dfltentry && saveentry && !check)
+                       break;
+       }
+       /* found default or arrived at last entry that has NULL,NULL spec */
+
+       if (!dfltentry)
+               dfltentry = entry;
+       if (!saveentry)
+               saveentry = entry;
+
+       if (dflt->dflt_bool)
+               *dflt->dflt_bool = dfltentry->val_bool;
+       if (dflt->dflt_str)
+               *dflt->dflt_str = dfltentry->val_str;
+       if (dflt->dflt_long)
+               *dflt->dflt_long = dfltentry->val_long;
+       if (dflt->dflt_ulong)
+               *dflt->dflt_ulong = dfltentry->val_ulong;
+       if (dflt->dflt_float)
+               *dflt->dflt_float = dfltentry->val_float;
+       if (dflt->save_bool)
+               *dflt->save_bool = saveentry->val_bool;
+       if (dflt->save_str)
+               *dflt->save_str = saveentry->val_str;
+       if (dflt->save_long)
+               *dflt->save_long = saveentry->val_long;
+       if (dflt->save_ulong)
+               *dflt->save_ulong = saveentry->val_ulong;
+       if (dflt->save_float)
+               *dflt->save_float = saveentry->val_float;
+}
+
+void frr_defaults_apply(void)
+{
+       struct frr_default *dflt;
+
+       for (dflt = dflt_first; dflt; dflt = dflt->next)
+               frr_default_apply_one(dflt, false);
+}
+
+bool frr_defaults_profile_valid(const char *profile)
+{
+       const char **p;
+
+       for (p = frr_defaults_profiles; *p; p++)
+               if (!strcmp(profile, *p))
+                       return true;
+       return false;
+}
+
+const char *frr_defaults_version(void)
+{
+       return df_version;
+}
+
+const char *frr_defaults_profile(void)
+{
+       return df_profile;
+}
+
+void frr_defaults_version_set(const char *version)
+{
+       strlcpy(df_version, version, sizeof(df_version));
+       frr_defaults_apply();
+}
+
+void frr_defaults_profile_set(const char *profile)
+{
+       strlcpy(df_profile, profile, sizeof(df_profile));
+       frr_defaults_apply();
+}
diff --git a/lib/defaults.h b/lib/defaults.h
new file mode 100644 (file)
index 0000000..7cdd181
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * FRR switchable defaults.
+ * Copyright (C) 2017-2019  David Lamparter for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_DEFAULTS_H
+#define _FRR_DEFAULTS_H
+
+#include <stdbool.h>
+
+#include "compiler.h"
+
+/* frr_default wraps information about a default that has different
+ * values depending on FRR version or default-set
+ *
+ * frr_default_entry describes one match rule and the resulting value;
+ * entries are evaluated in order and the first matching is used.
+ *
+ * If both match_version and match_profile are specified, they must both
+ * match.  A NULL value matches everything.
+ */
+struct frr_default_entry {
+       /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g.
+        *   ">= 6.1-dev" "<6.0"
+        */
+       const char *match_version;
+       /* exact profile string to compare against */
+       const char *match_profile;
+
+       /* value to use */
+       bool val_bool;
+       const char *val_str;
+       long val_long;
+       unsigned long val_ulong;
+       float val_float;
+};
+
+/* one struct frr_default exists for each malleable default value */
+struct frr_default {
+       struct frr_default *next;
+
+       /* for UI/debug use */
+       const char *name;
+
+       /* the following two sets of variables differ because the written
+        * config always targets the *current* FRR version
+        *
+        * e.g. if you load a config that has "frr version 5.0" on 6.0
+        *   *dflt_long => set to the default value in 5.0
+        *   *save_long => set to the default value in 6.0
+        * config save will write "frr version 6.0" with 6.0 defaults
+        */
+
+       /* variable holding the default value for reading/use */
+       bool *dflt_bool;
+       const char **dflt_str;
+       long *dflt_long;
+       unsigned long *dflt_ulong;
+       float *dflt_float;
+
+       /* variable to use when comparing for config save */
+       bool *save_bool;
+       const char **save_str;
+       long *save_long;
+       unsigned long *save_ulong;
+       float *save_float;
+
+       struct frr_default_entry entries[];
+};
+
+#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \
+       static type DFLT_##varname;                                            \
+       static type SAVE_##varname;                                            \
+       static struct frr_default _dflt_##varname = {                          \
+               .name = #varname,                                              \
+               .dflt_##typname = &DFLT_##varname,                             \
+               .save_##typname = &SAVE_##varname,                             \
+               .entries = { __VA_ARGS__ },                                    \
+       };                                                                     \
+       static void _dfltinit_##varname(void)                                  \
+               __attribute__((_CONSTRUCTOR(1000)));                           \
+       static void _dfltinit_##varname(void)                                  \
+       {                                                                      \
+               frr_default_add(&_dflt_##varname);                             \
+       }
+
+/* use:
+ *   FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS,
+ *     { .val_long = 2, .match_version = ">= 10.0" },
+ *     { .val_long = 1, .match_profile = "datacenter" },
+ *     { .val_long = 0 },
+ *   )
+ *
+ * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables.
+ *
+ * Note: preprocessor defines cannot be used as variable names because they
+ * will be expanded and blow up with a compile error.  Use an enum or add an
+ * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS)
+ */
+#define FRR_CFG_DEFAULT_BOOL(varname, ...) \
+       _FRR_CFG_DEFAULT(bool, bool, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_LONG(varname, ...) \
+       _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_ULONG(varname, ...) \
+       _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \
+       _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_STR(varname, ...) \
+       _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__)
+
+
+/* daemons don't need to call any of these, libfrr handles that */
+extern void frr_default_add(struct frr_default *dflt);
+extern void frr_defaults_version_set(const char *version);
+extern void frr_defaults_profile_set(const char *profile);
+extern const char *frr_defaults_version(void);
+extern const char *frr_defaults_profile(void);
+extern void frr_defaults_apply(void);
+
+extern const char *frr_defaults_profiles[];
+extern bool frr_defaults_profile_valid(const char *profile);
+
+/* like strcmp(), but with version ordering */
+extern int frr_version_cmp(const char *aa, const char *bb);
+
+#endif /* _FRR_DEFAULTS_H */
index ed3ffe9c67226d70b5bc9a3511cbe568cccbd8f9..31e25d600136920c74076d4be64fb0459aae309a 100644 (file)
@@ -2498,8 +2498,8 @@ DEFUN (no_ipv6_access_list_remark_comment,
        return no_ipv6_access_list_remark(self, vty, argc, argv);
 }
 
-void config_write_access_zebra(struct vty *, struct filter *);
-void config_write_access_cisco(struct vty *, struct filter *);
+static void config_write_access_zebra(struct vty *, struct filter *);
+static void config_write_access_cisco(struct vty *, struct filter *);
 
 /* show access-list command. */
 static int filter_show(struct vty *vty, const char *name, afi_t afi)
@@ -2685,7 +2685,7 @@ DEFUN (show_ipv6_access_list_name,
        return filter_show(vty, argv[idx_word]->arg, AFI_IP6);
 }
 
-void config_write_access_cisco(struct vty *vty, struct filter *mfilter)
+static void config_write_access_cisco(struct vty *vty, struct filter *mfilter)
 {
        struct filter_cisco *filter;
 
@@ -2724,7 +2724,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter)
        }
 }
 
-void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
+static void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
 {
        struct filter_zebra *filter;
        struct prefix *p;
index d65a4a98bf631e4c5e3449aeeb748e8a5fde9ecc..7e6475b6487e5992a74b8cba5c83632ee5c2d480 100644 (file)
@@ -206,7 +206,7 @@ void rcu_thread_unprepare(struct rcu_thread *rt)
        rcu_bump();
        if (rt != &rcu_thread_main)
                /* this free() happens after seqlock_release() below */
-               rcu_free_internal(&_mt_RCU_THREAD, rt, rcu_head);
+               rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head);
 
        rcu_threads_del(&rcu_threads, rt);
        seqlock_release(&rt->rcu);
@@ -269,7 +269,7 @@ static void rcu_bump(void)
         * "last item is being deleted - start over" case, and then we may end
         * up accessing old RCU queue items that are already free'd.
         */
-       rcu_free_internal(&_mt_RCU_NEXT, rn, head_free);
+       rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free);
 
        /* Only allow the RCU sweeper to run after these 2 items are queued.
         *
index 8f789303cc5cdfc43a27378d2f026dd7d1140dc9..06d87c39f1dd3debb1987b346156641d77e9fb28 100644 (file)
@@ -139,6 +139,8 @@ extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action);
 #define rcu_free(mtype, ptr, field)                                            \
        do {                                                                   \
                typeof(ptr) _ptr = (ptr);                                      \
+               if (!_ptr)                                                     \
+                       break;                                                 \
                struct rcu_head *_rcu_head = &_ptr->field;                     \
                static const struct rcu_action _rcu_action = {                 \
                        .type = RCUA_FREE,                                     \
index 2718046d0bfd075b9f9c1fa2a0563a8543bb310f..dd25c8976a4bd787095a57fa5bafa5c9ce04a78f 100644 (file)
@@ -6,7 +6,7 @@ chdir $dir || die "$dir: $!\n";
 
 my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`;
 chomp $gitdesc;
-my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
+my $gitsuffix = ($gitdesc =~ /-g([0-9a-fA-F]+(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
 
 printf STDERR "git suffix: %s\n", $gitsuffix;
 printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix;
index c6fd3c04adece80ad9f8930e56f15a5e62935a12..8ccdbfcbc12532aacf01b17e2790415467452ed9 100644 (file)
@@ -28,7 +28,6 @@
 #endif
 
 #include "command.h"
-#include "memory_vty.h"
 #include "graph.h"
 #include "linklist.h"
 #include "command_match.h"
@@ -39,9 +38,9 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
 
 /** headers **/
 void grammar_sandbox_init(void);
-void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
-                       struct graph_node **, size_t);
-void init_cmdgraph(struct vty *, struct graph **);
+static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
+                              struct graph_node **, size_t);
+static void init_cmdgraph(struct vty *, struct graph **);
 
 /** shim interface commands **/
 static struct graph *nodegraph = NULL, *nodegraph_free = NULL;
@@ -492,8 +491,9 @@ void grammar_sandbox_init(void)
  * @param start the node to take as the root
  * @param level indent level for recursive calls, always pass 0
  */
-void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
-                       int desc, struct graph_node **stack, size_t stackpos)
+static void pretty_print_graph(struct vty *vty, struct graph_node *start,
+                              int level, int desc, struct graph_node **stack,
+                              size_t stackpos)
 {
        // print this node
        char tokennum[32];
@@ -551,7 +551,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
 }
 
 /** stuff that should go in command.c + command.h */
-void init_cmdgraph(struct vty *vty, struct graph **graph)
+static void init_cmdgraph(struct vty *vty, struct graph **graph)
 {
        // initialize graph, add start noe
        *graph = graph_new();
index 6d28a667b387b968b3038050ebed247fc5c48afa..4bd8f5138aad0cb3dcf1e1401fac322076917410 100644 (file)
@@ -28,7 +28,7 @@
 #endif
 
 #include "command.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 
 static void vty_do_exit(int isexit)
 {
@@ -57,7 +57,7 @@ int main(int argc, char **argv)
        host.domainname = strdup("testdomainname");
 
        vty_init(master, true);
-       memory_init();
+       lib_cmd_init();
        yang_init();
        nb_init(master, NULL, 0);
 
index 20215640e637f1117d374440bcfa3cc695ca5903..7332dceb45e89bf7d336a18029ae073349072108 100644 (file)
--- a/lib/if.c
+++ b/lib/if.c
@@ -58,7 +58,7 @@ DEFINE_QOBJ_TYPE(interface)
 DEFINE_HOOK(if_add, (struct interface * ifp), (ifp))
 DEFINE_KOOH(if_del, (struct interface * ifp), (ifp))
 
-struct interface_master{
+static struct interface_master{
        int (*create_hook)(struct interface *ifp);
        int (*up_hook)(struct interface *ifp);
        int (*down_hook)(struct interface *ifp);
@@ -137,7 +137,12 @@ static int if_cmp_func(const struct interface *ifp1,
 static int if_cmp_index_func(const struct interface *ifp1,
                             const struct interface *ifp2)
 {
-       return ifp1->ifindex - ifp2->ifindex;
+       if (ifp1->ifindex == ifp2->ifindex)
+               return 0;
+       else if (ifp1->ifindex > ifp2->ifindex)
+               return 1;
+       else
+               return -1;
 }
 
 static void ifp_connected_free(void *arg)
@@ -262,7 +267,9 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
                        "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
                        ifp->name, old_vrf->name);
                if (if_dnode) {
+                       nb_running_unset_entry(if_dnode->parent);
                        yang_dnode_change_leaf(if_dnode, vrf->name);
+                       nb_running_set_entry(if_dnode->parent, ifp);
                        running_config->version++;
                }
        }
index ec08fc09a20edfbc612206f10fab790f52bba5e5..6bd614044c768d27d9979429b6ca39d2651e0712 100644 (file)
@@ -119,6 +119,12 @@ static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6,
        memcpy(in, (char *)in6 + 12, sizeof(struct in_addr));
 }
 
+static inline bool ipaddr_isset(struct ipaddr *ip)
+{
+       static struct ipaddr a = {};
+       return (0 == memcmp(&a, ip, sizeof(struct ipaddr)));
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
new file mode 100644 (file)
index 0000000..787da08
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Assorted library VTY commands
+ *
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * Copyright (C) 2016-2017  David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public 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 <zebra.h>
+/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_MALLOC_MALLOC_H
+#include <malloc/malloc.h>
+#endif
+#include <dlfcn.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+
+#include "log.h"
+#include "memory.h"
+#include "module.h"
+#include "defaults.h"
+#include "lib_vty.h"
+
+/* Looking up memory status from vty interface. */
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+
+#ifdef HAVE_MALLINFO
+static int show_memory_mallinfo(struct vty *vty)
+{
+       struct mallinfo minfo = mallinfo();
+       char buf[MTYPE_MEMSTR_LEN];
+
+       vty_out(vty, "System allocator statistics:\n");
+       vty_out(vty, "  Total heap allocated:  %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
+       vty_out(vty, "  Holding block headers: %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
+       vty_out(vty, "  Used small blocks:     %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
+       vty_out(vty, "  Used ordinary blocks:  %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
+       vty_out(vty, "  Free small blocks:     %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
+       vty_out(vty, "  Free ordinary blocks:  %s\n",
+               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
+       vty_out(vty, "  Ordinary blocks:       %ld\n",
+               (unsigned long)minfo.ordblks);
+       vty_out(vty, "  Small blocks:          %ld\n",
+               (unsigned long)minfo.smblks);
+       vty_out(vty, "  Holding blocks:        %ld\n",
+               (unsigned long)minfo.hblks);
+       vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
+       return 1;
+}
+#endif /* HAVE_MALLINFO */
+
+static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
+{
+       struct vty *vty = arg;
+       if (!mt) {
+               vty_out(vty, "--- qmem %s ---\n", mg->name);
+               vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
+                       "Type", "Current#", "  Size",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+                       "     Total",
+#else
+                       "",
+#endif
+                       "Max#",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+                       "MaxBytes"
+#else
+                       ""
+#endif
+                       );
+       } else {
+               if (mt->n_alloc != 0) {
+                       char size[32];
+                       snprintf(size, sizeof(size), "%6zu", mt->size);
+#ifdef HAVE_MALLOC_USABLE_SIZE
+#define TSTR " %9zu"
+#define TARG , mt->total
+#define TARG2 , mt->max_size
+#else
+#define TSTR ""
+#define TARG
+#define TARG2
+#endif
+                       vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
+                               mt->name,
+                               mt->n_alloc,
+                               mt->size == 0 ? ""
+                                             : mt->size == SIZE_VAR
+                                                       ? "variable"
+                                                       : size
+                               TARG,
+                               mt->n_max
+                               TARG2);
+               }
+       }
+       return 0;
+}
+
+
+DEFUN_NOSH (show_memory,
+           show_memory_cmd,
+           "show memory",
+           "Show running system information\n"
+           "Memory statistics\n")
+{
+#ifdef HAVE_MALLINFO
+       show_memory_mallinfo(vty);
+#endif /* HAVE_MALLINFO */
+
+       qmem_walk(qmem_walker, vty);
+       return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_modules,
+           show_modules_cmd,
+           "show modules",
+           "Show running system information\n"
+           "Loaded modules\n")
+{
+       struct frrmod_runtime *plug = frrmod_list;
+
+       vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
+               "Description");
+       while (plug) {
+               const struct frrmod_info *i = plug->info;
+
+               vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
+                       i->description);
+               if (plug->dl_handle) {
+#ifdef HAVE_DLINFO_ORIGIN
+                       char origin[MAXPATHLEN] = "";
+                       dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
+#ifdef HAVE_DLINFO_LINKMAP
+                       const char *name;
+                       struct link_map *lm = NULL;
+                       dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
+                       if (lm) {
+                               name = strrchr(lm->l_name, '/');
+                               name = name ? name + 1 : lm->l_name;
+                               vty_out(vty, "\tfrom: %s/%s\n", origin, name);
+                       }
+#else
+                       vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
+#endif
+#else
+                       vty_out(vty, "\tfrom: %s\n", plug->load_name);
+#endif
+               }
+               plug = plug->next;
+       }
+
+       vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (frr_defaults,
+       frr_defaults_cmd,
+       "frr defaults PROFILE...",
+       "FRRouting global parameters\n"
+       "set of configuration defaults used\n"
+       "profile string\n")
+{
+       char *profile = argv_concat(argv, argc, 2);
+       int rv = CMD_SUCCESS;
+
+       if (!frr_defaults_profile_valid(profile)) {
+               vty_out(vty, "%% WARNING: profile %s is not known in this version\n",
+                       profile);
+               rv = CMD_WARNING;
+       }
+       frr_defaults_profile_set(profile);
+       XFREE(MTYPE_TMP, profile);
+       return rv;
+}
+
+DEFUN (frr_version,
+       frr_version_cmd,
+       "frr version VERSION...",
+       "FRRouting global parameters\n"
+       "version configuration was written by\n"
+       "version string\n")
+{
+       char *version = argv_concat(argv, argc, 2);
+
+       frr_defaults_version_set(version);
+       XFREE(MTYPE_TMP, version);
+       return CMD_SUCCESS;
+}
+
+static void defaults_autocomplete(vector comps, struct cmd_token *token)
+{
+       const char **p;
+
+       for (p = frr_defaults_profiles; *p; p++)
+               vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p));
+}
+
+static const struct cmd_variable_handler default_var_handlers[] = {
+       {.tokenname = "PROFILE", .completions = defaults_autocomplete},
+       {.completions = NULL},
+};
+
+void lib_cmd_init(void)
+{
+       cmd_variable_handler_register(default_var_handlers);
+
+       install_element(CONFIG_NODE, &frr_defaults_cmd);
+       install_element(CONFIG_NODE, &frr_version_cmd);
+
+       install_element(VIEW_NODE, &show_memory_cmd);
+       install_element(VIEW_NODE, &show_modules_cmd);
+}
+
+/* Stats querying from users */
+/* Return a pointer to a human friendly string describing
+ * the byte count passed in. E.g:
+ * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
+ * Up to 4 significant figures will be given.
+ * The pointer returned may be NULL (indicating an error)
+ * or point to the given buffer, or point to static storage.
+ */
+const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
+{
+       unsigned int m, k;
+
+       /* easy cases */
+       if (!bytes)
+               return "0 bytes";
+       if (bytes == 1)
+               return "1 byte";
+
+       /*
+        * When we pass the 2gb barrier mallinfo() can no longer report
+        * correct data so it just does something odd...
+        * Reporting like Terrabytes of data.  Which makes users...
+        * edgy.. yes edgy that's the term for it.
+        * So let's just give up gracefully
+        */
+       if (bytes > 0x7fffffff)
+               return "> 2GB";
+
+       m = bytes >> 20;
+       k = bytes >> 10;
+
+       if (m > 10) {
+               if (bytes & (1 << 19))
+                       m++;
+               snprintf(buf, len, "%d MiB", m);
+       } else if (k > 10) {
+               if (bytes & (1 << 9))
+                       k++;
+               snprintf(buf, len, "%d KiB", k);
+       } else
+               snprintf(buf, len, "%ld bytes", bytes);
+
+       return buf;
+}
diff --git a/lib/lib_vty.h b/lib/lib_vty.h
new file mode 100644 (file)
index 0000000..48e409e
--- /dev/null
@@ -0,0 +1,40 @@
+/* Memory management routine
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with 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_LIB_VTY_H
+#define _ZEBRA_LIB_VTY_H
+
+#include "memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lib_cmd_init(void);
+
+/* Human friendly string for given byte count */
+#define MTYPE_MEMSTR_LEN 20
+extern const char *mtype_memstr(char *, size_t, unsigned long);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_LIB_VTY_H */
index 8ef32eaa8a760d277679712d42aaa30e212c86ec..4fb43edff28e3d88b431d173313ad35fab2961d9 100644 (file)
@@ -30,7 +30,7 @@
 #include "vty.h"
 #include "command.h"
 #include "version.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 #include "log_vty.h"
 #include "zclient.h"
 #include "log_int.h"
@@ -43,6 +43,7 @@
 #include "debug.h"
 #include "frrcu.h"
 #include "frr_pthread.h"
+#include "defaults.h"
 
 DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
 DEFINE_KOOH(frr_early_fini, (), ())
@@ -104,6 +105,7 @@ static const struct option lo_always[] = {
        {"version", no_argument, NULL, 'v'},
        {"daemon", no_argument, NULL, 'd'},
        {"module", no_argument, NULL, 'M'},
+       {"profile", required_argument, NULL, 'F'},
        {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
        {"moduledir", required_argument, NULL, OPTION_MODULEDIR},
        {"log", required_argument, NULL, OPTION_LOG},
@@ -112,11 +114,12 @@ static const struct option lo_always[] = {
        {"command-log-always", no_argument, NULL, OPTION_LOGGING},
        {NULL}};
 static const struct optspec os_always = {
-       "hvdM:",
+       "hvdM:F:",
        "  -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"
        "      --vty_socket   Override vty socket path\n"
        "      --moduledir    Override modules directory\n"
        "      --log          Set Logging to stdout, syslog, or file:<name>\n"
@@ -175,7 +178,6 @@ static const struct optspec os_user = {"u:g:",
                                       "  -g, --group        Group to run as\n",
                                       lo_user};
 
-
 bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
                      const char *path)
 {
@@ -390,6 +392,32 @@ static int frr_opt(int opt)
                *modnext = oc;
                modnext = &oc->next;
                break;
+       case 'F':
+               if (!frr_defaults_profile_valid(optarg)) {
+                       const char **p;
+                       FILE *ofd = stderr;
+
+                       if (!strcmp(optarg, "help"))
+                               ofd = stdout;
+                       else
+                               fprintf(stderr,
+                                       "The \"%s\" configuration profile is not valid for this FRR version.\n",
+                                       optarg);
+
+                       fprintf(ofd, "Available profiles are:\n");
+                       for (p = frr_defaults_profiles; *p; p++)
+                               fprintf(ofd, "%s%s\n",
+                                       strcmp(*p, DFLT_NAME) ? "   " : " * ",
+                                       *p);
+
+                       if (ofd == stdout)
+                               exit(0);
+                       fprintf(ofd, "\n");
+                       errors++;
+                       break;
+               }
+               frr_defaults_profile_set(optarg);
+               break;
        case 'i':
                if (di->flags & FRR_NO_CFG_PID_DRY)
                        return 1;
@@ -608,6 +636,7 @@ struct thread_master *frr_init(void)
        dir = di->module_path ? di->module_path : frr_moduledir;
 
        srandom(time(NULL));
+       frr_defaults_apply();
 
        if (di->instance) {
                snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
@@ -679,7 +708,7 @@ struct thread_master *frr_init(void)
                cmd_init(1);
 
        vty_init(master, di->log_always);
-       memory_init();
+       lib_cmd_init();
        log_filter_cmd_init();
 
        frr_pthread_init();
@@ -1077,7 +1106,6 @@ void frr_fini(void)
 
        hook_call(frr_fini);
 
-       /* memory_init -> nothing needed */
        vty_terminate();
        cmd_terminate();
        nb_terminate();
index 0d1efdf3aa1f363147e3ed6e21533bff435d4f45..272e153276025b80dd6d3cb95497c7fb2da02923 100644 (file)
@@ -339,29 +339,6 @@ void list_delete_node(struct list *list, struct listnode *node)
        listnode_free(node);
 }
 
-void list_add_list(struct list *list, struct list *add)
-{
-       struct listnode *n;
-
-       for (n = listhead(add); n; n = listnextnode(n))
-               listnode_add(list, n->data);
-}
-
-struct list *list_dup(struct list *list)
-{
-       struct list *new = list_new();
-       struct listnode *ln;
-       void *data;
-
-       new->cmp = list->cmp;
-       new->del = list->del;
-
-       for (ALL_LIST_ELEMENTS_RO(list, ln, data))
-               listnode_add(new, data);
-
-       return new;
-}
-
 void list_sort(struct list *list, int (*cmp)(const void **, const void **))
 {
        struct listnode *ln, *nn;
index ef914b965f038a8bb75140b8c6920b0f4faf9655..00cb9f871460be3105b5d31700fc96f6e0cbbf59 100644 (file)
@@ -207,17 +207,6 @@ extern struct listnode *listnode_lookup(struct list *list, const void *data);
  */
 extern void *listnode_head(struct list *list);
 
-/*
- * Duplicate a list.
- *
- * list
- *    list to duplicate
- *
- * Returns:
- *    copy of the list
- */
-extern struct list *list_dup(struct list *l);
-
 /*
  * Sort a list in place.
  *
@@ -295,19 +284,6 @@ extern void list_delete_all_node(struct list *list);
  */
 extern void list_delete_node(struct list *list, struct listnode *node);
 
-/*
- * Append a list to an existing list.
- *
- * Runtime is O(N) where N = listcount(add).
- *
- * list
- *    list to append to
- *
- * add
- *    list to append
- */
-extern void list_add_list(struct list *list, struct list *add);
-
 /*
  * Delete all nodes which satisfy a condition from a list.
  * Deletes the node if cond function returns true for the node.
index c77786873601024fec83ed7165a1c057aaae0e99..798b776d0074f479dda58a1f2a4263d403ea21af 100644 (file)
--- a/lib/log.c
+++ b/lib/log.c
@@ -1093,6 +1093,7 @@ static const struct zebra_desc_table command_types[] = {
        DESC_ENTRY(ZEBRA_VXLAN_SG_ADD),
        DESC_ENTRY(ZEBRA_VXLAN_SG_DEL),
        DESC_ENTRY(ZEBRA_VXLAN_SG_REPLAY),
+       DESC_ENTRY(ZEBRA_ERROR),
 };
 #undef DESC_ENTRY
 
index 8de5c4c2bf0b5627d36d55230c766b613c187271..44ea19b5579b17abedcc061f52ff269ed7eaa83a 100644 (file)
@@ -102,45 +102,42 @@ struct memgroup {
        }
 
 #define DECLARE_MTYPE(name)                                                    \
-       extern struct memtype _mt_##name;                                      \
-       extern struct memtype *const MTYPE_##name;                             \
+       extern struct memtype MTYPE_##name[1];                                 \
        /* end */
 
 #define DEFINE_MTYPE_ATTR(group, mname, attr, desc)                            \
-       attr struct memtype _mt_##mname                                        \
-               __attribute__((section(".data.mtypes"))) = {                   \
+       attr struct memtype MTYPE_##mname[1]                                   \
+               __attribute__((section(".data.mtypes"))) = { {                 \
                        .name = desc,                                          \
                        .next = NULL,                                          \
                        .n_alloc = 0,                                          \
                        .size = 0,                                             \
                        .ref = NULL,                                           \
-       };                                                                     \
+       } };                                                                   \
        static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \
        static void _mtinit_##mname(void)                                      \
        {                                                                      \
                if (_mg_##group.insert == NULL)                                \
                        _mg_##group.insert = &_mg_##group.types;               \
-               _mt_##mname.ref = _mg_##group.insert;                          \
-               *_mg_##group.insert = &_mt_##mname;                            \
-               _mg_##group.insert = &_mt_##mname.next;                        \
+               MTYPE_##mname->ref = _mg_##group.insert;                       \
+               *_mg_##group.insert = MTYPE_##mname;                           \
+               _mg_##group.insert = &MTYPE_##mname->next;                      \
        }                                                                      \
        static void _mtfini_##mname(void) __attribute__((_DESTRUCTOR(1001)));  \
        static void _mtfini_##mname(void)                                      \
        {                                                                      \
-               if (_mt_##mname.next)                                          \
-                       _mt_##mname.next->ref = _mt_##mname.ref;               \
-               *_mt_##mname.ref = _mt_##mname.next;                           \
+               if (MTYPE_##mname->next)                                       \
+                       MTYPE_##mname->next->ref = MTYPE_##mname->ref;         \
+               *MTYPE_##mname->ref = MTYPE_##mname->next;                     \
        }                                                                      \
        /* end */
 
 #define DEFINE_MTYPE(group, name, desc)                                        \
        DEFINE_MTYPE_ATTR(group, name, , desc)                                 \
-       struct memtype *const MTYPE_##name = &_mt_##name;                      \
        /* end */
 
 #define DEFINE_MTYPE_STATIC(group, name, desc)                                 \
        DEFINE_MTYPE_ATTR(group, name, static, desc)                           \
-       static struct memtype *const MTYPE_##name = &_mt_##name;               \
        /* end */
 
 DECLARE_MGROUP(LIB)
diff --git a/lib/memory_vty.c b/lib/memory_vty.c
deleted file mode 100644 (file)
index 1adc0d7..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Memory and dynamic module VTY routine
- *
- * Copyright (C) 1998 Kunihiro Ishiguro
- * Copyright (C) 2016-2017  David Lamparter for NetDEF, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public 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 <zebra.h>
-/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#ifdef HAVE_MALLOC_MALLOC_H
-#include <malloc/malloc.h>
-#endif
-#include <dlfcn.h>
-#ifdef HAVE_LINK_H
-#include <link.h>
-#endif
-
-#include "log.h"
-#include "memory.h"
-#include "module.h"
-#include "memory_vty.h"
-
-/* Looking up memory status from vty interface. */
-#include "vector.h"
-#include "vty.h"
-#include "command.h"
-
-#ifdef HAVE_MALLINFO
-static int show_memory_mallinfo(struct vty *vty)
-{
-       struct mallinfo minfo = mallinfo();
-       char buf[MTYPE_MEMSTR_LEN];
-
-       vty_out(vty, "System allocator statistics:\n");
-       vty_out(vty, "  Total heap allocated:  %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
-       vty_out(vty, "  Holding block headers: %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
-       vty_out(vty, "  Used small blocks:     %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
-       vty_out(vty, "  Used ordinary blocks:  %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
-       vty_out(vty, "  Free small blocks:     %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
-       vty_out(vty, "  Free ordinary blocks:  %s\n",
-               mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
-       vty_out(vty, "  Ordinary blocks:       %ld\n",
-               (unsigned long)minfo.ordblks);
-       vty_out(vty, "  Small blocks:          %ld\n",
-               (unsigned long)minfo.smblks);
-       vty_out(vty, "  Holding blocks:        %ld\n",
-               (unsigned long)minfo.hblks);
-       vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
-       return 1;
-}
-#endif /* HAVE_MALLINFO */
-
-static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
-{
-       struct vty *vty = arg;
-       if (!mt) {
-               vty_out(vty, "--- qmem %s ---\n", mg->name);
-               vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
-                       "Type", "Current#", "  Size",
-#ifdef HAVE_MALLOC_USABLE_SIZE
-                       "     Total",
-#else
-                       "",
-#endif
-                       "Max#",
-#ifdef HAVE_MALLOC_USABLE_SIZE
-                       "MaxBytes"
-#else
-                       ""
-#endif
-                       );
-       } else {
-               if (mt->n_alloc != 0) {
-                       char size[32];
-                       snprintf(size, sizeof(size), "%6zu", mt->size);
-#ifdef HAVE_MALLOC_USABLE_SIZE
-#define TSTR " %9zu"
-#define TARG , mt->total
-#define TARG2 , mt->max_size
-#else
-#define TSTR ""
-#define TARG
-#define TARG2
-#endif
-                       vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
-                               mt->name,
-                               mt->n_alloc,
-                               mt->size == 0 ? ""
-                                             : mt->size == SIZE_VAR
-                                                       ? "variable"
-                                                       : size
-                               TARG,
-                               mt->n_max
-                               TARG2);
-               }
-       }
-       return 0;
-}
-
-
-DEFUN (show_memory,
-       show_memory_cmd,
-       "show memory",
-       "Show running system information\n"
-       "Memory statistics\n")
-{
-#ifdef HAVE_MALLINFO
-       show_memory_mallinfo(vty);
-#endif /* HAVE_MALLINFO */
-
-       qmem_walk(qmem_walker, vty);
-       return CMD_SUCCESS;
-}
-
-DEFUN (show_modules,
-       show_modules_cmd,
-       "show modules",
-       "Show running system information\n"
-       "Loaded modules\n")
-{
-       struct frrmod_runtime *plug = frrmod_list;
-
-       vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
-               "Description");
-       while (plug) {
-               const struct frrmod_info *i = plug->info;
-
-               vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
-                       i->description);
-               if (plug->dl_handle) {
-#ifdef HAVE_DLINFO_ORIGIN
-                       char origin[MAXPATHLEN] = "";
-                       dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
-#ifdef HAVE_DLINFO_LINKMAP
-                       const char *name;
-                       struct link_map *lm = NULL;
-                       dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
-                       if (lm) {
-                               name = strrchr(lm->l_name, '/');
-                               name = name ? name + 1 : lm->l_name;
-                               vty_out(vty, "\tfrom: %s/%s\n", origin, name);
-                       }
-#else
-                       vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
-#endif
-#else
-                       vty_out(vty, "\tfrom: %s\n", plug->load_name);
-#endif
-               }
-               plug = plug->next;
-       }
-
-       vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
-
-       return CMD_SUCCESS;
-}
-
-void memory_init(void)
-{
-       install_element(VIEW_NODE, &show_memory_cmd);
-       install_element(VIEW_NODE, &show_modules_cmd);
-}
-
-/* Stats querying from users */
-/* Return a pointer to a human friendly string describing
- * the byte count passed in. E.g:
- * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
- * Up to 4 significant figures will be given.
- * The pointer returned may be NULL (indicating an error)
- * or point to the given buffer, or point to static storage.
- */
-const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
-{
-       unsigned int m, k;
-
-       /* easy cases */
-       if (!bytes)
-               return "0 bytes";
-       if (bytes == 1)
-               return "1 byte";
-
-       /*
-        * When we pass the 2gb barrier mallinfo() can no longer report
-        * correct data so it just does something odd...
-        * Reporting like Terrabytes of data.  Which makes users...
-        * edgy.. yes edgy that's the term for it.
-        * So let's just give up gracefully
-        */
-       if (bytes > 0x7fffffff)
-               return "> 2GB";
-
-       m = bytes >> 20;
-       k = bytes >> 10;
-
-       if (m > 10) {
-               if (bytes & (1 << 19))
-                       m++;
-               snprintf(buf, len, "%d MiB", m);
-       } else if (k > 10) {
-               if (bytes & (1 << 9))
-                       k++;
-               snprintf(buf, len, "%d KiB", k);
-       } else
-               snprintf(buf, len, "%ld bytes", bytes);
-
-       return buf;
-}
diff --git a/lib/memory_vty.h b/lib/memory_vty.h
deleted file mode 100644 (file)
index 941255b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Memory management routine
- * Copyright (C) 1998 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with 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_MEMORY_VTY_H
-#define _ZEBRA_MEMORY_VTY_H
-
-#include "memory.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void memory_init(void);
-
-/* Human friendly string for given byte count */
-#define MTYPE_MEMSTR_LEN 20
-extern const char *mtype_memstr(char *, size_t, unsigned long);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_MEMORY_VTY_H */
index 55c66fdc3df2374108b317cba4d04195c1fd4920..d1a31ae35fb5fd8317ca5e1b1f97eed553008093 100644 (file)
@@ -51,7 +51,7 @@ static struct ns *ns_lookup_name_internal(const char *name);
 
 RB_GENERATE(ns_head, ns, entry, ns_compare)
 
-struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
+static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
 
 static struct ns *default_ns;
 static int ns_current_ns_fd;
@@ -74,7 +74,8 @@ static inline int ns_map_compare(const struct ns_map_nsid *a,
 RB_HEAD(ns_map_nsid_head, ns_map_nsid);
 RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
 RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
-struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list);
+static struct ns_map_nsid_head ns_map_nsid_list =
+               RB_INITIALIZER(&ns_map_nsid_list);
 
 static ns_id_t ns_id_external_numbering;
 
@@ -123,7 +124,7 @@ static int have_netns(void)
 }
 
 /* Holding NS hooks  */
-struct ns_master {
+static struct ns_master {
        int (*ns_new_hook)(struct ns *ns);
        int (*ns_delete_hook)(struct ns *ns);
        int (*ns_enable_hook)(struct ns *ns);
index b0aae4f8df082dd18652478964a226738e1fa9ff..740d2b621ed7d3e0bb2357efb6304fb23a28d8f7 100644 (file)
@@ -34,7 +34,7 @@ static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
 
 RB_GENERATE(ns_head, ns, entry, ns_compare)
 
-struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
+static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
 
 static inline int ns_compare(const struct ns *a, const struct ns *b)
 {
index d05fa8e6d781a05238821deb7848b5a525666ef8..e23f8b0792b032e67ade5b290e5bfb1f98d47b5e 100644 (file)
@@ -33,6 +33,8 @@
 #include "mpls.h"
 #include "jhash.h"
 #include "printfrr.h"
+#include "vrf.h"
+#include "nexthop_group.h"
 
 DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
 DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
@@ -117,6 +119,12 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
        if (next1->type > next2->type)
                return 1;
 
+       if (next1->weight < next2->weight)
+               return -1;
+
+       if (next1->weight > next2->weight)
+               return 1;
+
        switch (next1->type) {
        case NEXTHOP_TYPE_IPV4:
        case NEXTHOP_TYPE_IPV6:
@@ -223,7 +231,23 @@ bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
 
 struct nexthop *nexthop_new(void)
 {
-       return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
+       struct nexthop *nh;
+
+       nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
+
+       /*
+        * Default the weight to 1 here for all nexthops.
+        * The linux kernel does some weird stuff with adding +1 to
+        * all nexthop weights it gets over netlink.
+        * To handle this, just default everything to 1 right from
+        * from the beggining so we don't have to special case
+        * default weights in the linux netlink code.
+        *
+        * 1 should be a valid on all platforms anyway.
+        */
+       nh->weight = 1;
+
+       return nh;
 }
 
 /* Free nexthop. */
@@ -281,6 +305,93 @@ bool nexthop_same_no_labels(const struct nexthop *nh1,
        return true;
 }
 
+/*
+ * Allocate a new nexthop object and initialize it from various args.
+ */
+struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->type = NEXTHOP_TYPE_IFINDEX;
+       nexthop->ifindex = ifindex;
+       nexthop->vrf_id = vrf_id;
+
+       return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
+                                 const struct in_addr *src,
+                                 vrf_id_t vrf_id)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->type = NEXTHOP_TYPE_IPV4;
+       nexthop->vrf_id = vrf_id;
+       nexthop->gate.ipv4 = *ipv4;
+       if (src)
+               nexthop->src.ipv4 = *src;
+
+       return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
+                                         const struct in_addr *src,
+                                         ifindex_t ifindex, vrf_id_t vrf_id)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+       nexthop->vrf_id = vrf_id;
+       nexthop->gate.ipv4 = *ipv4;
+       if (src)
+               nexthop->src.ipv4 = *src;
+       nexthop->ifindex = ifindex;
+
+       return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
+                                 vrf_id_t vrf_id)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->vrf_id = vrf_id;
+       nexthop->type = NEXTHOP_TYPE_IPV6;
+       nexthop->gate.ipv6 = *ipv6;
+
+       return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
+                                         ifindex_t ifindex, vrf_id_t vrf_id)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->vrf_id = vrf_id;
+       nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+       nexthop->gate.ipv6 = *ipv6;
+       nexthop->ifindex = ifindex;
+
+       return nexthop;
+}
+
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
+{
+       struct nexthop *nexthop;
+
+       nexthop = nexthop_new();
+       nexthop->vrf_id = VRF_DEFAULT;
+       nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
+       nexthop->bh_type = bh_type;
+
+       return nexthop;
+}
+
 /* Update nexthop with label information. */
 void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
                        uint8_t num_labels, mpls_label_t *label)
@@ -288,6 +399,9 @@ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
        struct mpls_label_stack *nh_label;
        int i;
 
+       if (num_labels == 0)
+               return;
+
        nexthop->nh_label_type = type;
        nh_label = XCALLOC(MTYPE_NH_LABEL,
                           sizeof(struct mpls_label_stack)
@@ -452,13 +566,15 @@ uint32_t nexthop_hash(const struct nexthop *nexthop)
        return key;
 }
 
-void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
-                 struct nexthop *rparent)
+void nexthop_copy_no_recurse(struct nexthop *copy,
+                            const struct nexthop *nexthop,
+                            struct nexthop *rparent)
 {
        copy->vrf_id = nexthop->vrf_id;
        copy->ifindex = nexthop->ifindex;
        copy->type = nexthop->type;
        copy->flags = nexthop->flags;
+       copy->weight = nexthop->weight;
        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));
@@ -469,6 +585,28 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
                                   &nexthop->nh_label->label[0]);
 }
 
+void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
+                 struct nexthop *rparent)
+{
+       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
+        * use a nexthop_group API.
+        */
+       if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
+               copy_nexthops(&copy->resolved, nexthop->resolved, copy);
+}
+
+struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
+                                      struct nexthop *rparent)
+{
+       struct nexthop *new = nexthop_new();
+
+       nexthop_copy_no_recurse(new, nexthop, rparent);
+       return new;
+}
+
 struct nexthop *nexthop_dup(const struct nexthop *nexthop,
                            struct nexthop *rparent)
 {
index fe029f1867932345182852d4d006cf7fc4c61cc4..cb5efe00ccbc7c50be2ab02dadbd7b4ab74d2945 100644 (file)
@@ -110,6 +110,9 @@ struct nexthop {
 
        /* Label(s) associated with this nexthop. */
        struct mpls_label_stack *nh_label;
+
+       /* Weight of the nexthop ( for unequal cost ECMP ) */
+       uint8_t weight;
 };
 
 struct nexthop *nexthop_new(void);
@@ -121,6 +124,22 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t,
                        mpls_label_t *);
 void nexthop_del_labels(struct nexthop *);
 
+/*
+ * Allocate a new nexthop object and initialize it from various args.
+ */
+struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
+                                 const struct in_addr *src,
+                                 vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
+                                         const struct in_addr *src,
+                                         ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
+                                 vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
+                                         ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type);
+
 /*
  * Hash a nexthop. Suitable for use with hash tables.
  *
@@ -168,9 +187,16 @@ extern unsigned int nexthop_level(struct nexthop *nexthop);
 /* Copies to an already allocated nexthop struct */
 extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
                         struct nexthop *rparent);
+/* Copies to an already allocated nexthop struct, not including recurse info */
+extern void nexthop_copy_no_recurse(struct nexthop *copy,
+                                   const struct nexthop *nexthop,
+                                   struct nexthop *rparent);
 /* Duplicates a nexthop and returns the newly allocated nexthop */
 extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
                                   struct nexthop *rparent);
+/* Duplicates a nexthop and returns the newly allocated nexthop */
+extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
+                                             struct nexthop *rparent);
 
 #ifdef __cplusplus
 }
index 9552f895683bb14199108c8c929ee669b09b71c3..3005a51c716987196148c485a73c28ddf076f1f8 100644 (file)
 
 DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group")
 
+/*
+ * Internal struct used to hold nhg config strings
+ */
+struct nexthop_hold {
+       char *nhvrf_name;
+       union sockunion *addr;
+       char *intf;
+       char *labels;
+       uint32_t weight;
+};
+
 struct nexthop_group_hooks {
        void (*new)(const char *name);
        void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
@@ -233,25 +244,15 @@ void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
        nexthop->prev = last;
 }
 
-void _nexthop_group_add_sorted(struct nexthop_group *nhg,
-                              struct nexthop *nexthop)
+/* Add nexthop to sorted list of nexthops */
+static void _nexthop_add_sorted(struct nexthop **head,
+                               struct nexthop *nexthop)
 {
-       struct nexthop *position, *prev, *tail;
-
-       /* Try to just append to the end first
-        * This trust it is already sorted
-        */
-
-       tail = nexthop_group_tail(nhg);
-
-       if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
-               tail->next = nexthop;
-               nexthop->prev = tail;
+       struct nexthop *position, *prev;
 
-               return;
-       }
+       assert(!nexthop->next);
 
-       for (position = nhg->nexthop, prev = NULL; position;
+       for (position = *head, prev = NULL; position;
             prev = position, position = position->next) {
                if (nexthop_cmp(position, nexthop) > 0) {
                        nexthop->next = position;
@@ -260,7 +261,7 @@ void _nexthop_group_add_sorted(struct nexthop_group *nhg,
                        if (nexthop->prev)
                                nexthop->prev->next = nexthop;
                        else
-                               nhg->nexthop = nexthop;
+                               *head = nexthop;
 
                        position->prev = nexthop;
                        return;
@@ -271,7 +272,29 @@ void _nexthop_group_add_sorted(struct nexthop_group *nhg,
        if (prev)
                prev->next = nexthop;
        else
-               nhg->nexthop = nexthop;
+               *head = nexthop;
+}
+
+void nexthop_group_add_sorted(struct nexthop_group *nhg,
+                             struct nexthop *nexthop)
+{
+       struct nexthop *tail;
+
+       assert(!nexthop->next);
+
+       /* Try to just append to the end first;
+        * trust the list is already sorted
+        */
+       tail = nexthop_group_tail(nhg);
+
+       if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
+               tail->next = nexthop;
+               nexthop->prev = tail;
+
+               return;
+       }
+
+       _nexthop_add_sorted(&nhg->nexthop, nexthop);
 }
 
 /* Delete nexthop from a nexthop list.  */
@@ -298,6 +321,40 @@ void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
        nh->next = NULL;
 }
 
+/*
+ * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
+ */
+void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
+                                 const struct nexthop *nh)
+{
+       struct nexthop *nexthop, *tail;
+       const struct nexthop *nh1;
+
+       /* We'll try to append to the end of the new list;
+        * if the original list in nh is already sorted, this eliminates
+        * lots of comparison operations.
+        */
+       tail = nexthop_group_tail(nhg);
+
+       for (nh1 = nh; nh1; nh1 = nh1->next) {
+               nexthop = nexthop_dup(nh1, NULL);
+
+               if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
+                       tail->next = nexthop;
+                       nexthop->prev = tail;
+
+                       tail = nexthop;
+                       continue;
+               }
+
+               _nexthop_add_sorted(&nhg->nexthop, nexthop);
+
+               if (tail == NULL)
+                       tail = nexthop;
+       }
+}
+
+/* Copy a list of nexthops, no effort made to sort or order them. */
 void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
                   struct nexthop *rparent)
 {
@@ -307,10 +364,6 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
        for (nh1 = nh; nh1; nh1 = nh1->next) {
                nexthop = nexthop_dup(nh1, rparent);
                _nexthop_add(tnh, nexthop);
-
-               if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
-                       copy_nexthops(&nexthop->resolved, nh1->resolved,
-                                     nexthop);
        }
 }
 
@@ -425,7 +478,11 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
        if (ret)
                return ret;
 
-       return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+       ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+       if (ret)
+               return ret;
+
+       return nhgc_cmp_helper(nh1->labels, nh2->labels);
 }
 
 static void nhgl_delete(struct nexthop_hold *nh)
@@ -437,6 +494,8 @@ static void nhgl_delete(struct nexthop_hold *nh)
        if (nh->addr)
                sockunion_free(nh->addr);
 
+       XFREE(MTYPE_TMP, nh->labels);
+
        XFREE(MTYPE_TMP, nh);
 }
 
@@ -510,7 +569,8 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
 static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
                                    const char *nhvrf_name,
                                    const union sockunion *addr,
-                                   const char *intf)
+                                   const char *intf, const char *labels,
+                                   const uint32_t weight)
 {
        struct nexthop_hold *nh;
 
@@ -522,6 +582,10 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
                nh->intf = XSTRDUP(MTYPE_TMP, intf);
        if (addr)
                nh->addr = sockunion_dup(addr);
+       if (labels)
+               nh->labels = XSTRDUP(MTYPE_TMP, labels);
+
+       nh->weight = weight;
 
        listnode_add_sort(nhgc->nhg_list, nh);
 }
@@ -529,15 +593,18 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
 static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
                                      const char *nhvrf_name,
                                      const union sockunion *addr,
-                                     const char *intf)
+                                     const char *intf, const char *labels,
+                                     const uint32_t weight)
 {
        struct nexthop_hold *nh;
        struct listnode *node;
 
        for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
-               if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
-                   nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
-                   nhgc_cmp_helper(intf, nh->intf) == 0)
+               if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
+                   && nhgc_addr_cmp_helper(addr, nh->addr) == 0
+                   && nhgc_cmp_helper(intf, nh->intf) == 0
+                   && nhgc_cmp_helper(labels, nh->labels) == 0
+                   && weight == nh->weight)
                        break;
        }
 
@@ -551,10 +618,19 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
        nhgl_delete(nh);
 }
 
+/*
+ * Parse the config strings we support for a single nexthop. This gets used
+ * in a couple of different ways, and we distinguish between transient
+ * failures - such as a still-unprocessed interface - and fatal errors
+ * from label-string parsing.
+ */
 static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
                                        const union sockunion *addr,
-                                       const char *intf, const char *name)
+                                       const char *intf, const char *name,
+                                       const char *labels, int *lbl_ret,
+                                       uint32_t weight)
 {
+       int ret = 0;
        struct vrf *vrf;
 
        memset(nhop, 0, sizeof(*nhop));
@@ -592,16 +668,50 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
        } else
                nhop->type = NEXTHOP_TYPE_IFINDEX;
 
+       if (labels) {
+               uint8_t num = 0;
+               mpls_label_t larray[MPLS_MAX_LABELS];
+
+               ret = mpls_str2label(labels, &num, larray);
+
+               /* Return label parse result */
+               if (lbl_ret)
+                       *lbl_ret = ret;
+
+               if (ret < 0)
+                       return false;
+               else if (num > 0)
+                       nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
+                                          num, larray);
+       }
+
+       nhop->weight = weight;
+
        return true;
 }
 
+/*
+ * Wrapper to parse the strings in a 'nexthop_hold'
+ */
+static bool nexthop_group_parse_nhh(struct nexthop *nhop,
+                                   const struct nexthop_hold *nhh)
+{
+       return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
+                                           nhh->nhvrf_name, nhh->labels, NULL,
+                                           nhh->weight));
+}
+
 DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
       "[no] nexthop\
         <\
          <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
          |INTERFACE$intf\
        >\
-       [nexthop-vrf NAME$vrf_name]",
+       [{ \
+          nexthop-vrf NAME$vrf_name \
+          |label WORD \
+           |weight (1-255) \
+       }]",
       NO_STR
       "Specify one of the nexthops in this ECMP group\n"
       "v4 Address\n"
@@ -609,14 +719,20 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
       "Interface to use\n"
       "Interface to use\n"
       "If the nexthop is in a different vrf tell us\n"
-      "The nexthop-vrf Name\n")
+      "The nexthop-vrf Name\n"
+      "Specify label(s) for this nexthop\n"
+      "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")
 {
        VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
        struct nexthop nhop;
        struct nexthop *nh;
+       int lbl_ret = 0;
        bool legal;
 
-       legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name);
+       legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
+                                           &lbl_ret, weight);
 
        if (nhop.type == NEXTHOP_TYPE_IPV6
            && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
@@ -625,10 +741,32 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
+       /* Handle label-string errors */
+       if (!legal && lbl_ret < 0) {
+               switch (lbl_ret) {
+               case -1:
+                       vty_out(vty, "%% Malformed label(s)\n");
+                       break;
+               case -2:
+                       vty_out(vty,
+                               "%% Cannot use reserved label(s) (%d-%d)\n",
+                               MPLS_LABEL_RESERVED_MIN,
+                               MPLS_LABEL_RESERVED_MAX);
+                       break;
+               case -3:
+                       vty_out(vty,
+                               "%% Too many labels. Enter %d or fewer\n",
+                               MPLS_MAX_LABELS);
+                       break;
+               }
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        nh = nexthop_exists(&nhgc->nhg, &nhop);
 
        if (no) {
-               nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
+               nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label,
+                                         weight);
                if (nh) {
                        _nexthop_del(&nhgc->nhg, nh);
 
@@ -646,7 +784,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
                        _nexthop_add(&nhgc->nhg.nexthop, nh);
                }
 
-               nexthop_group_save_nhop(nhgc, vrf_name, addr, intf);
+               nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
+                                       weight);
 
                if (legal && nhg_hooks.add_nexthop)
                        nhg_hooks.add_nexthop(nhgc, nh);
@@ -655,7 +794,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
        return CMD_SUCCESS;
 }
 
-struct cmd_node nexthop_group_node = {
+static struct cmd_node nexthop_group_node = {
        NH_GROUP_NODE,
        "%s(config-nh-group)# ",
        1
@@ -696,6 +835,19 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
                vrf = vrf_lookup_by_id(nh->vrf_id);
                vty_out(vty, " nexthop-vrf %s", vrf->name);
        }
+
+       if (nh->nh_label && nh->nh_label->num_labels > 0) {
+               char buf[200];
+
+               mpls_label2str(nh->nh_label->num_labels,
+                              nh->nh_label->label,
+                              buf, sizeof(buf), 0);
+               vty_out(vty, " label %s", buf);
+       }
+
+       if (nh->weight)
+               vty_out(vty, " weight %u", nh->weight);
+
        vty_out(vty, "\n");
 }
 
@@ -715,6 +867,12 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
        if (nh->nhvrf_name)
                vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
 
+       if (nh->labels)
+               vty_out(vty, " label %s", nh->labels);
+
+       if (nh->weight)
+               vty_out(vty, " weight %u", nh->weight);
+
        vty_out(vty, "\n");
 }
 
@@ -751,9 +909,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf)
                        struct nexthop nhop;
                        struct nexthop *nh;
 
-                       if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
-                                                        nhh->intf,
-                                                        nhh->nhvrf_name))
+                       if (!nexthop_group_parse_nhh(&nhop, nhh))
                                continue;
 
                        nh = nexthop_exists(&nhgc->nhg, &nhop);
@@ -787,9 +943,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf)
                        struct nexthop nhop;
                        struct nexthop *nh;
 
-                       if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
-                                                        nhh->intf,
-                                                        nhh->nhvrf_name))
+                       if (!nexthop_group_parse_nhh(&nhop, nhh))
                                continue;
 
                        nh = nexthop_exists(&nhgc->nhg, &nhop);
@@ -824,9 +978,7 @@ void nexthop_group_interface_state_change(struct interface *ifp,
                        for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
                                struct nexthop nhop;
 
-                               if (!nexthop_group_parse_nexthop(
-                                           &nhop, nhh->addr, nhh->intf,
-                                           nhh->nhvrf_name))
+                               if (!nexthop_group_parse_nhh(&nhop, nhh))
                                        continue;
 
                                switch (nhop.type) {
index 391775c69c3945bdbe7406c801e129814e8e7153..73b020283a98f1a4fb72f519ed3866e116683f81 100644 (file)
@@ -44,12 +44,21 @@ void nexthop_group_delete(struct nexthop_group **nhg);
 
 void nexthop_group_copy(struct nexthop_group *to,
                        struct nexthop_group *from);
+
+/*
+ * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
+ */
+void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
+                                 const struct nexthop *nh);
+
 void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
                   struct nexthop *rparent);
 
 uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg);
 uint32_t nexthop_group_hash(const struct nexthop_group *nhg);
 void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
+void nexthop_group_add_sorted(struct nexthop_group *nhg,
+                             struct nexthop *nexthop);
 
 /* The following for loop allows to iterate over the nexthop
  * structure of routes.
@@ -70,12 +79,6 @@ void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
        (nhop) = nexthop_next(nhop)
 
 
-struct nexthop_hold {
-       char *nhvrf_name;
-       union sockunion *addr;
-       char *intf;
-};
-
 struct nexthop_group_cmd {
 
        RB_ENTRY(nexthop_group_cmd) nhgc_entry;
index cdd0df0ab39e8c431cc161f0fe4003bec9bea1fe..4abda624ae717d0de83603a88b2b82a951f6b8bc 100644 (file)
@@ -35,8 +35,6 @@ extern "C" {
 
 void _nexthop_add(struct nexthop **target, struct nexthop *nexthop);
 void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop);
-void _nexthop_group_add_sorted(struct nexthop_group *nhg,
-                              struct nexthop *nexthop);
 
 #ifdef __cplusplus
 }
index 009e5bd8242515f3dcb8749e4ab79cf56b517fd2..17dc25628105500dc19eeee4d0bb7c285e33e946 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "libfrr.h"
 #include "version.h"
+#include "defaults.h"
 #include "log.h"
 #include "lib_errors.h"
 #include "command.h"
@@ -486,7 +487,7 @@ static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config,
        vty_out(vty, "Configuration:\n");
        vty_out(vty, "!\n");
        vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
-       vty_out(vty, "frr defaults %s\n", DFLT_NAME);
+       vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
 
        LY_TREE_FOR (config->dnode, root)
                nb_cli_show_dnode_cmds(vty, root, with_defaults);
index 1963b8a35968750efc194319b1614d132fc66ca7..20e0a38e3b3e0d84586188171e7a8c1640199d79 100644 (file)
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -71,8 +71,6 @@ struct ns {
 RB_HEAD(ns_head, ns);
 RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
 
-extern struct ns_head ns_tree;
-
 /*
  * API for managing NETNS. eg from zebra daemon
  * one want to manage the list of NETNS, etc...
index 64571a05b7ab383ca585ce3fc48d4cb4d5ebc220..a0976cd6bdd976ed29da62c8c72a0461795d07c0 100644 (file)
@@ -1891,6 +1891,8 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp,
        if (!plist)
                return CMD_WARNING_CONFIG_FAILED;
 
+       apply_mask(&orfp->p);
+
        if (set) {
                pentry = prefix_list_entry_make(
                        &orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY),
index e2bf3b949ceae77868eeca5bde085174c7072925..219f798dcc947f2e713770070a0812e0d3a33a41 100644 (file)
@@ -37,394 +37,6 @@ DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec")
 static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
                                  0xf8, 0xfc, 0xfe, 0xff};
 
-static const struct in6_addr maskbytes6[] = {
-       /* /0   */ {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /1   */
-       {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /2   */
-       {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /3   */
-       {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /4   */
-       {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /5   */
-       {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /6   */
-       {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /7   */
-       {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /8   */
-       {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /9   */
-       {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /10  */
-       {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /11  */
-       {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /12  */
-       {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /13  */
-       {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /14  */
-       {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /15  */
-       {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /16  */
-       {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /17  */
-       {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /18  */
-       {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /19  */
-       {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /20  */
-       {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /21  */
-       {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /22  */
-       {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /23  */
-       {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /24  */
-       {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /25  */
-       {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /26  */
-       {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /27  */
-       {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /28  */
-       {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /29  */
-       {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /30  */
-       {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /31  */
-       {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /32  */
-       {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /33  */
-       {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /34  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /35  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /36  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /37  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /38  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /39  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /40  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /41  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /42  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /43  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /44  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /45  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /46  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /47  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /48  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /49  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /50  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /51  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /52  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /53  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /54  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /55  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /56  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /57  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /58  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /59  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /60  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /61  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /62  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /63  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /64  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /65  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /66  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /67  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /68  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /69  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /70  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /71  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /72  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /73  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /74  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /75  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /76  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /77  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /78  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /79  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /80  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /81  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /82  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /83  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /84  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /85  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /86  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /87  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /88  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0x00, 0x00, 0x00, 0x00, 0x00}}},
-       /* /89  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0x80, 0x00, 0x00, 0x00, 0x00}}},
-       /* /90  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xc0, 0x00, 0x00, 0x00, 0x00}}},
-       /* /91  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xe0, 0x00, 0x00, 0x00, 0x00}}},
-       /* /92  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xf0, 0x00, 0x00, 0x00, 0x00}}},
-       /* /93  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xf8, 0x00, 0x00, 0x00, 0x00}}},
-       /* /94  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xfc, 0x00, 0x00, 0x00, 0x00}}},
-       /* /95  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xfe, 0x00, 0x00, 0x00, 0x00}}},
-       /* /96  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0x00, 0x00, 0x00, 0x00}}},
-       /* /97  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0x80, 0x00, 0x00, 0x00}}},
-       /* /98  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xc0, 0x00, 0x00, 0x00}}},
-       /* /99  */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xe0, 0x00, 0x00, 0x00}}},
-       /* /100 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xf0, 0x00, 0x00, 0x00}}},
-       /* /101 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xf8, 0x00, 0x00, 0x00}}},
-       /* /102 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xfc, 0x00, 0x00, 0x00}}},
-       /* /103 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xfe, 0x00, 0x00, 0x00}}},
-       /* /104 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0x00, 0x00, 0x00}}},
-       /* /105 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0x80, 0x00, 0x00}}},
-       /* /106 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xc0, 0x00, 0x00}}},
-       /* /107 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xe0, 0x00, 0x00}}},
-       /* /108 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xf0, 0x00, 0x00}}},
-       /* /109 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xf8, 0x00, 0x00}}},
-       /* /110 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xfc, 0x00, 0x00}}},
-       /* /111 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xfe, 0x00, 0x00}}},
-       /* /112 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0x00, 0x00}}},
-       /* /113 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0x80, 0x00}}},
-       /* /114 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xc0, 0x00}}},
-       /* /115 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xe0, 0x00}}},
-       /* /116 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xf0, 0x00}}},
-       /* /117 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xf8, 0x00}}},
-       /* /118 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xfc, 0x00}}},
-       /* /119 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xfe, 0x00}}},
-       /* /120 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0x00}}},
-       /* /121 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0x80}}},
-       /* /122 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xc0}}},
-       /* /123 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xe0}}},
-       /* /124 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xf0}}},
-       /* /125 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xf8}}},
-       /* /126 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xfc}}},
-       /* /127 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xfe}}},
-       /* /128 */
-       {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-          0xff, 0xff, 0xff, 0xff, 0xff}}}};
-
 /* Number of bits in prefix type. */
 #ifndef PNBBY
 #define PNBBY 8
@@ -432,15 +44,6 @@ static const struct in6_addr maskbytes6[] = {
 
 #define MASKBIT(offset)  ((0xff << (PNBBY - (offset))) & 0xff)
 
-void prefix_hexdump(const struct prefix *p)
-{
-       char buf[PREFIX_STRLEN];
-
-       zlog_debug("prefix: %s",
-                  prefix2str(p, buf, sizeof(buf)));
-       zlog_hexdump(p, sizeof(struct prefix));
-}
-
 int is_zero_mac(const struct ethaddr *mac)
 {
        int i = 0;
@@ -461,11 +64,6 @@ unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen)
        return (prefix[offset] >> shift) & 1;
 }
 
-unsigned int prefix6_bit(const struct in6_addr *prefix, const uint16_t prefixlen)
-{
-       return prefix_bit((const uint8_t *)&prefix->s6_addr, prefixlen);
-}
-
 int str2family(const char *string)
 {
        if (!strcmp("ipv4", string))
@@ -1121,31 +719,46 @@ int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p)
  * FIXME return uint8_t as ip_maskleni() does. */
 int ip6_masklen(struct in6_addr netmask)
 {
-       int len = 0;
-       unsigned char val;
-       unsigned char *pnt;
-
-       pnt = (unsigned char *)&netmask;
-
-       while ((*pnt == 0xff) && len < IPV6_MAX_BITLEN) {
-               len += 8;
-               pnt++;
-       }
-
-       if (len < IPV6_MAX_BITLEN) {
-               val = *pnt;
-               while (val) {
-                       len++;
-                       val <<= 1;
-               }
-       }
-       return len;
+       if (netmask.s6_addr32[0] != 0xffffffffU)
+               return __builtin_clz(~ntohl(netmask.s6_addr32[0]));
+       if (netmask.s6_addr32[1] != 0xffffffffU)
+               return __builtin_clz(~ntohl(netmask.s6_addr32[1])) + 32;
+       if (netmask.s6_addr32[2] != 0xffffffffU)
+               return __builtin_clz(~ntohl(netmask.s6_addr32[2])) + 64;
+       if (netmask.s6_addr32[3] != 0xffffffffU)
+               return __builtin_clz(~ntohl(netmask.s6_addr32[3])) + 96;
+       /* note __builtin_clz(0) is undefined */
+       return 128;
 }
 
 void masklen2ip6(const int masklen, struct in6_addr *netmask)
 {
        assert(masklen >= 0 && masklen <= IPV6_MAX_BITLEN);
-       memcpy(netmask, maskbytes6 + masklen, sizeof(struct in6_addr));
+
+       if (masklen == 0) {
+               /* note << 32 is undefined */
+               memset(netmask, 0, sizeof(*netmask));
+       } else if (masklen <= 32) {
+               netmask->s6_addr32[0] = htonl(0xffffffffU << (32 - masklen));
+               netmask->s6_addr32[1] = 0;
+               netmask->s6_addr32[2] = 0;
+               netmask->s6_addr32[3] = 0;
+       } else if (masklen <= 64) {
+               netmask->s6_addr32[0] = 0xffffffffU;
+               netmask->s6_addr32[1] = htonl(0xffffffffU << (64 - masklen));
+               netmask->s6_addr32[2] = 0;
+               netmask->s6_addr32[3] = 0;
+       } else if (masklen <= 96) {
+               netmask->s6_addr32[0] = 0xffffffffU;
+               netmask->s6_addr32[1] = 0xffffffffU;
+               netmask->s6_addr32[2] = htonl(0xffffffffU << (96 - masklen));
+               netmask->s6_addr32[3] = 0;
+       } else {
+               netmask->s6_addr32[0] = 0xffffffffU;
+               netmask->s6_addr32[1] = 0xffffffffU;
+               netmask->s6_addr32[2] = 0xffffffffU;
+               netmask->s6_addr32[3] = htonl(0xffffffffU << (128 - masklen));
+       }
 }
 
 void apply_mask_ipv6(struct prefix_ipv6 *p)
@@ -1183,33 +796,6 @@ void apply_mask(struct prefix *p)
        return;
 }
 
-/* Utility function of convert between struct prefix <=> union sockunion.
- * FIXME This function isn't used anywhere. */
-struct prefix *sockunion2prefix(const union sockunion *dest,
-                               const union sockunion *mask)
-{
-       if (dest->sa.sa_family == AF_INET) {
-               struct prefix_ipv4 *p;
-
-               p = prefix_ipv4_new();
-               p->family = AF_INET;
-               p->prefix = dest->sin.sin_addr;
-               p->prefixlen = ip_masklen(mask->sin.sin_addr);
-               return (struct prefix *)p;
-       }
-       if (dest->sa.sa_family == AF_INET6) {
-               struct prefix_ipv6 *p;
-
-               p = prefix_ipv6_new();
-               p->family = AF_INET6;
-               p->prefixlen = ip6_masklen(mask->sin6.sin6_addr);
-               memcpy(&p->prefix, &dest->sin6.sin6_addr,
-                      sizeof(struct in6_addr));
-               return (struct prefix *)p;
-       }
-       return NULL;
-}
-
 /* Utility function of convert between struct prefix <=> union sockunion. */
 struct prefix *sockunion2hostprefix(const union sockunion *su,
                                    struct prefix *prefix)
@@ -1521,14 +1107,6 @@ void apply_classful_mask_ipv4(struct prefix_ipv4 *p)
        }
 }
 
-in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen)
-{
-       struct in_addr mask;
-
-       masklen2ip(masklen, &mask);
-       return hostaddr & mask.s_addr;
-}
-
 in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen)
 {
        struct in_addr mask;
index 7a93c766a329da16407d60c131dce136f3ea62eb..667627ddfe804aa10e8b577ffdf622ada2942a89 100644 (file)
@@ -392,8 +392,6 @@ extern const char *afi2str(afi_t afi);
 
 /* Check bit of the prefix. */
 extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen);
-extern unsigned int prefix6_bit(const struct in6_addr *prefix,
-                               const uint16_t prefixlen);
 
 extern struct prefix *prefix_new(void);
 extern void prefix_free(struct prefix **p);
@@ -430,8 +428,6 @@ extern void apply_mask(struct prefix *);
 #define prefix_copy(a, b) ({ memset(a, 0, sizeof(*a)); prefix_copy(a, b); })
 #endif
 
-extern struct prefix *sockunion2prefix(const union sockunion *dest,
-                                      const union sockunion *mask);
 extern struct prefix *sockunion2hostprefix(const union sockunion *,
                                           struct prefix *p);
 extern void prefix2sockunion(const struct prefix *, union sockunion *);
@@ -453,8 +449,6 @@ extern void apply_classful_mask_ipv4(struct prefix_ipv4 *);
 
 extern uint8_t ip_masklen(struct in_addr);
 extern void masklen2ip(const int, struct in_addr *);
-/* returns the network portion of the host address */
-extern in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen);
 /* given the address of a host on a network and the network mask length,
  * calculate the broadcast address for that network;
  * special treatment for /31: returns the address of the other host
@@ -484,7 +478,6 @@ extern unsigned prefix_hash_key(const void *pp);
 
 extern int str_to_esi(const char *str, esi_t *esi);
 extern char *esi_to_str(const esi_t *esi, char *buf, int size);
-extern void prefix_hexdump(const struct prefix *p);
 extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
 
 static inline int ipv6_martian(struct in6_addr *addr)
index fb8aeed92f8de27a9e130c55d16a66eb8e81fcce..1be47bd6e1307052170074d08a7ab1cbfa2acc15 100644 (file)
@@ -145,7 +145,8 @@ static void ares_address_cb(void *arg, int status, int timeouts,
 {
        struct resolver_query *query = (struct resolver_query *)arg;
        union sockunion addr[16];
-       void (*callback)(struct resolver_query *, int, union sockunion *);
+       void (*callback)(struct resolver_query *, const char *, int,
+                        union sockunion *);
        size_t i;
 
        callback = query->callback;
@@ -153,9 +154,10 @@ static void ares_address_cb(void *arg, int status, int timeouts,
 
        if (status != ARES_SUCCESS) {
                if (resolver_debug)
-                       zlog_debug("[%p] Resolving failed", query);
+                       zlog_debug("[%p] Resolving failed (%s)",
+                                  query, ares_strerror(status));
 
-               callback(query, -1, NULL);
+               callback(query, ares_strerror(status), -1, NULL);
                return;
        }
 
@@ -177,14 +179,29 @@ static void ares_address_cb(void *arg, int status, int timeouts,
        if (resolver_debug)
                zlog_debug("[%p] Resolved with %d results", query, (int)i);
 
-       callback(query, i, &addr[0]);
+       callback(query, NULL, i, &addr[0]);
+}
+
+static int resolver_cb_literal(struct thread *t)
+{
+       struct resolver_query *query = THREAD_ARG(t);
+       void (*callback)(struct resolver_query *, const char *, int,
+                        union sockunion *);
+
+       callback = query->callback;
+       query->callback = NULL;
+
+       callback(query, ARES_SUCCESS, 1, &query->literal_addr);
+       return 0;
 }
 
 void resolver_resolve(struct resolver_query *query, int af,
                      const char *hostname,
-                     void (*callback)(struct resolver_query *, int,
-                                      union sockunion *))
+                     void (*callback)(struct resolver_query *, const char *,
+                                      int, union sockunion *))
 {
+       int ret;
+
        if (query->callback != NULL) {
                flog_err(
                        EC_LIB_RESOLVER,
@@ -193,10 +210,26 @@ void resolver_resolve(struct resolver_query *query, int af,
                return;
        }
 
+       query->callback = callback;
+       query->literal_cb = NULL;
+
+       ret = str2sockunion(hostname, &query->literal_addr);
+       if (ret == 0) {
+               if (resolver_debug)
+                       zlog_debug("[%p] Resolving '%s' (IP literal)",
+                                  query, hostname);
+
+               /* for consistency with proper name lookup, don't call the
+                * callback immediately; defer to thread loop
+                */
+               thread_add_timer_msec(state.master, resolver_cb_literal,
+                                     query, 0, &query->literal_cb);
+               return;
+       }
+
        if (resolver_debug)
                zlog_debug("[%p] Resolving '%s'", query, hostname);
 
-       query->callback = callback;
        ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
        resolver_update_timeouts(&state);
 }
index bc6326edaa4619e4be87d0a3441569f2326f5992..59bf0d0f55c704ad058a975da0ddeacef0801c73 100644 (file)
 #include "sockunion.h"
 
 struct resolver_query {
-       void (*callback)(struct resolver_query *, int n, union sockunion *);
+       void (*callback)(struct resolver_query *, const char *errstr, int n,
+                        union sockunion *);
+
+       /* used to immediate provide the result if IP literal is passed in */
+       union sockunion literal_addr;
+       struct thread *literal_cb;
 };
 
 void resolver_init(struct thread_master *tm);
 void resolver_resolve(struct resolver_query *query, int af,
                      const char *hostname, void (*cb)(struct resolver_query *,
-                                                      int, union sockunion *));
+                                                      const char *, int,
+                                                      union sockunion *));
 
 #endif /* _FRR_RESOLVER_H */
index c0e01488b28a1890baa5433160a124e9fec16bc2..14fec0283cafebcb98f9e6559f3b61a5a0172afd 100644 (file)
@@ -221,7 +221,7 @@ struct route_map_match_set_hooks {
                          const char *command, const char *arg);
 };
 
-struct route_map_match_set_hooks rmap_match_set_hook;
+static struct route_map_match_set_hooks rmap_match_set_hook;
 
 /* match interface */
 void route_map_match_interface_hook(int (*func)(
@@ -623,7 +623,7 @@ struct route_map_list {
 
 /* Master list of route map. */
 static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL};
-struct hash *route_map_master_hash = NULL;
+static struct hash *route_map_master_hash = NULL;
 
 static unsigned int route_map_hash_key_make(const void *p)
 {
@@ -683,7 +683,7 @@ struct route_map_dep_data {
 };
 
 /* Hashes maintaining dependency between various sublists used by route maps */
-struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX];
+static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX];
 
 static unsigned int route_map_dep_hash_make_key(const void *p);
 static void route_map_clear_all_references(char *rmap_name);
index dda442580a2c87d9e87e67cbde7b0514dfc93bf9..6efa2c362d9efbcd5ca2c133d204869cae13f8c7 100644 (file)
@@ -608,7 +608,7 @@ void skiplist_test(struct vty *vty)
        struct skiplist *l;
        register int i, k;
        void *keys[sampleSize];
-       void *v;
+       void *v = NULL;
 
        zlog_debug("%s: entry", __func__);
 
index 7726d74ff7c4f36df2ba1ba0e2579884af2a38ba..3b12d16cbc7a0d5a5914d0f61c70eadd54ebac57 100644 (file)
@@ -121,21 +121,6 @@ int setsockopt_ipv6_pktinfo(int sock, int val)
        return ret;
 }
 
-/* Set multicast hops val to the socket. */
-int setsockopt_ipv6_checksum(int sock, int val)
-{
-       int ret;
-
-#ifdef GNU_LINUX
-       ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
-#else
-       ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
-#endif /* GNU_LINUX */
-       if (ret < 0)
-               flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_CHECKSUM");
-       return ret;
-}
-
 /* Set multicast hops val to the socket. */
 int setsockopt_ipv6_multicast_hops(int sock, int val)
 {
index f6b57b8e07f1fc9724c17ee7b799f7ff52c8181b..59d8a65964bbdd2631e1ffbf37e081a81a98815e 100644 (file)
@@ -33,7 +33,6 @@ extern int getsockopt_so_sendbuf(const int sock);
 extern int getsockopt_so_recvbuf(const int sock);
 
 extern int setsockopt_ipv6_pktinfo(int, int);
-extern int setsockopt_ipv6_checksum(int, int);
 extern int setsockopt_ipv6_multicast_hops(int, int);
 extern int setsockopt_ipv6_unicast_hops(int, int);
 extern int setsockopt_ipv6_hoplimit(int, int);
index 2e1a0193a28bec2accd052f390f9a45521fa51d1..dd4d5bd96dc38d6941f5d04b526ce5cbe253502f 100644 (file)
@@ -812,7 +812,7 @@ int stream_put_ipv4(struct stream *s, uint32_t l)
 }
 
 /* Put long word to the stream. */
-int stream_put_in_addr(struct stream *s, struct in_addr *addr)
+int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
 {
        STREAM_VERIFY_SANE(s);
 
@@ -828,7 +828,8 @@ int stream_put_in_addr(struct stream *s, struct in_addr *addr)
 }
 
 /* Put in_addr at location in the stream. */
-int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr)
+int stream_put_in_addr_at(struct stream *s, size_t putp,
+                         const struct in_addr *addr)
 {
        STREAM_VERIFY_SANE(s);
 
@@ -842,7 +843,8 @@ int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr)
 }
 
 /* Put in6_addr at location in the stream. */
-int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr)
+int stream_put_in6_addr_at(struct stream *s, size_t putp,
+                          const struct in6_addr *addr)
 {
        STREAM_VERIFY_SANE(s);
 
@@ -856,7 +858,7 @@ int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr)
 }
 
 /* Put prefix by nlri type format. */
-int stream_put_prefix_addpath(struct stream *s, struct prefix *p,
+int stream_put_prefix_addpath(struct stream *s, const struct prefix *p,
                              int addpath_encode, uint32_t addpath_tx_id)
 {
        size_t psize;
@@ -890,7 +892,7 @@ int stream_put_prefix_addpath(struct stream *s, struct prefix *p,
        return psize;
 }
 
-int stream_put_prefix(struct stream *s, struct prefix *p)
+int stream_put_prefix(struct stream *s, const struct prefix *p)
 {
        return stream_put_prefix_addpath(s, p, 0, 0);
 }
index 1144e43ef0e84736e3195c5697db3ddddcaae4f3..c0d25e057988aa00ce6733ee65fd33d321a5d9e8 100644 (file)
@@ -186,13 +186,16 @@ extern int stream_putl_at(struct stream *, size_t, uint32_t);
 extern int stream_putq(struct stream *, uint64_t);
 extern int stream_putq_at(struct stream *, size_t, uint64_t);
 extern int stream_put_ipv4(struct stream *, uint32_t);
-extern int stream_put_in_addr(struct stream *, struct in_addr *);
-extern int stream_put_in_addr_at(struct stream *, size_t, struct in_addr *);
-extern int stream_put_in6_addr_at(struct stream *, size_t, struct in6_addr *);
-extern int stream_put_prefix_addpath(struct stream *, struct prefix *,
+extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr);
+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,
+                                 const struct in6_addr *addr);
+extern int stream_put_prefix_addpath(struct stream *s,
+                                    const struct prefix *p,
                                     int addpath_encode,
                                     uint32_t addpath_tx_id);
-extern int stream_put_prefix(struct stream *, struct prefix *);
+extern int stream_put_prefix(struct stream *s, const struct prefix *p);
 extern int stream_put_labeled_prefix(struct stream *, struct prefix *,
                                     mpls_label_t *, int addpath_encode,
                                     uint32_t addpath_tx_id);
index 23b1950384ac7df07bbb4b5058bace976103fd20..cb6fa7a3b81c050098c8adb2d29e0deba7f1b265 100644 (file)
@@ -18,6 +18,7 @@ lib_libfrr_la_SOURCES = \
        lib/command_parse.y \
        lib/csv.c \
        lib/debug.c \
+       lib/defaults.c \
        lib/distribute.c \
        lib/ferr.c \
        lib/filter.c \
@@ -40,13 +41,13 @@ lib_libfrr_la_SOURCES = \
        lib/json.c \
        lib/keychain.c \
        lib/lib_errors.c \
+       lib/lib_vty.c \
        lib/libfrr.c \
        lib/linklist.c \
        lib/log.c \
        lib/log_vty.c \
        lib/md5.c \
        lib/memory.c \
-       lib/memory_vty.c \
        lib/mlag.c \
        lib/module.c \
        lib/mpls.c \
@@ -114,6 +115,7 @@ vtysh_scan += \
        $(top_srcdir)/lib/if.c \
        $(top_srcdir)/lib/if_rmap.c \
        $(top_srcdir)/lib/keychain.c \
+       $(top_srcdir)/lib/lib_vty.c \
        $(top_srcdir)/lib/nexthop_group.c \
        $(top_srcdir)/lib/plist.c \
        $(top_srcdir)/lib/routemap.c \
@@ -156,6 +158,7 @@ pkginclude_HEADERS += \
        lib/csv.h \
        lib/db.h \
        lib/debug.h \
+       lib/defaults.h \
        lib/distribute.h \
        lib/ferr.h \
        lib/filter.h \
@@ -179,6 +182,7 @@ pkginclude_HEADERS += \
        lib/json.h \
        lib/keychain.h \
        lib/lib_errors.h \
+       lib/lib_vty.h \
        lib/libfrr.h \
        lib/libospf.h \
        lib/linklist.h \
@@ -186,7 +190,6 @@ pkginclude_HEADERS += \
        lib/log_vty.h \
        lib/md5.h \
        lib/memory.h \
-       lib/memory_vty.h \
        lib/module.h \
        lib/monotime.h \
        lib/mpls.h \
index 44db48d0060351bb007dec7376aaa6826162957f..81b0400ab99c067d00fa9dc8d8085168f3f35b41 100644 (file)
@@ -32,7 +32,7 @@
  * Wrapper this silliness if we
  * don't have systemd
  */
-void systemd_send_information(const char *info)
+static void systemd_send_information(const char *info)
 {
 #if defined HAVE_SYSTEMD
        sd_notify(0, info);
@@ -93,8 +93,8 @@ void systemd_send_stopping(void)
 /*
  * How many seconds should we wait between watchdog sends
  */
-int wsecs = 0;
-struct thread_master *systemd_master = NULL;
+static int wsecs = 0;
+static struct thread_master *systemd_master = NULL;
 
 static int systemd_send_watchdog(struct thread *t)
 {
index 1f730720cefb18dc719a304f55e953b1010c3236..d9885c5d9c3ef2267613cee9ad9566d8300e4a30 100644 (file)
@@ -32,7 +32,6 @@ extern "C" {
  * To turn on systemd compilation, use --enable-systemd on
  * configure run.
  */
-void systemd_send_information(const char *info);
 void systemd_send_stopping(void);
 
 /*
index 2411cc31118d6820274366de314f676e420eef01..c3a94224eef8e5141f02b147f90e8c380bc83acd 100644 (file)
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -67,7 +67,7 @@ static char vrf_default_name[VRF_NAMSIZ] = VRF_DEFAULT_NAME_INTERNAL;
 static int debug_vrf = 0;
 
 /* Holding VRF hooks  */
-struct vrf_master {
+static struct vrf_master {
        int (*vrf_new_hook)(struct vrf *);
        int (*vrf_delete_hook)(struct vrf *);
        int (*vrf_enable_hook)(struct vrf *);
@@ -555,7 +555,6 @@ void vrf_terminate(void)
        }
 }
 
-/* Create a socket for the VRF. */
 int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
               const char *interfacename)
 {
@@ -752,7 +751,7 @@ DEFUN (no_vrf,
 }
 
 
-struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
+static struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
 
 DEFUN_NOSH (vrf_netns,
        vrf_netns_cmd,
index ca253e58a3618c2c83cee703d7ba26649998a9f7..1d87576f78818c2f10c8ebdda48c3189ff4ac123 100644 (file)
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -218,13 +218,36 @@ extern void vrf_terminate(void);
  * or call network operations
  */
 
-/* Create a socket serving for the given VRF */
+/*
+ * Create a new socket associated with a VRF.
+ *
+ * This is a wrapper that ensures correct behavior when using namespace VRFs.
+ * In the namespace case, the socket is created within the namespace. In the
+ * non-namespace case, this is equivalent to socket().
+ *
+ * If name is provided, this is provided to vrf_bind() to bind the socket to
+ * the VRF. This is only relevant when using VRF-lite.
+ *
+ * Summary:
+ * - Namespace: pass vrf_id but not name
+ * - VRF-lite: pass vrf_id and name of VRF device to bind to
+ * - VRF-lite, no binding: pass vrf_id but not name, or just use socket()
+ */
 extern int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
                      const char *name);
 
 extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
                                const char *name);
 
+/*
+ * Binds a socket to a VRF device.
+ *
+ * If name is null, the socket is not bound, irrespective of any other
+ * arguments.
+ *
+ * name should be the name of the VRF device. vrf_id should be the
+ * corresponding vrf_id (the ifindex of the device).
+ */
 extern int vrf_bind(vrf_id_t vrf_id, int fd, const char *name);
 
 /* VRF ioctl operations */
index c08e5e151a6da6f7aea0de797151d3c62f2da6f6..40da8abcd74b575a63fe13a2b40fc0b59a9d0dde 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -89,7 +89,7 @@ static char *vty_ipv6_accesslist_name = NULL;
 static vector Vvty_serv_thread;
 
 /* Current directory. */
-char vty_cwd[MAXPATHLEN];
+static char vty_cwd[MAXPATHLEN];
 
 /* Login password check. */
 static int no_password_check = 0;
index d153f755305a49a77f399bc7517b7f4130b7c394..93e6db3055302dd9d39445c5b2d16dc12061e525 100644 (file)
@@ -77,6 +77,7 @@ static const char *const frr_native_modules[] = {
        "frr-ripd",
        "frr-ripngd",
        "frr-isisd",
+       "frr-vrrpd",
 };
 
 /* Generate the yang_modules tree. */
index 7a62e408eae7f161145da6a9c907b0a9bf7d23df..b2c74cd0b90875201a155cd9fa019e9947e1ebc8 100644 (file)
@@ -143,6 +143,18 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance)
        }
 }
 
+void redist_del_all_instances(struct redist_proto *red)
+{
+       struct listnode *ln, *nn;
+       unsigned short *id;
+
+       if (!red->instances)
+               return;
+
+       for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id))
+               redist_del_instance(red, *id);
+}
+
 /* Stop zebra client services. */
 void zclient_stop(struct zclient *zclient)
 {
@@ -802,6 +814,12 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1,
        if (next1->type > next2->type)
                return 1;
 
+       if (next1->weight < next2->weight)
+               return -1;
+
+       if (next1->weight > next2->weight)
+               return 1;
+
        switch (next1->type) {
        case NEXTHOP_TYPE_IPV4:
        case NEXTHOP_TYPE_IPV6:
@@ -859,6 +877,77 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp,
              &zapi_nexthop_cmp);
 }
 
+/*
+ * Encode a single zapi nexthop
+ */
+int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
+                       uint32_t api_flags)
+{
+       int ret = 0;
+       int nh_flags = api_nh->flags;
+
+       stream_putl(s, api_nh->vrf_id);
+       stream_putc(s, api_nh->type);
+
+       /* If needed, set 'labelled nexthop' flag */
+       if (api_nh->label_num > 0) {
+               SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
+               /* Validate label count */
+               if (api_nh->label_num > MPLS_MAX_LABELS) {
+                       ret = -1;
+                       goto done;
+               }
+       }
+
+       if (api_nh->weight)
+               SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT);
+
+       /* Note that we're only encoding a single octet */
+       stream_putc(s, nh_flags);
+
+       switch (api_nh->type) {
+       case NEXTHOP_TYPE_BLACKHOLE:
+               stream_putc(s, api_nh->bh_type);
+               break;
+       case NEXTHOP_TYPE_IPV4:
+       case NEXTHOP_TYPE_IPV4_IFINDEX:
+               stream_put_in_addr(s, &api_nh->gate.ipv4);
+               stream_putl(s, api_nh->ifindex);
+               break;
+       case NEXTHOP_TYPE_IFINDEX:
+               stream_putl(s, api_nh->ifindex);
+               break;
+       case NEXTHOP_TYPE_IPV6:
+       case NEXTHOP_TYPE_IPV6_IFINDEX:
+               stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
+                            16);
+               stream_putl(s, api_nh->ifindex);
+               break;
+       }
+
+       /* We only encode labels if we have >0 - we use
+        * the per-nexthop flag above to signal that the count
+        * is present in the payload.
+        */
+       if (api_nh->label_num > 0) {
+               stream_putc(s, api_nh->label_num);
+               stream_put(s, &api_nh->labels[0],
+                          api_nh->label_num * sizeof(mpls_label_t));
+       }
+
+       if (api_nh->weight)
+               stream_putl(s, api_nh->weight);
+
+       /* Router MAC for EVPN routes. */
+       if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE))
+               stream_put(s, &(api_nh->rmac),
+                          sizeof(struct ethaddr));
+
+done:
+       return ret;
+}
+
 int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
 {
        struct zapi_nexthop *api_nh;
@@ -921,59 +1010,22 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
                for (i = 0; i < api->nexthop_num; i++) {
                        api_nh = &api->nexthops[i];
 
-                       stream_putl(s, api_nh->vrf_id);
-                       stream_putc(s, api_nh->type);
-                       stream_putc(s, api_nh->onlink);
-                       switch (api_nh->type) {
-                       case NEXTHOP_TYPE_BLACKHOLE:
-                               stream_putc(s, api_nh->bh_type);
-                               break;
-                       case NEXTHOP_TYPE_IPV4:
-                               stream_put_in_addr(s, &api_nh->gate.ipv4);
-                               break;
-                       case NEXTHOP_TYPE_IPV4_IFINDEX:
-                               stream_put_in_addr(s, &api_nh->gate.ipv4);
-                               stream_putl(s, api_nh->ifindex);
-                               break;
-                       case NEXTHOP_TYPE_IFINDEX:
-                               stream_putl(s, api_nh->ifindex);
-                               break;
-                       case NEXTHOP_TYPE_IPV6:
-                               stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
-                                            16);
-                               break;
-                       case NEXTHOP_TYPE_IPV6_IFINDEX:
-                               stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
-                                            16);
-                               stream_putl(s, api_nh->ifindex);
-                               break;
-                       }
-
                        /* MPLS labels for BGP-LU or Segment Routing */
-                       if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) {
-                               if (api_nh->label_num > MPLS_MAX_LABELS) {
-                                       char buf[PREFIX2STR_BUFFER];
-                                       prefix2str(&api->prefix, buf,
-                                                  sizeof(buf));
-                                       flog_err(EC_LIB_ZAPI_ENCODE,
-                                                "%s: prefix %s: can't encode "
-                                                "%u labels (maximum is %u)",
-                                                __func__, buf,
-                                                api_nh->label_num,
-                                                MPLS_MAX_LABELS);
-                                       return -1;
-                               }
-
-                               stream_putc(s, api_nh->label_num);
-                               stream_put(s, &api_nh->labels[0],
-                                          api_nh->label_num
-                                                  * sizeof(mpls_label_t));
+                       if (api_nh->label_num > MPLS_MAX_LABELS) {
+                               char buf[PREFIX2STR_BUFFER];
+
+                               prefix2str(&api->prefix, buf, sizeof(buf));
+
+                               flog_err(EC_LIB_ZAPI_ENCODE,
+                                        "%s: prefix %s: can't encode %u labels (maximum is %u)",
+                                        __func__, buf,
+                                        api_nh->label_num,
+                                        MPLS_MAX_LABELS);
+                               return -1;
                        }
 
-                       /* Router MAC for EVPN routes. */
-                       if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
-                               stream_put(s, &(api_nh->rmac),
-                                          sizeof(struct ethaddr));
+                       if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+                               return -1;
                }
        }
 
@@ -995,6 +1047,71 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
        return 0;
 }
 
+/*
+ * Decode a single zapi nexthop object
+ */
+static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
+                              uint32_t api_flags)
+{
+       int ret = -1;
+
+       STREAM_GETL(s, api_nh->vrf_id);
+       STREAM_GETC(s, api_nh->type);
+
+       /* Note that we're only using a single octet of flags */
+       STREAM_GETC(s, api_nh->flags);
+
+       switch (api_nh->type) {
+       case NEXTHOP_TYPE_BLACKHOLE:
+               STREAM_GETC(s, api_nh->bh_type);
+               break;
+       case NEXTHOP_TYPE_IPV4:
+       case NEXTHOP_TYPE_IPV4_IFINDEX:
+               STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
+                          IPV4_MAX_BYTELEN);
+               STREAM_GETL(s, api_nh->ifindex);
+               break;
+       case NEXTHOP_TYPE_IFINDEX:
+               STREAM_GETL(s, api_nh->ifindex);
+               break;
+       case NEXTHOP_TYPE_IPV6:
+       case NEXTHOP_TYPE_IPV6_IFINDEX:
+               STREAM_GET(&api_nh->gate.ipv6, s, 16);
+               STREAM_GETL(s, api_nh->ifindex);
+               break;
+       }
+
+       /* MPLS labels for BGP-LU or Segment Routing */
+       if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)) {
+               STREAM_GETC(s, api_nh->label_num);
+               if (api_nh->label_num > MPLS_MAX_LABELS) {
+                       flog_err(
+                               EC_LIB_ZAPI_ENCODE,
+                               "%s: invalid number of MPLS labels (%u)",
+                               __func__, api_nh->label_num);
+                       return -1;
+               }
+
+               STREAM_GET(&api_nh->labels[0], s,
+                          api_nh->label_num * sizeof(mpls_label_t));
+       }
+
+       if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT))
+               STREAM_GETL(s, api_nh->weight);
+
+       /* Router MAC for EVPN routes. */
+       if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE))
+               STREAM_GET(&(api_nh->rmac), s,
+                          sizeof(struct ethaddr));
+
+       /* Success */
+       ret = 0;
+
+stream_failure:
+
+       return ret;
+}
+
 int zapi_route_decode(struct stream *s, struct zapi_route *api)
 {
        struct zapi_nexthop *api_nh;
@@ -1088,55 +1205,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
                for (i = 0; i < api->nexthop_num; i++) {
                        api_nh = &api->nexthops[i];
 
-                       STREAM_GETL(s, api_nh->vrf_id);
-                       STREAM_GETC(s, api_nh->type);
-                       STREAM_GETC(s, api_nh->onlink);
-                       switch (api_nh->type) {
-                       case NEXTHOP_TYPE_BLACKHOLE:
-                               STREAM_GETC(s, api_nh->bh_type);
-                               break;
-                       case NEXTHOP_TYPE_IPV4:
-                               STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
-                                          IPV4_MAX_BYTELEN);
-                               break;
-                       case NEXTHOP_TYPE_IPV4_IFINDEX:
-                               STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
-                                          IPV4_MAX_BYTELEN);
-                               STREAM_GETL(s, api_nh->ifindex);
-                               break;
-                       case NEXTHOP_TYPE_IFINDEX:
-                               STREAM_GETL(s, api_nh->ifindex);
-                               break;
-                       case NEXTHOP_TYPE_IPV6:
-                               STREAM_GET(&api_nh->gate.ipv6, s, 16);
-                               break;
-                       case NEXTHOP_TYPE_IPV6_IFINDEX:
-                               STREAM_GET(&api_nh->gate.ipv6, s, 16);
-                               STREAM_GETL(s, api_nh->ifindex);
-                               break;
-                       }
-
-                       /* MPLS labels for BGP-LU or Segment Routing */
-                       if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) {
-                               STREAM_GETC(s, api_nh->label_num);
-
-                               if (api_nh->label_num > MPLS_MAX_LABELS) {
-                                       flog_err(
-                                               EC_LIB_ZAPI_ENCODE,
-                                               "%s: invalid number of MPLS labels (%u)",
-                                               __func__, api_nh->label_num);
-                                       return -1;
-                               }
-
-                               STREAM_GET(&api_nh->labels[0], s,
-                                          api_nh->label_num
-                                                  * sizeof(mpls_label_t));
-                       }
-
-                       /* Router MAC for EVPN routes. */
-                       if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
-                               stream_get(&(api_nh->rmac), s,
-                                          sizeof(struct ethaddr));
+                       if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+                               return -1;
                }
        }
 
@@ -1335,6 +1405,35 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
        return n;
 }
 
+/*
+ * Convert nexthop to zapi nexthop
+ */
+int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
+                             const struct nexthop *nh)
+{
+       int i;
+
+       memset(znh, 0, sizeof(*znh));
+
+       znh->type = nh->type;
+       znh->vrf_id = nh->vrf_id;
+       znh->ifindex = nh->ifindex;
+       znh->gate = nh->gate;
+
+       if (nh->nh_label && (nh->nh_label->num_labels > 0)) {
+               for (i = 0; i < nh->nh_label->num_labels; i++)
+                       znh->labels[i] = nh->nh_label->label[i];
+
+               znh->label_num = i;
+               SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+       }
+
+       return 0;
+}
+
+/*
+ * Decode the nexthop-tracking update message
+ */
 bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
 {
        uint32_t i;
@@ -1361,38 +1460,8 @@ 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++) {
-               STREAM_GETL(s, nhr->nexthops[i].vrf_id);
-               STREAM_GETC(s, nhr->nexthops[i].type);
-               switch (nhr->nexthops[i].type) {
-               case NEXTHOP_TYPE_IPV4:
-               case NEXTHOP_TYPE_IPV4_IFINDEX:
-                       STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr, s,
-                                  IPV4_MAX_BYTELEN);
-                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
-                       break;
-               case NEXTHOP_TYPE_IFINDEX:
-                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
-                       break;
-               case NEXTHOP_TYPE_IPV6:
-               case NEXTHOP_TYPE_IPV6_IFINDEX:
-                       STREAM_GET(&nhr->nexthops[i].gate.ipv6, s,
-                                  IPV6_MAX_BYTELEN);
-                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
-                       break;
-               case NEXTHOP_TYPE_BLACKHOLE:
-                       break;
-               }
-               STREAM_GETC(s, nhr->nexthops[i].label_num);
-               if (nhr->nexthops[i].label_num > MPLS_MAX_LABELS) {
-                       flog_err(EC_LIB_ZAPI_ENCODE,
-                                "%s: invalid number of MPLS labels (%u)",
-                                __func__, nhr->nexthops[i].label_num);
-                       return false;
-               }
-               if (nhr->nexthops[i].label_num)
-                       STREAM_GET(&nhr->nexthops[i].labels[0], s,
-                                  nhr->nexthops[i].label_num
-                                          * sizeof(mpls_label_t));
+               if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0)
+                       return -1;
        }
 
        return true;
@@ -1400,6 +1469,21 @@ stream_failure:
        return false;
 }
 
+bool zapi_error_decode(struct stream *s, enum zebra_error_types *error)
+{
+       memset(error, 0, sizeof(*error));
+
+       STREAM_GET(error, s, sizeof(*error));
+
+       if (zclient_debug)
+               zlog_debug("%s: type: %s", __func__,
+                          zebra_error_type2str(*error));
+
+       return true;
+stream_failure:
+       return false;
+}
+
 /*
  * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
  * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
@@ -1639,6 +1723,17 @@ static void zclient_interface_down(struct zclient *zclient, vrf_id_t vrf_id)
        if_down_via_zapi(ifp);
 }
 
+static void zclient_handle_error(ZAPI_CALLBACK_ARGS)
+{
+       enum zebra_error_types error;
+       struct stream *s = zclient->ibuf;
+
+       zapi_error_decode(s, &error);
+
+       if (zclient->handle_error)
+               (*zclient->handle_error)(error);
+}
+
 static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
 {
 
@@ -2612,6 +2707,17 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl)
        }
 
        STREAM_GETW(s, zl->nexthop_num);
+
+       if (zl->nexthop_num > MULTIPATH_NUM) {
+               flog_warn(
+                       EC_LIB_ZAPI_ENCODE,
+                       "%s: Prefix %pFX has %d nexthops, but we can only use the first %d",
+                       __func__, &zl->route.prefix, zl->nexthop_num,
+                       MULTIPATH_NUM);
+       }
+
+       zl->nexthop_num = MIN(MULTIPATH_NUM, zl->nexthop_num);
+
        for (int i = 0; i < zl->nexthop_num; i++) {
                znh = &zl->nexthops[i];
 
@@ -3075,6 +3181,8 @@ static int zclient_read(struct thread *thread)
        case ZEBRA_MLAG_FORWARD_MSG:
                zclient_mlag_handle_msg(command, zclient, length, vrf_id);
                break;
+       case ZEBRA_ERROR:
+               zclient_handle_error(command, zclient, length, vrf_id);
        default:
                break;
        }
index 7adb294a31d4e20e33c6f7e111f9911f1dc7f309..d1aa42da6d2ae078a5c16c255c17f017308c2330 100644 (file)
@@ -183,8 +183,34 @@ typedef enum {
        ZEBRA_MLAG_CLIENT_REGISTER,
        ZEBRA_MLAG_CLIENT_UNREGISTER,
        ZEBRA_MLAG_FORWARD_MSG,
+       ZEBRA_ERROR,
 } zebra_message_types_t;
 
+enum zebra_error_types {
+       ZEBRA_UNKNOWN_ERROR,    /* Error of unknown type */
+       ZEBRA_NO_VRF,           /* Vrf in header was not found */
+       ZEBRA_INVALID_MSG_TYPE, /* No handler found for msg type */
+};
+
+static inline const char *zebra_error_type2str(enum zebra_error_types type)
+{
+       const char *ret = "UNKNOWN";
+
+       switch (type) {
+       case ZEBRA_UNKNOWN_ERROR:
+               ret = "ZEBRA_UNKNOWN_ERROR";
+               break;
+       case ZEBRA_NO_VRF:
+               ret = "ZEBRA_NO_VRF";
+               break;
+       case ZEBRA_INVALID_MSG_TYPE:
+               ret = "ZEBRA_INVALID_MSG_TYPE";
+               break;
+       }
+
+       return ret;
+}
+
 struct redist_proto {
        uint8_t enabled;
        struct list *instances;
@@ -280,6 +306,7 @@ struct zclient {
        int (*mlag_process_up)(void);
        int (*mlag_process_down)(void);
        int (*mlag_handle_msg)(struct stream *msg, int len);
+       int (*handle_error)(enum zebra_error_types error);
 };
 
 /* Zebra API message flag. */
@@ -289,7 +316,6 @@ struct zclient {
 #define ZAPI_MESSAGE_TAG      0x08
 #define ZAPI_MESSAGE_MTU      0x10
 #define ZAPI_MESSAGE_SRCPFX   0x20
-#define ZAPI_MESSAGE_LABEL    0x40
 /*
  * This should only be used by a DAEMON that needs to communicate
  * the table being used is not in the VRF.  You must pass the
@@ -313,7 +339,7 @@ struct zapi_nexthop {
        enum nexthop_types_t type;
        vrf_id_t vrf_id;
        ifindex_t ifindex;
-       bool onlink;
+       uint8_t flags;
        union {
                union g_addr gate;
                enum blackhole_type bh_type;
@@ -324,8 +350,17 @@ struct zapi_nexthop {
        mpls_label_t labels[MPLS_MAX_LABELS];
 
        struct ethaddr rmac;
+
+       uint32_t weight;
 };
 
+/*
+ * ZAPI nexthop flags values
+ */
+#define ZAPI_NEXTHOP_FLAG_ONLINK       0x01
+#define ZAPI_NEXTHOP_FLAG_LABEL                0x02
+#define ZAPI_NEXTHOP_FLAG_WEIGHT       0x04
+
 /*
  * Some of these data structures do not map easily to
  * a actual data structure size giving different compilers
@@ -485,6 +520,29 @@ enum zapi_iptable_notify_owner {
        ZAPI_IPTABLE_FAIL_REMOVE,
 };
 
+static inline const char *
+zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note)
+{
+       const char *ret = "UNKNOWN";
+
+       switch (note) {
+       case ZAPI_RULE_FAIL_INSTALL:
+               ret = "ZAPI_RULE_FAIL_INSTALL";
+               break;
+       case ZAPI_RULE_INSTALLED:
+               ret = "ZAPI_RULE_INSTALLED";
+               break;
+       case ZAPI_RULE_FAIL_REMOVE:
+               ret = "ZAPI_RULE_FAIL_REMOVE";
+               break;
+       case ZAPI_RULE_REMOVED:
+               ret = "ZAPI_RULE_REMOVED";
+               break;
+       }
+
+       return ret;
+}
+
 /* Zebra MAC types */
 #define ZEBRA_MACIP_TYPE_STICKY                0x01 /* Sticky MAC*/
 #define ZEBRA_MACIP_TYPE_GW                    0x02 /* gateway (SVI) mac*/
@@ -516,6 +574,7 @@ extern unsigned short *redist_check_instance(struct redist_proto *,
                                             unsigned short);
 extern void redist_add_instance(struct redist_proto *, unsigned short);
 extern void redist_del_instance(struct redist_proto *, unsigned short);
+extern void redist_del_all_instances(struct redist_proto *red);
 
 /*
  * Send to zebra that the specified vrf is using label to resolve
@@ -666,6 +725,8 @@ extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *);
 extern int zclient_send_rnh(struct zclient *zclient, int command,
                            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);
 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,
@@ -690,9 +751,14 @@ bool zapi_iptable_notify_decode(struct stream *s,
                enum zapi_iptable_notify_owner *note);
 
 extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
+int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
+                             const struct nexthop *nh);
 extern bool zapi_nexthop_update_decode(struct stream *s,
                                       struct zapi_route *nhr);
 
+/* Decode the zebra error message */
+extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error);
+
 static inline void zapi_route_set_blackhole(struct zapi_route *api,
                                            enum blackhole_type bh_type)
 {
index 66338511a3591e0a3e325b4af6f11e2155c2df31..69809184ee84ab2b709a7e541c22ce6348f50736 100644 (file)
@@ -186,7 +186,7 @@ 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
+      if test x"${py_ver}" = x"3.8" || test x"{py_ver}" = x"3.9"; then
         PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`"
       else
         PYTHON_LIBS="`\"$pycfg\" --ldflags`"
index 9fab662860884325fe7d5649aa25ade200806d3e..49d176150557a81a4a3bccc01e8a1c1fe69d1a7b 100644 (file)
@@ -1,4 +1,4 @@
-if HAVE_PROTOBUF
+if HAVE_PROTOBUF3
 lib_LTLIBRARIES += mlag/libmlag_pb.la
 endif
 
index a781122b16a3b2699abb35da7adab1b435d222b5..b58fe776ab543f6bfc1689f9420e66239f5be83f 100644 (file)
@@ -21,7 +21,6 @@
 #include "version.h"
 #include "log.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "command.h"
 #include "libfrr.h"
 
index 360972c327f54cb98038a0b648c6ff1a9c9cc516..bec6c014a0855a2d4c3de233741743bfa98e4cfc 100644 (file)
@@ -238,8 +238,8 @@ nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr)
        return NULL;
 }
 
-static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n,
-                               union sockunion *addrs)
+static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr,
+                               int n, union sockunion *addrs)
 {
        struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve);
        struct nhrp_interface *nifp = nhs->ifp->info;
index 3a74b756961b9ee943ba2894b55af84d3eaf1bfc..c5e985cdacad37b1167fda62996b3908ffa07996 100644 (file)
@@ -896,8 +896,10 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
 
        extoff = htons(hdr->extension_offset);
        if (extoff) {
-               if (extoff >= realsize) {
-                       info = "extoff larger than packet";
+               assert(zb->head > zb->buf);
+               uint32_t header_offset = zb->head - zb->buf;
+               if ((extoff >= realsize) || (extoff < (header_offset))) {
+                       info = "extoff larger than packet, or smaller than header";
                        goto drop;
                }
                paylen = extoff - (zb->head - zb->buf);
index fe76623ac38b7beb6d2fe64f8bdf92e5bad8efe3..42a6380b179d64edbbe308d99354fdc2c19f1a17 100644 (file)
@@ -5,7 +5,7 @@
 if NHRPD
 sbin_PROGRAMS += nhrpd/nhrpd
 vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c
-man8 += $(MANBUILD)/nhrpd.8
+man8 += $(MANBUILD)/frr-nhrpd.8
 endif
 
 nhrpd_nhrpd_LDADD = lib/libfrr.la lib/libfrrcares.la $(LIBCAP)
index 0fa5585b80b1e322a46ef21ac6e4c4f69f708a1d..0aaefeb3c26a0c4c9d28d899e2651a787782538e 100644 (file)
@@ -28,7 +28,6 @@
 #include "command.h"
 #include "vty.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "if.h"
 #include "filter.h"
 #include "prefix.h"
index 95dafff84e4037452be7bf17455f860ed14424ca..95537eb86ec0678d6b0473ceb267015732600140 100644 (file)
 
 DEFINE_QOBJ_TYPE(ospf6)
 
+FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+
 /* global ospf6d variable */
 struct ospf6 *ospf6;
 static struct ospf6_master ospf6_master;
@@ -179,11 +184,6 @@ static struct ospf6 *ospf6_create(vrf_id_t vrf_id)
 
        o->distance_table = route_table_init();
 
-/* Enable "log-adjacency-changes" */
-#if DFLT_OSPF6_LOG_ADJACENCY_CHANGES
-       SET_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
-#endif
-
        QOBJ_REG(o, ospf6);
 
        return o;
@@ -327,6 +327,9 @@ DEFUN_NOSH (router_ospf6,
 {
        if (ospf6 == NULL) {
                ospf6 = ospf6_create(VRF_DEFAULT);
+               if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
+                       SET_FLAG(ospf6->config_flags,
+                                OSPF6_LOG_ADJACENCY_CHANGES);
                if (ospf6->router_id == 0)
                        ospf6_router_id_update();
        }
@@ -1079,9 +1082,9 @@ static int config_write_ospf6(struct vty *vty)
        if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) {
                if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL))
                        vty_out(vty, " log-adjacency-changes detail\n");
-               else if (!DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
+               else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES)
                        vty_out(vty, " log-adjacency-changes\n");
-       } else if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) {
+       } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) {
                vty_out(vty, " no log-adjacency-changes\n");
        }
 
index ba41fca65b250644eed5e8e6ba79970382c2a79b..18c069702523333f021b468cda2d1dca4141c42c 100644 (file)
@@ -29,6 +29,12 @@ struct ospf6_master {
        uint32_t zebra_router_id;
 };
 
+/* ospf6->config_flags */
+enum {
+       OSPF6_LOG_ADJACENCY_CHANGES =   (1 << 0),
+       OSPF6_LOG_ADJACENCY_DETAIL =    (1 << 1),
+};
+
 /* OSPFv3 top level data structure */
 struct ospf6 {
        /* The relevant vrf_id */
@@ -68,10 +74,8 @@ struct ospf6 {
 
        uint8_t flag;
 
-       /* Configured flags */
+       /* Configuration bitmask, refer to enum above */
        uint8_t config_flags;
-#define OSPF6_LOG_ADJACENCY_CHANGES      (1 << 0)
-#define OSPF6_LOG_ADJACENCY_DETAIL       (1 << 1)
 
        /* LSA timer parameters */
        unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */
index eac0eee45f849894b1b6dd7be1ab9916114387bf..570b077cb1b17e79e7bce206bac7bf76ac6e219b 100644 (file)
@@ -26,7 +26,7 @@ vtysh_scan += \
 if SNMP
 module_LTLIBRARIES += ospf6d/ospf6d_snmp.la
 endif
-man8 += $(MANBUILD)/ospf6d.8
+man8 += $(MANBUILD)/frr-ospf6d.8
 endif
 
 ospf6d_libospf6_a_SOURCES = \
index 94d489358ce7824b363cc3c1fccb39439bc3f337..756ad88f151359753d7c4fe5dc6539116c7a6b6a 100644 (file)
@@ -5,7 +5,7 @@
 if OSPFCLIENT
 lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la
 noinst_PROGRAMS += ospfclient/ospfclient
-man8 += $(MANBUILD)/ospfclient.8
+#man8 += $(MANBUILD)/frr-ospfclient.8
 endif
 
 ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0
index bd703bc89de172eb73a736185b6dbdeaf706c17d..49730063b996ec1ddbf2734cd5cc152b404db247 100644 (file)
@@ -2325,7 +2325,7 @@ void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi)
 void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr)
 {
        struct msg *msg;
-       struct in_addr ifaddr = {.s_addr = 0L};
+       struct in_addr ifaddr;
        struct in_addr nbraddr;
 
        assert(nbr);
index 7ddffbcdbdc36d289b076f7a8ac27cd2d762cae1..8efb32af37ad61e44b8416f185c7e3edcc53574f 100644 (file)
@@ -636,9 +636,13 @@ void ospf_if_update_params(struct interface *ifp, struct in_addr addr)
 int ospf_if_new_hook(struct interface *ifp)
 {
        int rc = 0;
+       struct ospf_if_info *oii;
 
        ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info));
 
+       oii = ifp->info;
+       oii->curr_mtu = ifp->mtu;
+
        IF_OIFS(ifp) = route_table_init();
        IF_OIFS_PARAMS(ifp) = route_table_init();
 
@@ -902,10 +906,12 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf,
 
 static void ospf_vl_if_delete(struct ospf_vl_data *vl_data)
 {
+       struct interface *ifp = vl_data->vl_oi->ifp;
+
        vl_data->vl_oi->address->u.prefix4.s_addr = 0;
        vl_data->vl_oi->address->prefixlen = 0;
        ospf_if_free(vl_data->vl_oi);
-       if_delete(&vl_data->vl_oi->ifp);
+       if_delete(&ifp);
        vlink_count--;
 }
 
@@ -1261,31 +1267,21 @@ static int ospf_ifp_up(struct interface *ifp)
 {
        struct ospf_interface *oi;
        struct route_node *rn;
+       struct ospf_if_info *oii = ifp->info;
 
-       /* Interface is already up. */
-       if (if_is_operative(ifp)) {
-               /* Temporarily keep ifp values. */
-               struct interface if_tmp;
-               memcpy(&if_tmp, ifp, sizeof(struct interface));
+       ospf_if_recalculate_output_cost(ifp);
 
+       if (oii && oii->curr_mtu != ifp->mtu) {
                if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
                        zlog_debug(
-                               "Zebra: Interface[%s] state update speed %u -> %u, bw  %d -> %d",
-                               ifp->name, if_tmp.speed, ifp->speed,
-                               if_tmp.bandwidth, ifp->bandwidth);
+                               "Zebra: Interface[%s] MTU change %u -> %u.",
+                               ifp->name, oii->curr_mtu, ifp->mtu);
 
-               ospf_if_recalculate_output_cost(ifp);
+               oii->curr_mtu = ifp->mtu;
+               /* Must reset the interface (simulate down/up) when MTU
+                * changes. */
+               ospf_if_reset(ifp);
 
-               if (if_tmp.mtu != ifp->mtu) {
-                       if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
-                               zlog_debug(
-                                       "Zebra: Interface[%s] MTU change %u -> %u.",
-                                       ifp->name, if_tmp.mtu, ifp->mtu);
-
-                       /* Must reset the interface (simulate down/up) when MTU
-                        * changes. */
-                       ospf_if_reset(ifp);
-               }
                return 0;
        }
 
index cde52dbb9e3c21904692f09b45459a9e8c53f0ce..4b3dbcc5c27284e4fe1c051e6676f373a035874c 100644 (file)
@@ -117,6 +117,8 @@ struct ospf_if_info {
        struct route_table *oifs;
        unsigned int
                membership_counts[MEMBER_MAX]; /* multicast group refcnts */
+
+       uint32_t curr_mtu;
 };
 
 struct ospf_interface;
index a542b4da8373859a70f6faddbdd87e3470cc7ba2..d02ffe0448df73ad394fd1e04c67218729d2b850 100644 (file)
@@ -35,7 +35,6 @@
 #include "stream.h"
 #include "log.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "privs.h"
 #include "sigevent.h"
 #include "zclient.h"
index 80ffc3f36102e40e7d83bd70d415cc47a6dd9b07..95fb694925a3d1eb090bafb274563541d241257d 100644 (file)
@@ -2318,8 +2318,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd,
                                  safe_strerror(errno));
                return NULL;
        }
-       if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */
-       {
+       if ((unsigned int)ret < sizeof(struct ip)) {
                flog_warn(
                        EC_OSPF_PACKET,
                        "ospf_recv_packet: discarding runt packet of length %d "
@@ -3001,11 +3000,23 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf)
                return OSPF_READ_CONTINUE;
        }
 
-       /*
-        * Advance from IP header to OSPF header (iph->ip_hl has
-        * been verified by ospf_recv_packet() to be correct).
-        */
-       stream_forward_getp(ibuf, iph->ip_hl * 4);
+       /* Check that we have enough for an IP header */
+       if ((unsigned int)(iph->ip_hl << 2) >= STREAM_READABLE(ibuf)) {
+               if ((unsigned int)(iph->ip_hl << 2) == STREAM_READABLE(ibuf)) {
+                       flog_warn(
+                               EC_OSPF_PACKET,
+                               "Rx'd IP packet with OSPF protocol number but no payload");
+               } else {
+                       flog_warn(
+                               EC_OSPF_PACKET,
+                               "IP header length field claims header is %u bytes, but we only have %zu",
+                               (unsigned int)(iph->ip_hl << 2),
+                               STREAM_READABLE(ibuf));
+               }
+
+               return OSPF_READ_ERROR;
+       }
+       stream_forward_getp(ibuf, iph->ip_hl << 2);
 
        ospfh = (struct ospf_header *)stream_pnt(ibuf);
        if (MSG_OK
index 4c97615ed14858c31050082f55210df0b028f428..1542ef88fb9ad3eeedb8e7dadfc1d360af78151a 100644 (file)
 #include "ospfd/ospf_dump.h"
 #include "ospfd/ospf_bfd.h"
 
+FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
+       { .val_long = true, .match_profile = "datacenter", },
+       { .val_long = false },
+)
+
 static const char *const ospf_network_type_str[] = {
        "Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT",
        "VIRTUALLINK", "LOOPBACK"};
@@ -138,6 +143,7 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty,
        struct ospf *ospf = NULL;
        int idx_vrf = 0, idx_inst = 0;
        const char *vrf_name = NULL;
+       bool created = false;
 
        *instance = 0;
        if (argv_find(argv, argc, "(1-65535)", &idx_inst))
@@ -149,18 +155,23 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty,
                        vrf_name = NULL;
                if (enable) {
                        /* Allocate VRF aware instance */
-                       ospf = ospf_get(*instance, vrf_name);
+                       ospf = ospf_get(*instance, vrf_name, &created);
                } else {
                        ospf = ospf_lookup_by_inst_name(*instance, vrf_name);
                }
        } else {
                if (enable) {
-                       ospf = ospf_get(*instance, NULL);
+                       ospf = ospf_get(*instance, NULL, &created);
                } else {
                        ospf = ospf_lookup_instance(*instance);
                }
        }
 
+       if (created) {
+               if (DFLT_OSPF_LOG_ADJACENCY_CHANGES)
+                       SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
+       }
+
        return ospf;
 }
 
@@ -4154,7 +4165,7 @@ DEFUN (show_ip_ospf_interface_traffic,
 
 static void show_ip_ospf_neighbour_header(struct vty *vty)
 {
-       vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s\n",
+       vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-32s %5s %5s %5s\n",
                "Neighbor ID", "Pri", "State", "Dead Time", "Address",
                "Interface", "RXmtL", "RqstL", "DBsmL");
 }
@@ -4260,7 +4271,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty,
                                                        timebuf,
                                                        sizeof(timebuf)));
                                vty_out(vty, "%-15s ", inet_ntoa(nbr->src));
-                               vty_out(vty, "%-20s %5ld %5ld %5d\n",
+                               vty_out(vty, "%-32s %5ld %5ld %5d\n",
                                        IF_NAME(oi),
                                        ospf_ls_retransmit_count(nbr),
                                        ospf_ls_request_count(nbr),
@@ -4524,7 +4535,7 @@ static int show_ip_ospf_neighbor_all_common(struct vty *vty, struct ospf *ospf,
                                                "-", nbr_nbma->priority, "Down",
                                                "-");
                                        vty_out(vty,
-                                               "%-15s %-20s %5d %5d %5d\n",
+                                               "%-32s %-20s %5d %5d %5d\n",
                                                inet_ntoa(nbr_nbma->addr),
                                                IF_NAME(oi), 0, 0, 0);
                                }
@@ -9668,7 +9679,7 @@ DEFUN (show_ip_ospf_vrfs,
                if (uj)
                        json_vrf = json_object_new_object();
 
-               if (ospf->vrf_id == 0)
+               if (ospf->vrf_id == VRF_DEFAULT)
                        name = VRF_DEFAULT_NAME;
                else
                        name = ospf->name;
@@ -10358,9 +10369,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
        if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) {
                if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
                        vty_out(vty, " log-adjacency-changes detail\n");
-               else if (!DFLT_OSPF_LOG_ADJACENCY_CHANGES)
+               else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES)
                        vty_out(vty, " log-adjacency-changes\n");
-       } else if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) {
+       } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) {
                vty_out(vty, " no log-adjacency-changes\n");
        }
 
index 5058886f362b32465cef555088932276e72d061b..6a4e63372aedeab31919ccedc4981963b0ac9ab8 100644 (file)
@@ -293,11 +293,6 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
        new->oi_write_q = list_new();
        new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
 
-/* Enable "log-adjacency-changes" */
-#if DFLT_OSPF_LOG_ADJACENCY_CHANGES
-       SET_FLAG(new->config, OSPF_LOG_ADJACENCY_CHANGES);
-#endif
-
        QOBJ_REG(new, ospf);
 
        new->fd = -1;
@@ -368,7 +363,7 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name)
        return NULL;
 }
 
-struct ospf *ospf_get(unsigned short instance, const char *name)
+struct ospf *ospf_get(unsigned short instance, const char *name, bool *created)
 {
        struct ospf *ospf;
 
@@ -379,6 +374,7 @@ struct ospf *ospf_get(unsigned short instance, const char *name)
        else
                ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
 
+       *created = (ospf == NULL);
        if (ospf == NULL) {
                ospf = ospf_new(instance, name);
                ospf_add(ospf);
@@ -392,27 +388,18 @@ struct ospf *ospf_get(unsigned short instance, const char *name)
        return ospf;
 }
 
-struct ospf *ospf_get_instance(unsigned short instance)
+struct ospf *ospf_get_instance(unsigned short instance, bool *created)
 {
        struct ospf *ospf;
 
        ospf = ospf_lookup_instance(instance);
+       *created = (ospf == NULL);
        if (ospf == NULL) {
                ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/);
                ospf_add(ospf);
 
-               if (ospf->router_id_static.s_addr == 0) {
-                       if (vrf_lookup_by_id(ospf->vrf_id))
-                               ospf_router_id_update(ospf);
-                       else {
-                               if (IS_DEBUG_OSPF_EVENT)
-                                       zlog_debug(
-                                               "%s: ospf VRF (id %d) is not active yet, skip router id update",
-                                               __PRETTY_FUNCTION__,
-                                               ospf->vrf_id);
-                       }
+               if (ospf->router_id_static.s_addr == 0)
                        ospf_router_id_update(ospf);
-               }
 
                ospf_opaque_type11_lsa_init(ospf);
        }
@@ -584,7 +571,6 @@ static void ospf_finish_final(struct ospf *ospf)
        struct ospf_vl_data *vl_data;
        struct listnode *node, *nnode;
        int i;
-       unsigned short instance = 0;
 
        QOBJ_UNREG(ospf);
 
@@ -755,9 +741,6 @@ static void ospf_finish_final(struct ospf *ospf)
        ospf_distance_reset(ospf);
        route_table_finish(ospf->distance_table);
 
-       if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN))
-               instance = ospf->instance;
-
        list_delete(&ospf->areas);
        list_delete(&ospf->oi_write_q);
 
@@ -778,9 +761,6 @@ static void ospf_finish_final(struct ospf *ospf)
        }
 
        XFREE(MTYPE_OSPF_TOP, ospf);
-
-       if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN))
-               ospf_get_instance(instance);
 }
 
 
index 937d363b4c2030b293f006ec6889c6b5e0009072..5e91e6f8e6f73dcde6b513dfa9db1ba5c1d3e94e 100644 (file)
@@ -117,6 +117,14 @@ struct ospf_redist {
 #define ROUTEMAP(R)        (R->route_map.map)
 };
 
+/* ospf->config */
+enum {
+       OSPF_RFC1583_COMPATIBLE =       (1 << 0),
+       OSPF_OPAQUE_CAPABLE =           (1 << 2),
+       OSPF_LOG_ADJACENCY_CHANGES =    (1 << 3),
+       OSPF_LOG_ADJACENCY_DETAIL =     (1 << 4),
+};
+
 /* OSPF instance structure. */
 struct ospf {
        /* OSPF's running state based on the '[no] router ospf [<instance>]'
@@ -151,12 +159,8 @@ struct ospf {
        /* NSSA ABR */
        uint8_t anyNSSA; /* Bump for every NSSA attached. */
 
-       /* Configured variables. */
+       /* Configuration bitmask, refer to enum above */
        uint8_t config;
-#define OSPF_RFC1583_COMPATIBLE         (1 << 0)
-#define OSPF_OPAQUE_CAPABLE            (1 << 2)
-#define OSPF_LOG_ADJACENCY_CHANGES     (1 << 3)
-#define OSPF_LOG_ADJACENCY_DETAIL      (1 << 4)
 
        /* Opaque-LSA administrative flags. */
        uint8_t opaque;
@@ -500,8 +504,9 @@ extern struct zebra_privs_t ospfd_privs;
 /* Prototypes. */
 extern const char *ospf_redist_string(unsigned int route_type);
 extern struct ospf *ospf_lookup_instance(unsigned short);
-extern struct ospf *ospf_get(unsigned short instance, const char *name);
-extern struct ospf *ospf_get_instance(unsigned short);
+extern struct ospf *ospf_get(unsigned short instance, const char *name,
+                            bool *created);
+extern struct ospf *ospf_get_instance(unsigned short, bool *created);
 extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance,
                                             const char *name);
 extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id);
index 48dd741b24639d6121179ea9e9997bec0339857c..6de4099c5b8ac33500e3f7f23ae3045866685456 100644 (file)
@@ -19,7 +19,7 @@ vtysh_scan += \
 if SNMP
 module_LTLIBRARIES += ospfd/ospfd_snmp.la
 endif
-man8 += $(MANBUILD)/ospfd.8
+man8 += $(MANBUILD)/frr-ospfd.8
 endif
 
 ospfd_libfrrospf_a_SOURCES = \
index 7e34066b47b32861a428030df57e3e74a4dbc483..37b6be694931738dbc069b874725572f9bf860e5 100644 (file)
@@ -100,6 +100,37 @@ static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi)
        }
 }
 
+static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms,
+                              const struct pbr_map_interface *pmi)
+{
+       uint64_t is_installed = (uint64_t)1 << pmi->install_bit;
+
+       is_installed &= pbrms->installed;
+
+       if (is_installed)
+               return true;
+
+       return false;
+}
+
+/* If any sequence is installed on the interface, assume installed */
+static bool
+pbr_map_interface_is_installed(const struct pbr_map *pbrm,
+                              const struct pbr_map_interface *check_pmi)
+{
+
+       struct pbr_map_sequence *pbrms;
+       struct pbr_map_interface *pmi;
+       struct listnode *node, *inode;
+
+       for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+               for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+                       if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi))
+                               return true;
+
+       return false;
+}
+
 static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi)
 {
        /* Don't install rules without a real ifindex on the incoming interface.
@@ -171,7 +202,7 @@ void pbr_map_reason_string(unsigned int reason, char *buf, int size)
 void pbr_map_final_interface_deletion(struct pbr_map *pbrm,
                                      struct pbr_map_interface *pmi)
 {
-       if (pmi->delete == true) {
+       if (pmi->delete == true && !pbr_map_interface_is_installed(pbrm, pmi)) {
                listnode_delete(pbrm->incoming, pmi);
                pmi->pbrm = NULL;
 
index bc4aa947a9c07922566af576d3d0e9ddfff361ef..c1922d87288a1020132d697b26e5f112b509a7a1 100644 (file)
@@ -127,11 +127,16 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
        pbrms->family = prefix->family;
 
        if (!no) {
-               if (prefix_same(pbrms->src, prefix))
-                       return CMD_SUCCESS;
+               if (pbrms->src) {
+                       if (prefix_same(pbrms->src, prefix))
+                               return CMD_SUCCESS;
+
+                       vty_out(vty,
+                               "A `match src-ip XX` command already exists, please remove that first\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
 
-               if (!pbrms->src)
-                       pbrms->src = prefix_new();
+               pbrms->src = prefix_new();
                prefix_copy(pbrms->src, prefix);
        } else
                prefix_free(&pbrms->src);
@@ -145,7 +150,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
        "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
        NO_STR
        "Match the rest of the command\n"
-       "Choose the src ip or ipv6 prefix to use\n"
+       "Choose the dst ip or ipv6 prefix to use\n"
        "v4 Prefix\n"
        "v6 Prefix\n")
 {
@@ -154,11 +159,16 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
        pbrms->family = prefix->family;
 
        if (!no) {
-               if (prefix_same(pbrms->dst, prefix))
-                       return CMD_SUCCESS;
+               if (pbrms->dst) {
+                       if (prefix_same(pbrms->dst, prefix))
+                               return CMD_SUCCESS;
 
-               if (!pbrms->dst)
-                       pbrms->dst = prefix_new();
+                       vty_out(vty,
+                               "A `match dst-ip XX` command already exists, please remove that first\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+
+               pbrms->dst = prefix_new();
                prefix_copy(pbrms->dst, prefix);
        } else
                prefix_free(&pbrms->dst);
@@ -183,12 +193,18 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
 #endif
 
        if (!no) {
-               if (pbrms->mark == (uint32_t) mark)
-                       return CMD_SUCCESS;
-               pbrms->mark = (uint32_t) mark;
-       } else {
+               if (pbrms->mark) {
+                       if (pbrms->mark == (uint32_t)mark)
+                               return CMD_SUCCESS;
+
+                       vty_out(vty,
+                               "A `match mark XX` command already exists, please remove that first\n");
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+
+               pbrms->mark = (uint32_t)mark;
+       } else
                pbrms->mark = 0;
-       }
 
        pbr_map_check(pbrms);
 
@@ -232,7 +248,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
                        pbr_map_delete_nexthops(pbrms);
                else {
                        vty_out(vty,
-                               "Nexthop Group specified: %s does not exist to remove",
+                               "Nexthop Group specified: %s does not exist to remove\n",
                                name);
                        return CMD_WARNING_CONFIG_FAILED;
                }
@@ -240,7 +256,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
                if (pbrms->nhgrp_name) {
                        if (strcmp(name, pbrms->nhgrp_name) != 0) {
                                vty_out(vty,
-                                       "Please delete current nexthop group before modifying current one");
+                                       "Please delete current nexthop group before modifying current one\n");
                                return CMD_WARNING_CONFIG_FAILED;
                        }
 
@@ -277,7 +293,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
 
        if (pbrms->nhgrp_name) {
                vty_out(vty,
-                       "Please unconfigure the nexthop group before adding an individual nexthop");
+                       "Please unconfigure the nexthop group before adding an individual nexthop\n");
                return CMD_WARNING_CONFIG_FAILED;
        }
 
@@ -359,7 +375,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
 
                if (pbrms->nhg->nexthop) {
                        vty_out(vty,
-                               "If you would like more than one nexthop please use nexthop-groups");
+                               "If you would like more than one nexthop please use nexthop-groups\n");
                        return CMD_WARNING_CONFIG_FAILED;
                }
 
@@ -508,6 +524,88 @@ DEFPY (show_pbr,
        return CMD_SUCCESS;
 }
 
+static void vty_show_pbrms(struct vty *vty,
+                          const struct pbr_map_sequence *pbrms, bool detail)
+{
+       char buf[PREFIX_STRLEN];
+       char rbuf[64];
+
+       if (pbrms->reason)
+               pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
+
+       vty_out(vty, "    Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno);
+
+       if (detail)
+               vty_out(vty, "        Installed: %" PRIu64 "(%u) Reason: %s\n",
+                       pbrms->installed, pbrms->unique,
+                       pbrms->reason ? rbuf : "Valid");
+       else
+               vty_out(vty, "        Installed: %s Reason: %s\n",
+                       pbrms->installed ? "yes" : "no",
+                       pbrms->reason ? rbuf : "Valid");
+
+       if (pbrms->src)
+               vty_out(vty, "        SRC Match: %s\n",
+                       prefix2str(pbrms->src, buf, sizeof(buf)));
+       if (pbrms->dst)
+               vty_out(vty, "        DST Match: %s\n",
+                       prefix2str(pbrms->dst, buf, sizeof(buf)));
+       if (pbrms->mark)
+               vty_out(vty, "        MARK Match: %u\n", pbrms->mark);
+
+       if (pbrms->nhgrp_name) {
+               vty_out(vty, "        Nexthop-Group: %s\n", pbrms->nhgrp_name);
+
+               if (detail)
+                       vty_out(vty,
+                               "          Installed: %u(%d) Tableid: %d\n",
+                               pbrms->nhs_installed,
+                               pbr_nht_get_installed(pbrms->nhgrp_name),
+                               pbr_nht_get_table(pbrms->nhgrp_name));
+               else
+                       vty_out(vty, "          Installed: %s Tableid: %d\n",
+                               pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
+                                                                        : "no",
+                               pbr_nht_get_table(pbrms->nhgrp_name));
+
+       } else if (pbrms->nhg) {
+               vty_out(vty, "        ");
+               nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
+               if (detail)
+                       vty_out(vty,
+                               "          Installed: %u(%d) Tableid: %d\n",
+                               pbrms->nhs_installed,
+                               pbr_nht_get_installed(pbrms->internal_nhg_name),
+                               pbr_nht_get_table(pbrms->internal_nhg_name));
+               else
+                       vty_out(vty, "          Installed: %s Tableid: %d\n",
+                               pbr_nht_get_installed(pbrms->internal_nhg_name)
+                                       ? "yes"
+                                       : "no",
+                               pbr_nht_get_table(pbrms->internal_nhg_name));
+
+       } else if (pbrms->vrf_unchanged) {
+               vty_out(vty, "        VRF Unchanged (use interface vrf)\n");
+       } else if (pbrms->vrf_lookup) {
+               vty_out(vty, "        VRF Lookup: %s\n", pbrms->vrf_name);
+       } else {
+               vty_out(vty, "        Nexthop-Group: Unknown Installed: no\n");
+       }
+}
+
+static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
+                            bool detail)
+{
+       struct pbr_map_sequence *pbrms;
+       struct listnode *node;
+
+       vty_out(vty, "  pbr-map %s valid: %s\n", pbrm->name,
+               pbrm->valid ? "yes" : "no");
+
+       for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+               vty_show_pbrms(vty, pbrms, detail);
+}
+
 DEFPY (show_pbr_map,
        show_pbr_map_cmd,
        "show pbr map [NAME$name] [detail$detail]",
@@ -517,69 +615,13 @@ DEFPY (show_pbr_map,
        "PBR Map Name\n"
        "Detailed information\n")
 {
-       struct pbr_map_sequence *pbrms;
        struct pbr_map *pbrm;
-       struct listnode *node;
-       char buf[PREFIX_STRLEN];
-       char rbuf[64];
 
        RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
                if (name && strcmp(name, pbrm->name) != 0)
                        continue;
 
-               vty_out(vty, "  pbr-map %s valid: %d\n", pbrm->name,
-                       pbrm->valid);
-
-               for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
-                       if (pbrms->reason)
-                               pbr_map_reason_string(pbrms->reason, rbuf,
-                                                     sizeof(rbuf));
-                       vty_out(vty,
-                               "    Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
-                               pbrms->seqno, pbrms->ruleno, pbrms->installed,
-                               pbrms->unique, pbrms->reason ? rbuf : "Valid");
-
-                       if (pbrms->src)
-                               vty_out(vty, "\tSRC Match: %s\n",
-                                       prefix2str(pbrms->src, buf,
-                                                  sizeof(buf)));
-                       if (pbrms->dst)
-                               vty_out(vty, "\tDST Match: %s\n",
-                                       prefix2str(pbrms->dst, buf,
-                                                  sizeof(buf)));
-                       if (pbrms->mark)
-                               vty_out(vty, "\tMARK Match: %u\n", pbrms->mark);
-
-                       if (pbrms->nhgrp_name) {
-                               vty_out(vty,
-                                       "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
-                                       pbrms->nhgrp_name,
-                                       pbr_nht_get_table(pbrms->nhgrp_name),
-                                       pbrms->nhs_installed,
-                                       pbr_nht_get_installed(
-                                               pbrms->nhgrp_name));
-                       } else if (pbrms->nhg) {
-                               vty_out(vty, "     ");
-                               nexthop_group_write_nexthop(
-                                       vty, pbrms->nhg->nexthop);
-                               vty_out(vty,
-                                       "\tInstalled: %u(%d) Tableid: %d\n",
-                                       pbrms->nhs_installed,
-                                       pbr_nht_get_installed(
-                                               pbrms->internal_nhg_name),
-                                       pbr_nht_get_table(
-                                               pbrms->internal_nhg_name));
-                       } else if (pbrms->vrf_unchanged) {
-                               vty_out(vty,
-                                       "\tVRF Unchanged (use interface vrf)\n");
-                       } else if (pbrms->vrf_lookup) {
-                               vty_out(vty, "\tVRF Lookup: %s\n",
-                                       pbrms->vrf_name);
-                       } else {
-                               vty_out(vty,
-                                       "\tNexthop-Group: Unknown Installed: 0(0)\n");
-                       }
-               }
+               vty_show_pbr_map(vty, pbrm, detail);
        }
        return CMD_SUCCESS;
 }
index b0a689a7e4b428edeb3ba075e3bcce6cff5c9bb8..ec0327d74f8f69ab7574a450fe7e04c7e8c9f539 100644 (file)
@@ -234,23 +234,21 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
        switch (note) {
        case ZAPI_RULE_FAIL_INSTALL:
                pbrms->installed &= ~installed;
-               DEBUGD(&pbr_dbg_zebra,
-                      "%s: Received RULE_FAIL_INSTALL: %" PRIu64,
-                      __PRETTY_FUNCTION__, pbrms->installed);
                break;
        case ZAPI_RULE_INSTALLED:
                pbrms->installed |= installed;
-               DEBUGD(&pbr_dbg_zebra, "%s: Received RULE_INSTALLED: %" PRIu64,
-                      __PRETTY_FUNCTION__, pbrms->installed);
                break;
        case ZAPI_RULE_FAIL_REMOVE:
+               /* Don't change state on rule removal failure */
+               break;
        case ZAPI_RULE_REMOVED:
                pbrms->installed &= ~installed;
-               DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED: %" PRIu64,
-                      __PRETTY_FUNCTION__, pbrms->installed);
                break;
        }
 
+       DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__,
+              zapi_rule_notify_owner2str(note), pbrms->installed);
+
        pbr_map_final_interface_deletion(pbrms->parent, pmi);
 
        return 0;
@@ -281,6 +279,7 @@ static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg,
                api_nh = &api->nexthops[i];
                api_nh->vrf_id = nhop->vrf_id;
                api_nh->type = nhop->type;
+               api_nh->weight = nhop->weight;
                switch (nhop->type) {
                case NEXTHOP_TYPE_IPV4:
                        api_nh->gate.ipv4 = nhop->gate.ipv4;
index 41d0e5a0b88b92ee73a1396bd35576d06fd2b224..c55f0b41cc8be1b9a2dc1bb0b042842433ea5b6b 100644 (file)
@@ -10,7 +10,7 @@ vtysh_scan += \
        $(top_srcdir)/pbrd/pbr_vty.c \
        $(top_srcdir)/pbrd/pbr_debug.c \
        # end
-man8 += $(MANBUILD)/pbrd.8
+man8 += $(MANBUILD)/frr-pbrd.8
 endif
 
 pbrd_libpbr_a_SOURCES = \
index ca86017f105acedb80f37f99c9380df4842a38cb..01bebebd290215e991f06fe213073e9d55c75c67 100644 (file)
@@ -914,7 +914,6 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
        struct in_addr ifaddr;
        struct interface *ifp;
        struct listnode *neighnode;
-       struct listnode *upnode;
        struct pim_interface *pim_ifp;
        struct pim_neighbor *neigh;
        struct pim_upstream *up;
@@ -1052,8 +1051,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
                                            pim_ifp->pim_dr_election_changes);
 
                        // FHR
-                       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
-                                                 up)) {
+                       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                                if (ifp != up->rpf.source_nexthop.interface)
                                        continue;
 
@@ -1215,8 +1213,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
 
                        // FHR
                        print_header = 1;
-                       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
-                                                 up)) {
+                       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                                if (!up->rpf.source_nexthop.interface)
                                        continue;
 
@@ -1386,7 +1383,6 @@ static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty,
                                bool uj)
 {
        struct interface *ifp;
-       struct listnode *upnode;
        struct pim_interface *pim_ifp;
        struct pim_upstream *up;
        int fhr = 0;
@@ -1408,7 +1404,7 @@ static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty,
                pim_ifchannels = pim_if_ifchannel_count(pim_ifp);
                fhr = 0;
 
-               for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up))
+               frr_each (rb_pim_upstream, &pim->upstream_head, up)
                        if (ifp == up->rpf.source_nexthop.interface)
                                if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
                                        fhr++;
@@ -1975,7 +1971,6 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                           const char *src_or_group, const char *group, bool uj)
 {
        struct channel_oil *c_oil;
-       struct listnode *node;
        json_object *json = NULL;
        json_object *json_group = NULL;
        json_object *json_ifp_in = NULL;
@@ -1989,20 +1984,28 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                json = json_object_new_object();
        } else {
                vty_out(vty,
-                       "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN");
+                       "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted");
                vty_out(vty,
-                       "\nInstalled Source           Group            IIF               OIL\n");
+                       "\nActive Source           Group            RPT  IIF               OIL\n");
        }
 
-       for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+       frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
                char grp_str[INET_ADDRSTRLEN];
                char src_str[INET_ADDRSTRLEN];
                char in_ifname[INTERFACE_NAMSIZ + 1];
                char out_ifname[INTERFACE_NAMSIZ + 1];
                int oif_vif_index;
                struct interface *ifp_in;
+               bool isRpt;
                first_oif = 1;
 
+               if ((c_oil->up &&
+                       PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) ||
+                       c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+                       isRpt = true;
+               else
+                       isRpt = false;
+
                pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str,
                               sizeof(grp_str));
                pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str,
@@ -2056,6 +2059,12 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                                                       json_ifp_in);
                                json_object_int_add(json_source, "Installed",
                                                    c_oil->installed);
+                               if (isRpt)
+                                       json_object_boolean_true_add(
+                                                       json_source, "isRpt");
+                               else
+                                       json_object_boolean_false_add(
+                                                       json_source, "isRpt");
                                json_object_int_add(json_source, "RefCount",
                                                    c_oil->oil_ref_count);
                                json_object_int_add(json_source, "OilListSize",
@@ -2074,8 +2083,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                                                    c_oil->cc.wrong_if);
                        }
                } else {
-                       vty_out(vty, "%-9d %-15s  %-15s  %-16s  ",
-                               c_oil->installed, src_str, grp_str, in_ifname);
+                       vty_out(vty, "%-6d %-15s  %-15s  %-3s  %-16s  ",
+                                       c_oil->installed, src_str, grp_str,
+                                       isRpt ? "y" : "n", in_ifname);
                }
 
                for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
@@ -2118,7 +2128,8 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                        } else {
                                if (first_oif) {
                                        first_oif = 0;
-                                       vty_out(vty, "%s(%c%c%c%c)", out_ifname,
+                                       vty_out(vty, "%s(%c%c%c%c%c)",
+                                               out_ifname,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_IGMP)
                                                        ? 'I'
@@ -2134,9 +2145,13 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_STAR)
                                                        ? '*'
+                                                       : ' ',
+                                               (c_oil->oif_flags[oif_vif_index]
+                                                & PIM_OIF_FLAG_MUTE)
+                                                       ? 'M'
                                                        : ' ');
                                } else
-                                       vty_out(vty, ", %s(%c%c%c%c)",
+                                       vty_out(vty, ", %s(%c%c%c%c%c)",
                                                out_ifname,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_IGMP)
@@ -2153,6 +2168,10 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
                                                (c_oil->oif_flags[oif_vif_index]
                                                 & PIM_OIF_FLAG_PROTO_STAR)
                                                        ? '*'
+                                                       : ' ',
+                                               (c_oil->oif_flags[oif_vif_index]
+                                                & PIM_OIF_FLAG_MUTE)
+                                                       ? 'M'
                                                        : ' ');
                        }
                }
@@ -2405,7 +2424,6 @@ static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state,
 static void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
                              struct prefix_sg *sg, bool uj)
 {
-       struct listnode *upnode;
        struct pim_upstream *up;
        time_t now;
        json_object *json = NULL;
@@ -2420,7 +2438,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
                vty_out(vty,
                        "Iif             Source          Group           State       Uptime   JoinTimer RSTimer   KATimer   RefCnt\n");
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                char src_str[INET_ADDRSTRLEN];
                char grp_str[INET_ADDRSTRLEN];
                char uptime[10];
@@ -2554,7 +2572,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
        }
 }
 
-static void pim_show_join_desired_helper(struct pim_instance *pim,
+static void pim_show_channel_helper(struct pim_instance *pim,
                                         struct vty *vty,
                                         struct pim_interface *pim_ifp,
                                         struct pim_ifchannel *ch,
@@ -2613,7 +2631,7 @@ static void pim_show_join_desired_helper(struct pim_instance *pim,
        }
 }
 
-static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
+static void pim_show_channel(struct pim_instance *pim, struct vty *vty,
                                  bool uj)
 {
        struct pim_interface *pim_ifp;
@@ -2637,7 +2655,7 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
 
                RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
                        /* scan all interfaces */
-                       pim_show_join_desired_helper(pim, vty, pim_ifp, ch,
+                       pim_show_channel_helper(pim, vty, pim_ifp, ch,
                                                     json, uj);
                }
        }
@@ -2649,10 +2667,75 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
        }
 }
 
+static void pim_show_join_desired_helper(struct pim_instance *pim,
+                                        struct vty *vty,
+                                        struct pim_upstream *up,
+                                        json_object *json, bool uj)
+{
+       json_object *json_group = NULL;
+       char src_str[INET_ADDRSTRLEN];
+       char grp_str[INET_ADDRSTRLEN];
+       json_object *json_row = NULL;
+
+       pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+       pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+
+       if (uj) {
+               json_object_object_get_ex(json, grp_str, &json_group);
+
+               if (!json_group) {
+                       json_group = json_object_new_object();
+                       json_object_object_add(json, grp_str, json_group);
+               }
+
+               json_row = json_object_new_object();
+               json_object_pim_upstream_add(json_row, up);
+               json_object_string_add(json_row, "source", src_str);
+               json_object_string_add(json_row, "group", grp_str);
+
+               if (pim_upstream_evaluate_join_desired(pim, up))
+                       json_object_boolean_true_add(json_row,
+                                                    "evaluateJoinDesired");
+
+               json_object_object_add(json_group, src_str, json_row);
+
+       } else {
+               vty_out(vty, "%-15s %-15s %-6s\n",
+                       src_str, grp_str,
+                       pim_upstream_evaluate_join_desired(pim, up) ? "yes"
+                                                                   : "no");
+       }
+}
+
+static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
+                                 bool uj)
+{
+       struct pim_upstream *up;
+
+       json_object *json = NULL;
+
+       if (uj)
+               json = json_object_new_object();
+       else
+               vty_out(vty,
+                       "Source          Group           EvalJD\n");
+
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+               /* scan all interfaces */
+               pim_show_join_desired_helper(pim, vty, up,
+                               json, uj);
+       }
+
+       if (uj) {
+               vty_out(vty, "%s\n", json_object_to_json_string_ext(
+                                            json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
+       }
+}
+
 static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty,
                                  bool uj)
 {
-       struct listnode *upnode;
        struct pim_upstream *up;
        json_object *json = NULL;
        json_object *json_group = NULL;
@@ -2664,7 +2747,7 @@ static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty,
                vty_out(vty,
                        "Source          Group           RpfIface         RibNextHop      RpfAddress     \n");
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                char src_str[INET_ADDRSTRLEN];
                char grp_str[INET_ADDRSTRLEN];
                char rpf_nexthop_str[PREFIX_STRLEN];
@@ -2785,7 +2868,6 @@ static void show_scan_oil_stats(struct pim_instance *pim, struct vty *vty,
 
 static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj)
 {
-       struct listnode *up_node;
        struct pim_upstream *up;
        time_t now = pim_time_monotonic_sec();
        json_object *json = NULL;
@@ -2802,7 +2884,7 @@ static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj)
                        "Source          Group           RpfIface         RpfAddress      RibNextHop      Metric Pref\n");
        }
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                char src_str[INET_ADDRSTRLEN];
                char grp_str[INET_ADDRSTRLEN];
                char rpf_addr_str[PREFIX_STRLEN];
@@ -3842,11 +3924,8 @@ static void clear_mroute(struct pim_instance *pim)
        }
 
        /* clean up all upstreams*/
-       if (pim->upstream_list) {
-               while (pim->upstream_list->count) {
-                       up = listnode_head(pim->upstream_list);
-                       pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
-               }
+       while ((up = rb_pim_upstream_first(&pim->upstream_head))) {
+               pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
        }
 }
 
@@ -4438,6 +4517,90 @@ DEFUN (show_ip_pim_join_vrf_all,
        return CMD_WARNING;
 }
 
+static void pim_show_jp_agg_helper(struct vty *vty,
+               struct interface *ifp,
+               struct pim_neighbor *neigh,
+               struct pim_upstream *up,
+               int is_join)
+{
+       char src_str[INET_ADDRSTRLEN];
+       char grp_str[INET_ADDRSTRLEN];
+       char rpf_str[INET_ADDRSTRLEN];
+
+       pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+       pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+                       /* pius->address.s_addr */
+       pim_inet4_dump("<rpf?>", neigh->source_addr, rpf_str, sizeof(rpf_str));
+
+       vty_out(vty, "%-16s %-15s %-15s %-15s %5s\n",
+                       ifp->name, rpf_str, src_str,
+                       grp_str, is_join?"J":"P");
+}
+
+static void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty)
+{
+       struct interface *ifp;
+       struct pim_interface *pim_ifp;
+       struct listnode *n_node;
+       struct pim_neighbor *neigh;
+       struct listnode *jag_node;
+       struct pim_jp_agg_group *jag;
+       struct listnode *js_node;
+       struct pim_jp_sources *js;
+
+       vty_out(vty,
+                       "Interface        RPF Nbr         Source          Group           State\n");
+
+       FOR_ALL_INTERFACES (pim->vrf, ifp) {
+               pim_ifp = ifp->info;
+               if (!pim_ifp)
+                       continue;
+
+               for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
+                                       n_node, neigh)) {
+                       for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg,
+                                               jag_node, jag)) {
+                               for (ALL_LIST_ELEMENTS_RO(jag->sources,
+                                                       js_node, js)) {
+                                       pim_show_jp_agg_helper(vty,
+                                                       ifp, neigh, js->up,
+                                                       js->is_join);
+                               }
+                       }
+               }
+       }
+}
+
+DEFPY (show_ip_pim_jp_agg,
+       show_ip_pim_jp_agg_cmd,
+       "show ip pim [vrf NAME] jp-agg",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       VRF_CMD_HELP_STR
+       "join prune aggregation list\n")
+{
+       struct vrf *v;
+       struct pim_instance *pim;
+
+       v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
+
+       if (!v) {
+               vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
+               return CMD_WARNING;
+       }
+       pim = pim_get_pim_instance(v->vrf_id);
+
+       if (!pim) {
+               vty_out(vty, "%% Unable to find pim instance\n");
+               return CMD_WARNING;
+       }
+
+       pim_show_jp_agg_list(pim, vty);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (show_ip_pim_local_membership,
        show_ip_pim_local_membership_cmd,
        "show ip pim [vrf NAME] local-membership [json]",
@@ -4702,6 +4865,28 @@ DEFUN (show_ip_pim_upstream_vrf_all,
        return CMD_SUCCESS;
 }
 
+DEFUN (show_ip_pim_channel,
+       show_ip_pim_channel_cmd,
+       "show ip pim [vrf NAME] channel [json]",
+       SHOW_STR
+       IP_STR
+       PIM_STR
+       VRF_CMD_HELP_STR
+       "PIM downstream channel info\n"
+       JSON_STR)
+{
+       int idx = 2;
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       bool uj = use_json(argc, argv);
+
+       if (!vrf)
+               return CMD_WARNING;
+
+       pim_show_channel(vrf->info, vty, uj);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (show_ip_pim_upstream_join_desired,
        show_ip_pim_upstream_join_desired_cmd,
        "show ip pim [vrf NAME] upstream-join-desired [json]",
@@ -5223,7 +5408,7 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty,
        now = pim_time_monotonic_sec();
 
        /* print list of PIM and IGMP routes */
-       for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+       frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
                found_oif = 0;
                first = 1;
                if (!c_oil->installed && !uj)
@@ -5293,6 +5478,16 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty,
                        if (ttl < 1)
                                continue;
 
+                       /* do not display muted OIFs */
+                       if (c_oil->oif_flags[oif_vif_index]
+                                       & PIM_OIF_FLAG_MUTE)
+                               continue;
+
+                       if (c_oil->oil.mfcc_parent == oif_vif_index &&
+                                       !pim_mroute_allow_iif_in_oil(c_oil,
+                                               oif_vif_index))
+                               continue;
+
                        ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index);
                        pim_time_uptime(
                                mroute_uptime, sizeof(mroute_uptime),
@@ -5621,7 +5816,7 @@ DEFUN (clear_ip_mroute_count,
                return CMD_WARNING;
 
        pim = vrf->info;
-       for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+       frr_each(rb_pim_oil, &pim->channel_oil_head, c_oil) {
                if (!c_oil->installed)
                        continue;
 
@@ -5656,7 +5851,7 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty)
                "Source          Group           LastUsed Packets Bytes WrongIf  \n");
 
        /* Print PIM and IGMP route counts */
-       for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+       frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
                char group_str[INET_ADDRSTRLEN];
                char source_str[INET_ADDRSTRLEN];
 
@@ -5761,7 +5956,7 @@ static void show_mroute_summary(struct pim_instance *pim, struct vty *vty)
 
        vty_out(vty, "Mroute Type    Installed/Total\n");
 
-       for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+       frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
                if (!c_oil->installed) {
                        if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
                                starg_sw_mroute_cnt++;
@@ -6708,7 +6903,7 @@ DEFUN (interface_no_ip_igmp,
 
 DEFUN (interface_ip_igmp_join,
        interface_ip_igmp_join_cmd,
-       "ip igmp join A.B.C.D A.B.C.D",
+       "ip igmp join A.B.C.D [A.B.C.D]",
        IP_STR
        IFACE_IGMP_STR
        "IGMP join multicast group\n"
@@ -6734,12 +6929,21 @@ DEFUN (interface_ip_igmp_join,
        }
 
        /* Source address */
-       source_str = argv[idx_ipv4_2]->arg;
-       result = inet_pton(AF_INET, source_str, &source_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad source address %s: errno=%d: %s\n",
-                       source_str, errno, safe_strerror(errno));
-               return CMD_WARNING_CONFIG_FAILED;
+       if (argc == (idx_ipv4_2 + 1)) {
+               source_str = argv[idx_ipv4_2]->arg;
+               result = inet_pton(AF_INET, source_str, &source_addr);
+               if (result <= 0) {
+                       vty_out(vty, "Bad source address %s: errno=%d: %s\n",
+                               source_str, errno, safe_strerror(errno));
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               /* Reject 0.0.0.0. Reserved for any source. */
+               if (source_addr.s_addr == INADDR_ANY) {
+                       vty_out(vty, "Bad source address %s\n", source_str);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       } else {
+               source_addr.s_addr = INADDR_ANY;
        }
 
        CMD_FERR_RETURN(pim_if_igmp_join_add(ifp, group_addr, source_addr),
@@ -6750,7 +6954,7 @@ DEFUN (interface_ip_igmp_join,
 
 DEFUN (interface_no_ip_igmp_join,
        interface_no_ip_igmp_join_cmd,
-       "no ip igmp join A.B.C.D A.B.C.D",
+       "no ip igmp join A.B.C.D [A.B.C.D]",
        NO_STR
        IP_STR
        IFACE_IGMP_STR
@@ -6777,12 +6981,22 @@ DEFUN (interface_no_ip_igmp_join,
        }
 
        /* Source address */
-       source_str = argv[idx_ipv4_2]->arg;
-       result = inet_pton(AF_INET, source_str, &source_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad source address %s: errno=%d: %s\n",
-                       source_str, errno, safe_strerror(errno));
-               return CMD_WARNING_CONFIG_FAILED;
+       if (argc == (idx_ipv4_2 + 1)) {
+               source_str = argv[idx_ipv4_2]->arg;
+               result = inet_pton(AF_INET, source_str, &source_addr);
+               if (result <= 0) {
+                       vty_out(vty, "Bad source address %s: errno=%d: %s\n",
+                               source_str, errno, safe_strerror(errno));
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               /* Reject 0.0.0.0. Reserved for any source. */
+               if (source_addr.s_addr == INADDR_ANY) {
+                       vty_out(vty, "Bad source address %s\n", source_str);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       } else {
+               source_str = "*";
+               source_addr.s_addr = INADDR_ANY;
        }
 
        result = pim_if_igmp_join_del(ifp, group_addr, source_addr);
@@ -7647,55 +7861,7 @@ DEFUN(interface_no_ip_pim_boundary_oil,
 
 DEFUN (interface_ip_mroute,
        interface_ip_mroute_cmd,
-       "ip mroute INTERFACE A.B.C.D",
-       IP_STR
-       "Add multicast route\n"
-       "Outgoing interface name\n"
-       "Group address\n")
-{
-       VTY_DECLVAR_CONTEXT(interface, iif);
-       struct pim_interface *pim_ifp;
-       struct pim_instance *pim;
-       int idx_interface = 2;
-       int idx_ipv4 = 3;
-       struct interface *oif;
-       const char *oifname;
-       const char *grp_str;
-       struct in_addr grp_addr;
-       struct in_addr src_addr;
-       int result;
-
-       PIM_GET_PIM_INTERFACE(pim_ifp, iif);
-       pim = pim_ifp->pim;
-
-       oifname = argv[idx_interface]->arg;
-       oif = if_lookup_by_name(oifname, pim->vrf_id);
-       if (!oif) {
-               vty_out(vty, "No such interface name %s\n", oifname);
-               return CMD_WARNING;
-       }
-
-       grp_str = argv[idx_ipv4]->arg;
-       result = inet_pton(AF_INET, grp_str, &grp_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str,
-                       errno, safe_strerror(errno));
-               return CMD_WARNING;
-       }
-
-       src_addr.s_addr = INADDR_ANY;
-
-       if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) {
-               vty_out(vty, "Failed to add route\n");
-               return CMD_WARNING;
-       }
-
-       return CMD_SUCCESS;
-}
-
-DEFUN (interface_ip_mroute_source,
-       interface_ip_mroute_source_cmd,
-       "ip mroute INTERFACE A.B.C.D A.B.C.D",
+       "ip mroute INTERFACE A.B.C.D [A.B.C.D]",
        IP_STR
        "Add multicast route\n"
        "Outgoing interface name\n"
@@ -7707,7 +7873,6 @@ DEFUN (interface_ip_mroute_source,
        struct pim_instance *pim;
        int idx_interface = 2;
        int idx_ipv4 = 3;
-       int idx_ipv4_2 = 4;
        struct interface *oif;
        const char *oifname;
        const char *grp_str;
@@ -7734,16 +7899,21 @@ DEFUN (interface_ip_mroute_source,
                return CMD_WARNING;
        }
 
-       src_str = argv[idx_ipv4_2]->arg;
-       result = inet_pton(AF_INET, src_str, &src_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str,
-                       errno, safe_strerror(errno));
-               return CMD_WARNING;
-       }
+        if (argc == (idx_ipv4 + 1)) {
+                src_addr.s_addr = INADDR_ANY;
+        }
+        else {
+                src_str = argv[idx_ipv4 + 1]->arg;
+                result = inet_pton(AF_INET, src_str, &src_addr);
+                if (result <= 0) {
+                        vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str,
+                                errno, safe_strerror(errno));
+                        return CMD_WARNING;
+                }
+        }
 
        if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) {
-               vty_out(vty, "Failed to add route\n");
+               vty_out(vty, "Failed to add static mroute\n");
                return CMD_WARNING;
        }
 
@@ -7752,56 +7922,7 @@ DEFUN (interface_ip_mroute_source,
 
 DEFUN (interface_no_ip_mroute,
        interface_no_ip_mroute_cmd,
-       "no ip mroute INTERFACE A.B.C.D",
-       NO_STR
-       IP_STR
-       "Add multicast route\n"
-       "Outgoing interface name\n"
-       "Group Address\n")
-{
-       VTY_DECLVAR_CONTEXT(interface, iif);
-       struct pim_interface *pim_ifp;
-       struct pim_instance *pim;
-       int idx_interface = 3;
-       int idx_ipv4 = 4;
-       struct interface *oif;
-       const char *oifname;
-       const char *grp_str;
-       struct in_addr grp_addr;
-       struct in_addr src_addr;
-       int result;
-
-       PIM_GET_PIM_INTERFACE(pim_ifp, iif);
-       pim = pim_ifp->pim;
-
-       oifname = argv[idx_interface]->arg;
-       oif = if_lookup_by_name(oifname, pim->vrf_id);
-       if (!oif) {
-               vty_out(vty, "No such interface name %s\n", oifname);
-               return CMD_WARNING;
-       }
-
-       grp_str = argv[idx_ipv4]->arg;
-       result = inet_pton(AF_INET, grp_str, &grp_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str,
-                       errno, safe_strerror(errno));
-               return CMD_WARNING;
-       }
-
-       src_addr.s_addr = INADDR_ANY;
-
-       if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) {
-               vty_out(vty, "Failed to remove route\n");
-               return CMD_WARNING;
-       }
-
-       return CMD_SUCCESS;
-}
-
-DEFUN (interface_no_ip_mroute_source,
-       interface_no_ip_mroute_source_cmd,
-       "no ip mroute INTERFACE A.B.C.D A.B.C.D",
+       "no ip mroute INTERFACE A.B.C.D [A.B.C.D]",
        NO_STR
        IP_STR
        "Add multicast route\n"
@@ -7814,7 +7935,6 @@ DEFUN (interface_no_ip_mroute_source,
        struct pim_instance *pim;
        int idx_interface = 3;
        int idx_ipv4 = 4;
-       int idx_ipv4_2 = 5;
        struct interface *oif;
        const char *oifname;
        const char *grp_str;
@@ -7841,16 +7961,21 @@ DEFUN (interface_no_ip_mroute_source,
                return CMD_WARNING;
        }
 
-       src_str = argv[idx_ipv4_2]->arg;
-       result = inet_pton(AF_INET, src_str, &src_addr);
-       if (result <= 0) {
-               vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str,
-                       errno, safe_strerror(errno));
-               return CMD_WARNING;
-       }
+        if (argc == (idx_ipv4 + 1)) {
+                src_addr.s_addr = INADDR_ANY;
+        }
+        else {
+                src_str = argv[idx_ipv4 + 1]->arg;
+                result = inet_pton(AF_INET, src_str, &src_addr);
+                if (result <= 0) {
+                        vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str,
+                                errno, safe_strerror(errno));
+                        return CMD_WARNING;
+                }
+        }
 
        if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) {
-               vty_out(vty, "Failed to remove route\n");
+               vty_out(vty, "Failed to remove static mroute\n");
                return CMD_WARNING;
        }
 
@@ -10321,9 +10446,7 @@ void pim_cmd_init(void)
 
        // Static mroutes NEB
        install_element(INTERFACE_NODE, &interface_ip_mroute_cmd);
-       install_element(INTERFACE_NODE, &interface_ip_mroute_source_cmd);
        install_element(INTERFACE_NODE, &interface_no_ip_mroute_cmd);
-       install_element(INTERFACE_NODE, &interface_no_ip_mroute_source_cmd);
 
        install_element(VIEW_NODE, &show_ip_igmp_interface_cmd);
        install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd);
@@ -10344,6 +10467,7 @@ void pim_cmd_init(void)
        install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd);
        install_element(VIEW_NODE, &show_ip_pim_join_cmd);
        install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd);
+       install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd);
        install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd);
        install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd);
        install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd);
@@ -10354,6 +10478,7 @@ void pim_cmd_init(void)
        install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd);
        install_element(VIEW_NODE, &show_ip_pim_upstream_cmd);
        install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd);
+       install_element(VIEW_NODE, &show_ip_pim_channel_cmd);
        install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
        install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
        install_element(VIEW_NODE, &show_ip_pim_rp_cmd);
index 3ee9caebcf6c17d3fe1890d95038b690fe8db817..c615540149621e2ad70d314d81082bb2a70abcdc 100644 (file)
@@ -1481,6 +1481,13 @@ void pim_if_create_pimreg(struct pim_instance *pim)
 
                pim_if_new(pim->regiface, false, false, true,
                        false /*vxlan_term*/);
+               /*
+                * On vrf moves we delete the interface if there
+                * is nothing going on with it.  We cannot have
+                * the pimregiface deleted.
+                */
+               pim->regiface->configured = true;
+
        }
 }
 
@@ -1581,6 +1588,7 @@ int pim_ifp_create(struct interface *ifp)
 
 int pim_ifp_up(struct interface *ifp)
 {
+       struct pim_interface *pim_ifp;
        struct pim_instance *pim;
        uint32_t table_id;
 
@@ -1593,24 +1601,21 @@ int pim_ifp_up(struct interface *ifp)
        }
 
        pim = pim_get_pim_instance(ifp->vrf_id);
-       if (if_is_operative(ifp)) {
-               struct pim_interface *pim_ifp;
 
-               pim_ifp = ifp->info;
-               /*
-                * If we have a pim_ifp already and this is an if_add
-                * that means that we probably have a vrf move event
-                * If that is the case, set the proper vrfness.
-                */
-               if (pim_ifp)
-                       pim_ifp->pim = pim;
+       pim_ifp = ifp->info;
+       /*
+        * If we have a pim_ifp already and this is an if_add
+        * that means that we probably have a vrf move event
+        * If that is the case, set the proper vrfness.
+        */
+       if (pim_ifp)
+               pim_ifp->pim = pim;
 
-               /*
-                 pim_if_addr_add_all() suffices for bringing up both IGMP and
-                 PIM
-               */
-               pim_if_addr_add_all(ifp);
-       }
+       /*
+         pim_if_addr_add_all() suffices for bringing up both IGMP and
+         PIM
+       */
+       pim_if_addr_add_all(ifp);
 
        /*
         * If we have a pimreg device callback and it's for a specific
index 48bd031f1ee92b2f5e6e6869213be3d5fa90017a..3a7eb45f27fb3abdb44c794e913267cf127a5b60 100644 (file)
@@ -147,10 +147,11 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
                                        ch->upstream, ch, ch->parent))
                        pim_channel_add_oif(ch->upstream->channel_oil,
                                        ch->interface,
-                                       PIM_OIF_FLAG_PROTO_STAR);
+                                       PIM_OIF_FLAG_PROTO_STAR,
+                                       __func__);
 
                pim_channel_del_oif(ch->upstream->channel_oil,
-                                       ch->interface, mask);
+                                       ch->interface, mask, __func__);
                /*
                 * Do we have any S,G's that are inheriting?
                 * Nuke from on high too.
@@ -161,9 +162,10 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
 
                        for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
                                                  up_node, child))
-                               pim_channel_del_oif(child->channel_oil,
-                                                   ch->interface,
-                                                   PIM_OIF_FLAG_PROTO_STAR);
+                               pim_channel_del_inherited_oif(
+                                               child->channel_oil,
+                                               ch->interface,
+                                               __func__);
                }
        }
 
@@ -179,9 +181,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
 
        listnode_delete(ch->upstream->ifchannels, ch);
 
-       if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
-               pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
-       }
+       pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
 
        /* upstream is common across ifchannels, check if upstream's
           ifchannel list is empty before deleting upstream_del
@@ -250,6 +250,7 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
 {
        enum pim_ifjoin_state old_state = ch->ifjoin_state;
        struct pim_interface *pim_ifp = ch->interface->info;
+       struct pim_ifchannel *child_ch;
 
        if (PIM_DEBUG_PIM_EVENTS)
                zlog_debug(
@@ -293,28 +294,18 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
                                        if (!c_oil)
                                                continue;
 
-                                       if (!pim_upstream_evaluate_join_desired(
-                                                   pim_ifp->pim, child)) {
-                                               pim_channel_del_oif(
-                                                       c_oil, ch->interface,
-                                                       PIM_OIF_FLAG_PROTO_STAR);
-                                               pim_upstream_update_join_desired(
-                                                       pim_ifp->pim, child);
-                                       }
-
                                        /*
                                         * If the S,G has no if channel and the
                                         * c_oil still
                                         * has output here then the *,G was
                                         * supplying the implied
                                         * if channel.  So remove it.
-                                        * I think this is dead code now. is it?
                                         */
                                        if (c_oil->oil.mfcc_ttls
                                                    [pim_ifp->mroute_vif_index])
-                                               pim_channel_del_oif(
+                                               pim_channel_del_inherited_oif(
                                                        c_oil, ch->interface,
-                                                       PIM_OIF_FLAG_PROTO_STAR);
+                                                       __func__);
                                }
                        }
                        if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
@@ -328,12 +319,19 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
                                                        child->sg_str,
                                                        up->sg_str);
 
-                                       if (pim_upstream_evaluate_join_desired(
-                                                   pim_ifp->pim, child)) {
+                                       /* check if the channel can be
+                                        * inherited into the SG's OIL
+                                        */
+                                       child_ch = pim_ifchannel_find(
+                                                       ch->interface,
+                                                       &child->sg);
+                                       if (pim_upstream_eval_inherit_if(
+                                                   child, child_ch, ch)) {
                                                pim_channel_add_oif(
                                                        child->channel_oil,
                                                        ch->interface,
-                                                       PIM_OIF_FLAG_PROTO_STAR);
+                                                       PIM_OIF_FLAG_PROTO_STAR,
+                                                       __func__);
                                                pim_upstream_update_join_desired(
                                                        pim_ifp->pim, child);
                                        }
@@ -783,6 +781,28 @@ static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
        return 1; /* non-local */
 }
 
+static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
+               struct pim_interface *pim_ifp)
+{
+       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
+                       PIM_IFJOIN_JOIN);
+       PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
+       /* check if the interface qualifies as an immediate
+        * OIF
+        */
+       if (pim_upstream_evaluate_join_desired_interface(
+                               ch->upstream, ch,
+                               NULL /*starch*/)) {
+               pim_channel_add_oif(ch->upstream->channel_oil,
+                               ch->interface,
+                               PIM_OIF_FLAG_PROTO_PIM,
+                               __func__);
+               pim_upstream_update_join_desired(pim_ifp->pim,
+                               ch->upstream);
+       }
+}
+
+
 void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
                            struct in_addr upstream, struct prefix_sg *sg,
                            uint8_t source_flags, uint16_t holdtime)
@@ -890,25 +910,8 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
                if (source_flags & PIM_ENCODE_RPT_BIT)
                        pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
                                                    PIM_IFJOIN_NOINFO);
-               else {
-                       /*
-                        * We have received a S,G join and we are in
-                        * S,G RPT Prune state.  Which means we need
-                        * to transition to Join state and setup
-                        * state as appropriate.
-                        */
-                       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
-                                                   PIM_IFJOIN_JOIN);
-                       PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
-                       if (pim_upstream_evaluate_join_desired(pim_ifp->pim,
-                                                              ch->upstream)) {
-                               pim_channel_add_oif(ch->upstream->channel_oil,
-                                                   ch->interface,
-                                                   PIM_OIF_FLAG_PROTO_PIM);
-                               pim_upstream_update_join_desired(pim_ifp->pim,
-                                                                ch->upstream);
-                       }
-               }
+               else
+                       pim_ifchannel_ifjoin_handler(ch, pim_ifp);
                break;
        case PIM_IFJOIN_PRUNE_PENDING:
                THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
@@ -916,9 +919,9 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
                        THREAD_OFF(ch->t_ifjoin_expiry_timer);
                        pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
                                                    PIM_IFJOIN_NOINFO);
-               } else
-                       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
-                                                   PIM_IFJOIN_JOIN);
+               } else {
+                       pim_ifchannel_ifjoin_handler(ch, pim_ifp);
+               }
                break;
        case PIM_IFJOIN_PRUNE_TMP:
                break;
@@ -1105,9 +1108,9 @@ int pim_ifchannel_local_membership_add(struct interface *ifp,
                        if (pim_upstream_evaluate_join_desired_interface(
                                    child, ch, starch)) {
                                pim_channel_add_oif(child->channel_oil, ifp,
-                                                   PIM_OIF_FLAG_PROTO_STAR);
-                               pim_upstream_switch(pim, child,
-                                                   PIM_UPSTREAM_JOINED);
+                                                   PIM_OIF_FLAG_PROTO_STAR,
+                                                       __func__);
+                               pim_upstream_update_join_desired(pim, child);
                        }
                }
 
@@ -1124,12 +1127,14 @@ int pim_ifchannel_local_membership_add(struct interface *ifp,
                                    == PREFIX_DENY) {
                                        pim_channel_add_oif(
                                                up->channel_oil, pim->regiface,
-                                               PIM_OIF_FLAG_PROTO_IGMP);
+                                               PIM_OIF_FLAG_PROTO_IGMP,
+                                               __func__);
                                }
                        }
                } else
                        pim_channel_add_oif(up->channel_oil, pim->regiface,
-                                           PIM_OIF_FLAG_PROTO_IGMP);
+                                       PIM_OIF_FLAG_PROTO_IGMP,
+                                       __func__);
        }
 
        return 1;
@@ -1174,22 +1179,19 @@ void pim_ifchannel_local_membership_del(struct interface *ifp,
                                           child->sg_str);
 
                        ch = pim_ifchannel_find(ifp, &child->sg);
-                       if (c_oil
-                           && !pim_upstream_evaluate_join_desired_interface(
-                                      child, ch, starch))
-                               pim_channel_del_oif(c_oil, ifp,
-                                                   PIM_OIF_FLAG_PROTO_STAR);
-
                        /*
                         * If the S,G has no if channel and the c_oil still
                         * has output here then the *,G was supplying the
                         * implied
                         * if channel.  So remove it.
                         */
-                       if (!chchannel && c_oil
-                           && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
-                               pim_channel_del_oif(c_oil, ifp,
-                                                   PIM_OIF_FLAG_PROTO_STAR);
+                       if (!pim_upstream_evaluate_join_desired_interface(
+                               child, ch, starch) ||
+                               (!chchannel &&
+                                c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])) {
+                               pim_channel_del_inherited_oif(c_oil, ifp,
+                                               __func__);
+                       }
 
                        /* Child node removal/ref count-- will happen as part of
                         * parent' delete_no_info */
@@ -1408,12 +1410,10 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
                                child->upstream))) {
                                pim_channel_add_oif(
                                        child->upstream->channel_oil,
-                                       ch->interface, PIM_OIF_FLAG_PROTO_STAR);
-                               pim_upstream_switch(pim, child->upstream,
-                                                   PIM_UPSTREAM_JOINED);
-                               pim_jp_agg_single_upstream_send(
-                                       &child->upstream->rpf, child->upstream,
-                                       true);
+                                       ch->interface, PIM_OIF_FLAG_PROTO_STAR,
+                                       __func__);
+                               pim_upstream_update_join_desired(pim,
+                                               child->upstream);
                        }
                        send_upstream_starg = true;
 
index 3602d98a3ea3de930f2c5461553c5d5a0b6631ce..39ef706f79c1118e11ba3b87c4bf4283aa9cb06f 100644 (file)
@@ -478,10 +478,24 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
 
        ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
 
+       if (ip_hlen > len) {
+               zlog_warn(
+                       "IGMP packet header claims size %zu, but we only have %zu bytes",
+                       ip_hlen, len);
+               return -1;
+       }
+
        igmp_msg = buf + ip_hlen;
-       msg_type = *igmp_msg;
        igmp_msg_len = len - ip_hlen;
 
+       if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
+               zlog_warn("IGMP message size=%d shorter than minimum=%d",
+                         igmp_msg_len, PIM_IGMP_MIN_LEN);
+               return -1;
+       }
+
+       msg_type = *igmp_msg;
+
        if (PIM_DEBUG_IGMP_PACKETS) {
                zlog_debug(
                        "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
@@ -489,12 +503,6 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
                        msg_type, igmp_msg_len);
        }
 
-       if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
-               zlog_warn("IGMP message size=%d shorter than minimum=%d",
-                         igmp_msg_len, PIM_IGMP_MIN_LEN);
-               return -1;
-       }
-
        switch (msg_type) {
        case PIM_IGMP_MEMBERSHIP_QUERY: {
                int max_resp_code = igmp_msg[1];
@@ -1105,8 +1113,10 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
        }
 
        if (pim_is_group_224_0_0_0_24(group_addr)) {
-               zlog_warn("%s: Group specified is part of 224.0.0.0/24",
-                         __PRETTY_FUNCTION__);
+               if (PIM_DEBUG_IGMP_TRACE)
+                       zlog_debug(
+                               "%s: Group specified %s is part of 224.0.0.0/24",
+                               __PRETTY_FUNCTION__, inet_ntoa(group_addr));
                return NULL;
        }
        /*
index 88385bffba47631c77b5faaf455d98a4690ca3be..c3239027647cc3a0ebe8e90007a28b0444fe73e5 100644 (file)
 #define SOL_IP IPPROTO_IP
 #endif
 
+#ifndef MCAST_JOIN_GROUP
+#define MCAST_JOIN_GROUP 42
+#endif
+
 #ifndef MCAST_JOIN_SOURCE_GROUP
 #define MCAST_JOIN_SOURCE_GROUP 46
 struct group_source_req {
@@ -58,8 +62,12 @@ static int pim_igmp_join_source(int fd, ifindex_t ifindex,
 
        req.gsr_interface = ifindex;
 
-       return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req,
-                         sizeof(req));
+       if (source_addr.s_addr == INADDR_ANY)
+               return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req,
+                                 sizeof(req));
+       else
+               return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req,
+                                 sizeof(req));
 }
 
 #endif /* PIM_IGMP_JOIN_H */
index 0758e2f784eb6c7ddec8300fe0b7b1d39e1fd701..695d04c7c2ba4cdc0243446fdb21a0e47a57e4fa 100644 (file)
@@ -864,6 +864,16 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
        pim_ifp = ifp->info;
        pim = pim_ifp->pim;
 
+       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",
+                               from_str, ifp->name, igmp_msg_len,
+                               sizeof(struct igmp_mtrace));
+               return -1;
+       }
+
        mtracep = (struct igmp_mtrace *)igmp_msg;
 
        recv_checksum = mtracep->checksum;
index dd3ac8fcb07bfcb6c1759cdf40158d93b7eba041..da0c75decb6c8b07b602207b7a42c5b782a6e76b 100644 (file)
@@ -28,6 +28,8 @@
 #include "pim_assert.h"
 #include "pim_bsm.h"
 #include "pim_vxlan_instance.h"
+#include "pim_oil.h"
+#include "pim_upstream.h"
 
 #if defined(HAVE_LINUX_MROUTE_H)
 #include <linux/mroute.h>
@@ -107,8 +109,7 @@ struct pim_instance {
        struct list *static_routes;
 
        // Upstream vrf specific information
-       struct list *upstream_list;
-       struct hash *upstream_hash;
+       struct rb_pim_upstream_head upstream_head;
        struct timer_wheel *upstream_sg_wheel;
 
        /*
@@ -119,8 +120,7 @@ struct pim_instance {
 
        int iface_vif_index[MAXVIFS];
 
-       struct list *channel_oil_list;
-       struct hash *channel_oil_hash;
+       struct rb_pim_oil_head channel_oil_head;
 
        struct pim_msdp msdp;
        struct pim_vxlan_instance vxlan;
index 89be42842ee32f1e5c23b046f2a5ea6fd6e1d0c9..fbb547c80e797e64b669321a91cba1e723199d1f 100644 (file)
@@ -327,7 +327,6 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
                        recv_prune(ifp, neigh, msg_holdtime,
                                   msg_upstream_addr.u.prefix4, &sg,
                                   msg_source_flags);
-
                        /*
                         * So if we are receiving a S,G,RPT prune
                         * before we have any data for that S,G
@@ -348,10 +347,10 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
                                                        "%s: SGRpt flag is set, del inherit oif from up %s",
                                                        __PRETTY_FUNCTION__,
                                                        up->sg_str);
-                                       pim_channel_del_oif(
+                                       pim_channel_del_inherited_oif(
                                                up->channel_oil,
                                                starg_ch->interface,
-                                               PIM_OIF_FLAG_PROTO_STAR);
+                                               __func__);
                                }
                        }
                }
index 418f66df0558bbf4f75b1186f7bce6050b5fe129..2662c9eae68d2b2aaba910c65555ab00ea7d006e 100644 (file)
@@ -117,10 +117,16 @@ void pim_jp_agg_clear_group(struct list *group)
 static struct pim_iface_upstream_switch *
 pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
 {
-       struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info;
+       struct interface *ifp = rpf->source_nexthop.interface;
+       struct pim_interface *pim_ifp;
        struct pim_iface_upstream_switch *pius;
        struct listnode *node, *nnode;
 
+       if (!ifp)
+               return NULL;
+
+       pim_ifp = ifp->info;
+
        /* Old interface is pim disabled */
        if (!pim_ifp)
                return NULL;
@@ -142,7 +148,8 @@ pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
        return pius;
 }
 
-void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up)
+void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
+               struct pim_neighbor *nbr)
 {
        struct listnode *node, *nnode;
        struct pim_jp_agg_group *jag = NULL;
@@ -161,6 +168,20 @@ void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up)
                        break;
        }
 
+       if (nbr) {
+               if (PIM_DEBUG_TRACE) {
+                       char src_str[INET_ADDRSTRLEN];
+
+                       pim_inet4_dump("<src?>", nbr->source_addr, src_str,
+                                       sizeof(src_str));
+                       zlog_debug(
+                               "up %s remove from nbr %s/%s jp-agg-list",
+                               up->sg_str,
+                               nbr->interface->name,
+                               src_str);
+               }
+       }
+
        if (js) {
                js->up = NULL;
                listnode_delete(jag->sources, js);
@@ -248,7 +269,7 @@ void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
 }
 
 void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
-                         bool is_join)
+                         bool is_join, struct pim_neighbor *nbr)
 {
        struct listnode *node, *nnode;
        struct pim_jp_agg_group *jag = NULL;
@@ -274,6 +295,20 @@ void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
                        break;
        }
 
+       if (nbr) {
+               if (PIM_DEBUG_TRACE) {
+                       char src_str[INET_ADDRSTRLEN];
+
+                       pim_inet4_dump("<src?>", nbr->source_addr, src_str,
+                                       sizeof(src_str));
+                       zlog_debug(
+                               "up %s add to nbr %s/%s jp-agg-list",
+                               up->sg_str,
+                               up->rpf.source_nexthop.interface->name,
+                               src_str);
+               }
+       }
+
        if (!js) {
                js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
                             sizeof(struct pim_jp_sources));
@@ -314,10 +349,11 @@ void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
 
        /* send Prune(S,G) to the old upstream neighbor */
        if (opius)
-               pim_jp_agg_add_group(opius->us, up, false);
+               pim_jp_agg_add_group(opius->us, up, false, NULL);
 
        /* send Join(S,G) to the current upstream neighbor */
-       pim_jp_agg_add_group(npius->us, up, true);
+       if (npius)
+               pim_jp_agg_add_group(npius->us, up, true, NULL);
 }
 
 
@@ -331,10 +367,10 @@ void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
        static bool first = true;
 
        /* skip JP upstream messages if source is directly connected */
-       if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source(
-                                                            rpf->source_nexthop
-                                                                    .interface,
-                                                            up->sg.src))
+       if (!up || !rpf->source_nexthop.interface ||
+               pim_if_connected_to_source(rpf->source_nexthop.interface,
+                       up->sg.src) ||
+               if_is_loopback_or_vrf(rpf->source_nexthop.interface))
                return;
 
        if (first) {
index aa21aa9816a02479514a2d00ef350f331930407d..d88ff8892b5101942aa6f40b05bf02e95f9f00b7 100644 (file)
@@ -37,10 +37,11 @@ void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag);
 int pim_jp_agg_group_list_cmp(void *arg1, void *arg2);
 
 void pim_jp_agg_clear_group(struct list *group);
-void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up);
+void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
+               struct pim_neighbor *nbr);
 
 void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
-                         bool is_join);
+               bool is_join, struct pim_neighbor *nbr);
 
 void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
                                 struct pim_upstream *up);
index 3f79b304e5952617ae66b1a0cee77f9db05da58a..f0ca367258d4ab5840b8bf6e9316136bb5b721f1 100644 (file)
@@ -29,7 +29,6 @@
 
 #include "memory.h"
 #include "vrf.h"
-#include "memory_vty.h"
 #include "filter.h"
 #include "vty.h"
 #include "sigevent.h"
index 2859d26ebad0f3ef93f363e07ca1a332fbeab994..3459abbc191c3626c227bed4155d8d631eb9d985 100644 (file)
@@ -207,7 +207,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp,
                up = pim_upstream_find_or_add(
                        &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
                        __PRETTY_FUNCTION__);
-               pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+               pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
 
                return 0;
        }
@@ -228,16 +228,10 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp,
        pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
 
        up->channel_oil->cc.pktcnt++;
-       PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
        // resolve mfcc_parent prior to mroute_add in channel_add_oif
        if (up->rpf.source_nexthop.interface &&
            up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
-               int vif_index = 0;
-               vif_index = pim_if_find_vifindex_by_ifindex(
-                       pim_ifp->pim,
-                       up->rpf.source_nexthop.interface->ifindex);
-               pim_channel_oil_change_iif(pim_ifp->pim, up->channel_oil,
-                                          vif_index, __PRETTY_FUNCTION__);
+               pim_upstream_mroute_iif_update(up->channel_oil, __func__);
        }
        pim_register_join(up);
 
@@ -283,8 +277,7 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp,
                        pim_upstream_keep_alive_timer_start(
                                up, pim_ifp->pim->keep_alive_time);
                        pim_upstream_inherited_olist(pim_ifp->pim, up);
-                       pim_upstream_switch(pim_ifp->pim, up,
-                                           PIM_UPSTREAM_JOINED);
+                       pim_upstream_update_join_desired(pim_ifp->pim, up);
 
                        if (PIM_DEBUG_MROUTE)
                                zlog_debug("%s: Creating %s upstream on LHR",
@@ -330,6 +323,15 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp,
                                        pim_str_sg_dump(&sg));
                        return 0;
                }
+
+               if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
+                       if (PIM_DEBUG_PIM_REG)
+                               zlog_debug(
+                                       "%s register forward skipped, not FHR",
+                                       up->sg_str);
+                       return 0;
+               }
+
                pim_register_send((uint8_t *)buf + sizeof(struct ip),
                                  ntohs(ip_hdr->ip_len) - sizeof(struct ip),
                                  pim_ifp->primary_address, rpg, 0, up);
@@ -518,7 +520,7 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp,
 
                        pim_upstream_inherited_olist(pim_ifp->pim, up);
                        if (!up->channel_oil->installed)
-                               pim_mroute_add(up->channel_oil,
+                               pim_upstream_mroute_add(up->channel_oil,
                                               __PRETTY_FUNCTION__);
                } else {
                        if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
@@ -557,6 +559,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp,
                up->channel_oil->cc.pktcnt++;
                pim_register_join(up);
                pim_upstream_inherited_olist(pim_ifp->pim, up);
+               if (!up->channel_oil->installed)
+                       pim_upstream_mroute_add(up->channel_oil, __func__);
 
                // Send the packet to the RP
                pim_mroute_msg_wholepkt(fd, ifp, buf);
@@ -565,7 +569,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp,
                                      PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
                                      __PRETTY_FUNCTION__, NULL);
                if (!up->channel_oil->installed)
-                       pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+                       pim_upstream_mroute_add(up->channel_oil,
+                                       __PRETTY_FUNCTION__);
        }
 
        return 0;
@@ -585,6 +590,9 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
        struct in_addr ifaddr;
        struct igmp_sock *igmp;
 
+       if (buf_size < (int)sizeof(struct ip))
+               return 0;
+
        ip_hdr = (const struct ip *)buf;
 
        if (ip_hdr->ip_p == IPPROTO_IGMP) {
@@ -882,72 +890,92 @@ int pim_mroute_del_vif(struct interface *ifp)
        return 0;
 }
 
-int pim_mroute_add(struct channel_oil *c_oil, const char *name)
+/*
+ * Prevent creating MFC entry with OIF=IIF.
+ *
+ * This is a protection against implementation mistakes.
+ *
+ * PIM protocol implicitely ensures loopfree multicast topology.
+ *
+ * IGMP must be protected against adding looped MFC entries created
+ * by both source and receiver attached to the same interface. See
+ * TODO T22.
+ * We shall allow igmp to create upstream when it is DR for the intf.
+ * Assume RP reachable via non DR.
+ */
+bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
+               int oif_index)
+{
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+       struct interface *ifp_out;
+       struct pim_interface *pim_ifp;
+
+       if (c_oil->up &&
+               PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
+               return true;
+
+       ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
+       if (!ifp_out)
+               return false;
+       pim_ifp = ifp_out->info;
+       if (!pim_ifp)
+               return false;
+       if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) &&
+                       PIM_I_am_DR(pim_ifp))
+               return true;
+
+       return false;
+#else
+       return true;
+#endif
+}
+
+static inline void pim_mroute_copy(struct mfcctl *oil,
+               struct channel_oil *c_oil)
+{
+       int i;
+
+       oil->mfcc_origin = c_oil->oil.mfcc_origin;
+       oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp;
+       oil->mfcc_parent = c_oil->oil.mfcc_parent;
+
+       for (i = 0; i < MAXVIFS; ++i) {
+               if ((oil->mfcc_parent == i) &&
+                               !pim_mroute_allow_iif_in_oil(c_oil, i)) {
+                       oil->mfcc_ttls[i] = 0;
+                       continue;
+               }
+
+               if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE)
+                       oil->mfcc_ttls[i] = 0;
+               else
+                       oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i];
+       }
+}
+
+/* This function must not be called directly 0
+ * use pim_upstream_mroute_add or pim_static_mroute_add instead
+ */
+static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
 {
        struct pim_instance *pim = c_oil->pim;
+       struct mfcctl tmp_oil;
        int err;
-       int orig = 0;
-       int orig_iif_vif = 0;
-       struct pim_interface *pim_reg_ifp = NULL;
-       int orig_pimreg_ttl = 0;
-       bool pimreg_ttl_reset = false;
-       struct pim_interface *vxlan_ifp = NULL;
-       int orig_term_ttl = 0;
-       bool orig_term_ttl_reset = false;
 
        pim->mroute_add_last = pim_time_monotonic_sec();
        ++pim->mroute_add_events;
 
-       /* Do not install route if incoming interface is undefined. */
-       if (c_oil->oil.mfcc_parent >= MAXVIFS) {
-               if (PIM_DEBUG_MROUTE) {
-                       char buf[1000];
-                       zlog_debug(
-                               "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
-                               __PRETTY_FUNCTION__, name,
-                               pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
-               }
-               return -2;
-       }
+       /* Copy the oil to a temporary structure to fixup (without need to
+        * later restore) before sending the mroute add to the dataplane
+        */
+       pim_mroute_copy(&tmp_oil, c_oil);
 
        /* The linux kernel *expects* the incoming
         * vif to be part of the outgoing list
         * in the case of a (*,G).
         */
        if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
-               orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
-               c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
-       }
-
-       if (c_oil->up) {
-               /* suppress pimreg in the OIL if the mroute is not supposed to
-                * trigger register encapsulated data
-                */
-               if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) {
-                       pim_reg_ifp = pim->regiface->info;
-                       orig_pimreg_ttl =
-                               c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index];
-                       c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0;
-                       /* remember to flip it back after MFC programming */
-                       pimreg_ttl_reset = true;
-               }
-
-               vxlan_ifp = pim_vxlan_get_term_ifp(pim);
-               /* 1. vxlan termination device must never be added to the
-                * origination mroute (and that can actually happen because
-                * of XG inheritance from the termination mroute) otherwise
-                * traffic will end up looping.
-                * 2. vxlan termination device should be removed from the non-DF
-                * to prevent duplicates to the overlay rxer
-                */
-               if (vxlan_ifp &&
-                       (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
-                        PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) {
-                       orig_term_ttl_reset = true;
-                       orig_term_ttl =
-                               c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index];
-                       c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0;
-               }
+               tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
        }
 
        /*
@@ -959,33 +987,19 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name)
         */
        if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
            && c_oil->oil.mfcc_parent != 0) {
-               orig_iif_vif = c_oil->oil.mfcc_parent;
-               c_oil->oil.mfcc_parent = 0;
+               tmp_oil.mfcc_parent = 0;
        }
        err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
-                        &c_oil->oil, sizeof(c_oil->oil));
+                        &tmp_oil, sizeof(tmp_oil));
 
        if (!err && !c_oil->installed
            && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
-           && orig_iif_vif != 0) {
-               c_oil->oil.mfcc_parent = orig_iif_vif;
+           && c_oil->oil.mfcc_parent != 0) {
+               tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent;
                err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
-                                &c_oil->oil, sizeof(c_oil->oil));
-       }
-
-       if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
-               c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
-
-       if (pimreg_ttl_reset) {
-               assert(pim_reg_ifp);
-               c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] =
-                       orig_pimreg_ttl;
+                                &tmp_oil, sizeof(tmp_oil));
        }
 
-       if (orig_term_ttl_reset)
-               c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] =
-                       orig_term_ttl;
-
        if (err) {
                zlog_warn(
                        "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
@@ -1007,6 +1021,142 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name)
        return 0;
 }
 
+static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
+               const char *name)
+{
+       vifi_t iif = MAXVIFS;
+       struct interface *ifp = NULL;
+       struct pim_interface *pim_ifp;
+       struct pim_upstream *up = c_oil->up;
+
+       if (up) {
+               if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
+                       if (up->parent)
+                               ifp = up->parent->rpf.source_nexthop.interface;
+               } else {
+                       ifp = up->rpf.source_nexthop.interface;
+               }
+               if (ifp) {
+                       pim_ifp = (struct pim_interface *)ifp->info;
+                       if (pim_ifp)
+                               iif = pim_ifp->mroute_vif_index;
+               }
+       }
+       return iif;
+}
+
+static int pim_upstream_mroute_update(struct channel_oil *c_oil,
+               const char *name)
+{
+       char buf[1000];
+
+       if (c_oil->oil.mfcc_parent >= MAXVIFS) {
+               /* the c_oil cannot be installed as a mroute yet */
+               if (PIM_DEBUG_MROUTE)
+                       zlog_debug(
+                                       "%s(%s) %s mroute not ready to be installed; %s",
+                                       __func__, name,
+                                       pim_channel_oil_dump(c_oil, buf,
+                                               sizeof(buf)),
+                                       c_oil->installed ?
+                                       "uninstall" : "skip");
+               /* if already installed flush it out as we are going to stop
+                * updates to it leaving it in a stale state
+                */
+               if (c_oil->installed)
+                       pim_mroute_del(c_oil, name);
+               /* return success (skipped) */
+               return 0;
+       }
+
+       return pim_mroute_add(c_oil, name);
+}
+
+/* IIF associated with SGrpt entries are re-evaluated when the parent
+ * (*,G) entries IIF changes
+ */
+static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
+{
+       struct listnode *listnode;
+       struct pim_upstream *child;
+
+       for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
+                               child)) {
+               if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+                       pim_upstream_mroute_iif_update(child->channel_oil,
+                                       __func__);
+       }
+}
+
+/* In the case of "PIM state machine" added mroutes an upstream entry
+ * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
+ */
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+       vifi_t iif;
+
+       iif = pim_upstream_get_mroute_iif(c_oil, name);
+
+       if (c_oil->oil.mfcc_parent != iif) {
+               c_oil->oil.mfcc_parent = iif;
+               if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+                               c_oil->up)
+                       pim_upstream_all_sources_iif_update(c_oil->up);
+       } else {
+               c_oil->oil.mfcc_parent = iif;
+       }
+
+       return pim_upstream_mroute_update(c_oil, name);
+}
+
+/* Look for IIF changes and update the dateplane entry only if the IIF
+ * has changed.
+ */
+int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
+{
+       vifi_t iif;
+       char buf[1000];
+
+       iif = pim_upstream_get_mroute_iif(c_oil, name);
+       if (c_oil->oil.mfcc_parent == iif) {
+               /* no change */
+               return 0;
+       }
+       c_oil->oil.mfcc_parent = iif;
+
+       if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+                       c_oil->up)
+               pim_upstream_all_sources_iif_update(c_oil->up);
+
+       if (PIM_DEBUG_MROUTE_DETAIL)
+               zlog_debug("%s(%s) %s mroute iif update %d",
+                               __func__, name,
+                               pim_channel_oil_dump(c_oil, buf,
+                                       sizeof(buf)), iif);
+       /* XXX: is this hack needed? */
+       c_oil->oil_inherited_rescan = 1;
+       return pim_upstream_mroute_update(c_oil, name);
+}
+
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+       return pim_mroute_add(c_oil, name);
+}
+
+void pim_static_mroute_iif_update(struct channel_oil *c_oil,
+                               int input_vif_index,
+                               const char *name)
+{
+       if (c_oil->oil.mfcc_parent == input_vif_index)
+               return;
+
+       c_oil->oil.mfcc_parent = input_vif_index;
+       if (input_vif_index == MAXVIFS)
+               pim_mroute_del(c_oil, name);
+       else
+               pim_static_mroute_add(c_oil, name);
+}
+
 int pim_mroute_del(struct channel_oil *c_oil, const char *name)
 {
        struct pim_instance *pim = c_oil->pim;
index bd71acbf82cab506d7e05a161ea575585f0d4c63..2d8e1b01fb89117e391aebf0fa21f0d6c63bb0e0 100644 (file)
@@ -174,8 +174,15 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr,
                       unsigned char flags);
 int pim_mroute_del_vif(struct interface *ifp);
 
-int pim_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name);
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name);
+void pim_static_mroute_iif_update(struct channel_oil *c_oil,
+                               int input_vif_index,
+                               const char *name);
 int pim_mroute_del(struct channel_oil *c_oil, const char *name);
 
 void pim_mroute_update_counters(struct channel_oil *c_oil);
+bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
+               int oif_index);
 #endif /* PIM_MROUTE_H */
index 74a3a9836bc6b7436320775f3cdc2c61ffef8f93..58ebc6ce67d716d56f31a9259504894029e56ab7 100644 (file)
@@ -126,7 +126,12 @@ static void pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
        if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
                PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
                sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
-               pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__);
+               up = pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__);
+               /* re-eval joinDesired; clearing peer-msdp-sa flag can
+                * cause JD to change
+                */
+               if (up)
+                       pim_upstream_update_join_desired(sa->pim, up);
                sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
        }
 
@@ -445,10 +450,9 @@ static bool pim_msdp_sa_local_add_ok(struct pim_upstream *up)
                return false;
        }
 
-       if (!up->t_ka_timer) {
+       if (!pim_upstream_is_kat_running(up))
                /* stream is not active */
                return false;
-       }
 
        if (!I_am_RP(pim, up->sg.grp)) {
                /* we are not RP for the group */
@@ -561,11 +565,9 @@ void pim_msdp_sa_local_update(struct pim_upstream *up)
 static void pim_msdp_sa_local_setup(struct pim_instance *pim)
 {
        struct pim_upstream *up;
-       struct listnode *up_node;
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up)
                pim_msdp_sa_local_update(up);
-       }
 }
 
 /* whenever the RP changes we need to re-evaluate the "local" SA-cache */
index 2e467502b1feb38261dfbc8ab23a88d40a348791..fba76d84dd4f0a99eaa7e63233d0def5b1e281b3 100644 (file)
@@ -126,8 +126,17 @@ size_t pim_msg_get_jp_group_size(struct list *sources)
                                __PRETTY_FUNCTION__, up->sg_str);
 
                for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
-                       if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) {
-                               if (!pim_rpf_is_same(&up->rpf, &child->rpf)) {
+                       if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) {
+                               /* If we are using SPT and the SPT and RPT IIFs
+                                * are different we can prune the source off
+                                * of the RPT.
+                                * If RPF_interface(S) is not resolved hold
+                                * decision to prune as SPT may end up on the
+                                * same IIF as RPF_interface(RP).
+                                */
+                               if (child->rpf.source_nexthop.interface &&
+                                       !pim_rpf_is_same(&up->rpf,
+                                               &child->rpf)) {
                                        size += sizeof(
                                                struct pim_encoded_source_ipv4);
                                        PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
@@ -143,37 +152,25 @@ size_t pim_msg_get_jp_group_size(struct list *sources)
                                                "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)",
                                                __PRETTY_FUNCTION__, up->sg_str,
                                                child->sg_str);
-                       } else if (pim_upstream_is_sg_rpt(child)) {
-                               if (pim_upstream_empty_inherited_olist(child)) {
-                                       size += sizeof(
+                       } else if (pim_upstream_empty_inherited_olist(child)) {
+                               /* S is supposed to be forwarded along the RPT
+                                * but it's inherited OIL is empty. So just
+                                * prune it off.
+                                */
+                               size += sizeof(
                                                struct pim_encoded_source_ipv4);
-                                       PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
+                               PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
                                                child->flags);
-                                       if (PIM_DEBUG_PIM_PACKETS)
-                                               zlog_debug(
+                               if (PIM_DEBUG_PIM_PACKETS)
+                                       zlog_debug(
                                                        "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message",
                                                        __PRETTY_FUNCTION__,
                                                        child->sg_str);
-                               } else if (!pim_rpf_is_same(&up->rpf,
-                                                           &child->rpf)) {
-                                       size += sizeof(
-                                               struct pim_encoded_source_ipv4);
-                                       PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
-                                               child->flags);
-                                       if (PIM_DEBUG_PIM_PACKETS)
-                                               zlog_debug(
-                                                       "%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message",
-                                                       __PRETTY_FUNCTION__,
-                                                       up->sg_str,
-                                                       child->sg_str);
-                               } else if (PIM_DEBUG_PIM_PACKETS)
-                                       zlog_debug(
-                                               "%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message",
-                                               __PRETTY_FUNCTION__, up->sg_str,
-                                               child->sg_str);
                        } else if (PIM_DEBUG_PIM_PACKETS)
-                               zlog_debug("%s: SPT bit is not set for (%s)",
-                                          __PRETTY_FUNCTION__, child->sg_str);
+                               zlog_debug(
+                                               "%s: Do not add Prune %s to compound message %s",
+                                               __PRETTY_FUNCTION__, child->sg_str,
+                                               up->sg_str);
                }
        }
        return size;
index 7900e392312571a7db1ac96f25fad89d7e24ee43..5cb9492ec36ae2c8e68162bad175f16ef3237ab0 100644 (file)
@@ -177,7 +177,6 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
        struct pim_nexthop_cache *pnc = NULL;
        struct pim_nexthop_cache lookup;
        struct zclient *zclient = NULL;
-       struct listnode *upnode = NULL;
        struct pim_upstream *upstream = NULL;
 
        zclient = pim_zebra_zclient_get();
@@ -190,8 +189,8 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr,
                        /* Release the (*, G)upstream from pnc->upstream_hash,
                         * whose Group belongs to the RP getting deleted
                         */
-                       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
-                               upstream)) {
+                       frr_each (rb_pim_upstream, &pim->upstream_head,
+                                 upstream) {
                                struct prefix grp;
                                struct rp_info *trp_info;
 
@@ -441,37 +440,22 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
 {
        struct pim_instance *pim = (struct pim_instance *)arg;
        struct pim_upstream *up = (struct pim_upstream *)bucket->data;
-       int vif_index = 0;
 
        enum pim_rpf_result rpf_result;
        struct pim_rpf old;
 
        old.source_nexthop.interface = up->rpf.source_nexthop.interface;
-       rpf_result = pim_rpf_update(pim, up, &old);
-       if (rpf_result == PIM_RPF_FAILURE) {
-               pim_upstream_rpf_clear(pim, up);
-               return HASHWALK_CONTINUE;
-       }
-
-       /* update kernel multicast forwarding cache (MFC) */
-       if (up->rpf.source_nexthop.interface) {
-               ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
+       rpf_result = pim_rpf_update(pim, up, &old, __func__);
 
-               vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
-               /* Pass Current selected NH vif index to mroute download
-                */
-               if (vif_index)
-                       pim_scan_individual_oil(up->channel_oil, vif_index);
-               else {
-                       if (PIM_DEBUG_PIM_NHT)
-                               zlog_debug(
-                                       "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
-                                       __PRETTY_FUNCTION__, up->sg_str,
-                                       up->rpf.source_nexthop.interface->name);
-               }
-       }
+       /* update kernel multicast forwarding cache (MFC); if the
+        * RPF nbr is now unreachable the MFC has already been updated
+        * by pim_rpf_clear
+        */
+       if (rpf_result != PIM_RPF_FAILURE)
+               pim_upstream_mroute_iif_update(up->channel_oil, __func__);
 
-       if (rpf_result == PIM_RPF_CHANGED)
+       if (rpf_result == PIM_RPF_CHANGED ||
+               (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface))
                pim_zebra_upstream_rpf_changed(pim, up, &old);
 
 
@@ -480,7 +464,8 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
                        __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
                        old.source_nexthop.interface
                        ? old.source_nexthop.interface->name : "Unknown",
-                       up->rpf.source_nexthop.interface->name);
+                       up->rpf.source_nexthop.interface
+                       ? up->rpf.source_nexthop.interface->name : "Unknown");
        }
 
        return HASHWALK_CONTINUE;
index d14293491663d98e4ca19df1c41530021a69d6de..598988f88f7704f9a8ee8cbe4f46db09903154d7 100644 (file)
 #include "pim_str.h"
 #include "pim_iface.h"
 #include "pim_time.h"
+#include "pim_vxlan.h"
 
 // struct list *pim_channel_oil_list = NULL;
 // struct hash *pim_channel_oil_hash = NULL;
 
+static void pim_channel_update_mute(struct channel_oil *c_oil);
+
 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
 {
        char *out;
@@ -61,8 +64,8 @@ char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
        return buf;
 }
 
-static int pim_channel_oil_compare(struct channel_oil *c1,
-                                  struct channel_oil *c2)
+int pim_channel_oil_compare(const struct channel_oil *c1,
+                           const struct channel_oil *c2)
 {
        if (ntohl(c1->oil.mfcc_mcastgrp.s_addr)
            < ntohl(c2->oil.mfcc_mcastgrp.s_addr))
@@ -83,48 +86,19 @@ static int pim_channel_oil_compare(struct channel_oil *c1,
        return 0;
 }
 
-static bool pim_oil_equal(const void *arg1, const void *arg2)
-{
-       const struct channel_oil *c1 = (const struct channel_oil *)arg1;
-       const struct channel_oil *c2 = (const struct channel_oil *)arg2;
-
-       if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
-           && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
-               return true;
-
-       return false;
-}
-
-static unsigned int pim_oil_hash_key(const void *arg)
-{
-       const struct channel_oil *oil = arg;
-
-       return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
-                           oil->oil.mfcc_origin.s_addr, 0);
-}
-
 void pim_oil_init(struct pim_instance *pim)
 {
-       char hash_name[64];
-
-       snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name);
-       pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key,
-                                                pim_oil_equal, hash_name);
-
-       pim->channel_oil_list = list_new();
-       pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
-       pim->channel_oil_list->cmp =
-               (int (*)(void *, void *))pim_channel_oil_compare;
+       rb_pim_oil_init(&pim->channel_oil_head);
 }
 
 void pim_oil_terminate(struct pim_instance *pim)
 {
-       if (pim->channel_oil_list)
-               list_delete(&pim->channel_oil_list);
+       struct channel_oil *c_oil;
 
-       if (pim->channel_oil_hash)
-               hash_free(pim->channel_oil_hash);
-       pim->channel_oil_hash = NULL;
+       while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
+               pim_channel_oil_free(c_oil);
+
+       rb_pim_oil_fini(&pim->channel_oil_head);
 }
 
 void pim_channel_oil_free(struct channel_oil *c_oil)
@@ -141,71 +115,36 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
        lookup.oil.mfcc_mcastgrp = sg->grp;
        lookup.oil.mfcc_origin = sg->src;
 
-       c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
+       c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
 
        return c_oil;
 }
 
-void pim_channel_oil_change_iif(struct pim_instance *pim,
-                               struct channel_oil *c_oil,
-                               int input_vif_index,
-                               const char *name)
-{
-       int old_vif_index = c_oil->oil.mfcc_parent;
-       struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
-                              .grp = c_oil->oil.mfcc_origin};
-
-       if (c_oil->oil.mfcc_parent == input_vif_index) {
-               if (PIM_DEBUG_MROUTE_DETAIL)
-                       zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF",
-                                  __PRETTY_FUNCTION__, name, &sg,
-                                  input_vif_index);
-
-               return;
-       }
-
-       if (PIM_DEBUG_MROUTE_DETAIL)
-               zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d",
-                          __PRETTY_FUNCTION__, name, &sg,
-                          c_oil->oil.mfcc_parent, input_vif_index,
-                          c_oil->installed);
-
-       c_oil->oil.mfcc_parent = input_vif_index;
-       if (c_oil->installed) {
-               if (input_vif_index == MAXVIFS)
-                       pim_mroute_del(c_oil, name);
-               else
-                       pim_mroute_add(c_oil, name);
-       } else
-               if (old_vif_index == MAXVIFS)
-                       pim_mroute_add(c_oil, name);
-
-       return;
-}
-
 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                                        struct prefix_sg *sg,
-                                       int input_vif_index, const char *name)
+                                       const char *name)
 {
        struct channel_oil *c_oil;
-       struct interface *ifp;
 
        c_oil = pim_find_channel_oil(pim, sg);
        if (c_oil) {
-               if (c_oil->oil.mfcc_parent != input_vif_index) {
-                       c_oil->oil_inherited_rescan = 1;
-                       if (PIM_DEBUG_MROUTE_DETAIL)
-                               zlog_debug(
-                                       "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
-                                       __PRETTY_FUNCTION__, sg,
-                                       c_oil->oil.mfcc_parent,
-                                       input_vif_index);
-               }
-               pim_channel_oil_change_iif(pim, c_oil, input_vif_index,
-                                          name);
                ++c_oil->oil_ref_count;
-               /* channel might be present prior to upstream */
-               c_oil->up = pim_upstream_find(pim, sg);
+
+               if (!c_oil->up) {
+                       /* channel might be present prior to upstream */
+                       c_oil->up = pim_upstream_find(
+                                       pim, sg);
+                       /* if the upstream entry is being anchored to an
+                        * already existing channel OIL we need to re-evaluate
+                        * the "Mute" state on AA OIFs
+                        */
+                       pim_channel_update_mute(c_oil);
+               }
+
+               /* check if the IIF has changed
+                * XXX - is this really needed
+                */
+               pim_upstream_mroute_iif_update(c_oil, __func__);
 
                if (PIM_DEBUG_MROUTE)
                        zlog_debug(
@@ -215,38 +154,28 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                return c_oil;
        }
 
-       if (input_vif_index != MAXVIFS) {
-               ifp = pim_if_find_by_vif_index(pim, input_vif_index);
-               if (!ifp) {
-                       /* warning only */
-                       zlog_warn(
-                               "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
-                               __PRETTY_FUNCTION__, name, sg, input_vif_index);
-               }
-       }
-
        c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
 
        c_oil->oil.mfcc_mcastgrp = sg->grp;
        c_oil->oil.mfcc_origin = sg->src;
-       c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
 
-       c_oil->oil.mfcc_parent = input_vif_index;
+       c_oil->oil.mfcc_parent = MAXVIFS;
        c_oil->oil_ref_count = 1;
        c_oil->installed = 0;
        c_oil->up = pim_upstream_find(pim, sg);
        c_oil->pim = pim;
 
-       listnode_add_sort(pim->channel_oil_list, c_oil);
+       rb_pim_oil_add(&pim->channel_oil_head, c_oil);
 
        if (PIM_DEBUG_MROUTE)
-               zlog_debug(
-                       "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
-                       __PRETTY_FUNCTION__, name, sg, input_vif_index);
+               zlog_debug("%s(%s): c_oil %s add",
+                               __func__, name, pim_str_sg_dump(sg));
+
        return c_oil;
 }
 
-void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
+struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
+               const char *name)
 {
        if (PIM_DEBUG_MROUTE) {
                struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
@@ -265,15 +194,32 @@ void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
                 * called by list_delete_all_node()
                 */
                c_oil->up = NULL;
-               listnode_delete(c_oil->pim->channel_oil_list, c_oil);
-               hash_release(c_oil->pim->channel_oil_hash, c_oil);
+               rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil);
 
                pim_channel_oil_free(c_oil);
+               return NULL;
+       }
+
+       return c_oil;
+}
+
+void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
+{
+       /* The upstream entry associated with a channel_oil is abt to be
+        * deleted. If the channel_oil is kept around because of other
+        * references we need to remove upstream based states out of it.
+        */
+       c_oil = pim_channel_oil_del(c_oil, __func__);
+       if (c_oil) {
+               /* note: here we assume that c_oil->up has already been
+                * cleared
+                */
+               pim_channel_update_mute(c_oil);
        }
 }
 
 int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
-                       uint32_t proto_mask)
+                       uint32_t proto_mask, const char *caller)
 {
        struct pim_interface *pim_ifp;
 
@@ -311,7 +257,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
 
        channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
 
-       if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
+       if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
+                       PIM_OIF_FLAG_PROTO_ANY) {
                if (PIM_DEBUG_MROUTE) {
                        char group_str[INET_ADDRSTRLEN];
                        char source_str[INET_ADDRSTRLEN];
@@ -333,8 +280,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
        }
 
        channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+       /* clear mute; will be re-evaluated when the OIF becomes valid again */
+       channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
 
-       if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+       if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
                if (PIM_DEBUG_MROUTE) {
                        char group_str[INET_ADDRSTRLEN];
                        char source_str[INET_ADDRSTRLEN];
@@ -363,8 +312,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
                pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
                               source_str, sizeof(source_str));
                zlog_debug(
-                       "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
-                       __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+                       "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
+                       __PRETTY_FUNCTION__, caller, source_str, group_str,
                        proto_mask, channel_oil->oil.mfcc_parent, oif->name,
                        pim_ifp->mroute_vif_index);
        }
@@ -372,13 +321,114 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
        return 0;
 }
 
+void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
+               struct interface *oif, const char *caller)
+{
+       struct pim_upstream *up = c_oil->up;
+
+       pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
+                       caller);
+
+       /* if an inherited OIF is being removed join-desired can change
+        * if the inherited OIL is now empty and KAT is running
+        */
+       if (up && up->sg.src.s_addr != INADDR_ANY &&
+                       pim_upstream_empty_inherited_olist(up))
+               pim_upstream_update_join_desired(up->pim, up);
+}
+
+static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
+               struct pim_interface *pim_ifp)
+{
+       struct pim_interface *pim_reg_ifp;
+       struct pim_interface *vxlan_ifp;
+       bool do_mute = false;
+       struct pim_instance *pim = c_oil->pim;
+
+       if (!c_oil->up)
+               return do_mute;
+
+       pim_reg_ifp = pim->regiface->info;
+       if (pim_ifp == pim_reg_ifp) {
+               /* suppress pimreg in the OIL if the mroute is not supposed to
+                * trigger register encapsulated data
+                */
+               if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
+                       do_mute = true;
+
+               return do_mute;
+       }
+
+       vxlan_ifp = pim_vxlan_get_term_ifp(pim);
+       if (pim_ifp == vxlan_ifp) {
+               /* 1. vxlan termination device must never be added to the
+                * origination mroute (and that can actually happen because
+                * of XG inheritance from the termination mroute) otherwise
+                * traffic will end up looping.
+                * PS: This check has also been extended to non-orig mroutes
+                * that have a local SIP as such mroutes can move back and
+                * forth between orig<=>non-orig type.
+                * 2. vxlan termination device should be removed from the non-DF
+                * to prevent duplicates to the overlay rxer
+                */
+               if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
+                       PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
+                       pim_vxlan_is_local_sip(c_oil->up))
+                       do_mute = true;
+
+               return do_mute;
+       }
+
+       return do_mute;
+}
+
+void pim_channel_update_oif_mute(struct channel_oil *c_oil,
+               struct pim_interface *pim_ifp)
+{
+       bool old_mute;
+       bool new_mute;
+
+       /* If pim_ifp is not a part of the OIL there is nothing to do */
+       if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+               return;
+
+       old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
+                       PIM_OIF_FLAG_MUTE);
+       new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
+       if (old_mute == new_mute)
+               return;
+
+       if (new_mute)
+               c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+                       PIM_OIF_FLAG_MUTE;
+       else
+               c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+                       ~PIM_OIF_FLAG_MUTE;
+
+       pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
+}
+
+/* pim_upstream has been set or cleared on the c_oil. re-eval mute state
+ * on all existing OIFs
+ */
+static void pim_channel_update_mute(struct channel_oil *c_oil)
+{
+       struct pim_interface *pim_reg_ifp;
+       struct pim_interface *vxlan_ifp;
+
+       pim_reg_ifp = c_oil->pim->regiface->info;
+       if (pim_reg_ifp)
+               pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
+       vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
+       if (vxlan_ifp)
+               pim_channel_update_oif_mute(c_oil, vxlan_ifp);
+}
 
 int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
-                       uint32_t proto_mask)
+                       uint32_t proto_mask, const char *caller)
 {
        struct pim_interface *pim_ifp;
        int old_ttl;
-       bool allow_iif_in_oil = false;
 
        /*
         * If we've gotten here we've gone bad, but let's
@@ -391,48 +441,6 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
 
        pim_ifp = oif->info;
 
-#ifdef PIM_ENFORCE_LOOPFREE_MFC
-       /*
-         Prevent creating MFC entry with OIF=IIF.
-
-         This is a protection against implementation mistakes.
-
-         PIM protocol implicitely ensures loopfree multicast topology.
-
-         IGMP must be protected against adding looped MFC entries created
-         by both source and receiver attached to the same interface. See
-         TODO T22.
-         We shall allow igmp to create upstream when it is DR for the intf.
-         Assume RP reachable via non DR.
-       */
-       if ((channel_oil->up &&
-           PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) ||
-           ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) {
-               allow_iif_in_oil = true;
-       }
-
-       if (!allow_iif_in_oil &&
-               pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
-               channel_oil->oil_inherited_rescan = 1;
-               if (PIM_DEBUG_MROUTE) {
-                       char group_str[INET_ADDRSTRLEN];
-                       char source_str[INET_ADDRSTRLEN];
-                       pim_inet4_dump("<group?>",
-                                      channel_oil->oil.mfcc_mcastgrp,
-                                      group_str, sizeof(group_str));
-                       pim_inet4_dump("<source?>",
-                                      channel_oil->oil.mfcc_origin, source_str,
-                                      sizeof(source_str));
-                       zlog_debug(
-                               "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
-                               __FILE__, __PRETTY_FUNCTION__, proto_mask,
-                               oif->name, pim_ifp->mroute_vif_index,
-                               source_str, group_str);
-               }
-               return -2;
-       }
-#endif
-
        /* Prevent single protocol from subscribing same interface to
           channel (S,G) multiple times */
        if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
@@ -517,11 +525,23 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
        channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
                PIM_MROUTE_MIN_TTL;
 
+       /* Some OIFs are held in a muted state i.e. the PIM state machine
+        * decided to include the OIF but additional status check such as
+        * MLAG DF role prevent it from being activated for traffic
+        * forwarding.
+        */
+       if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
+               channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+                       PIM_OIF_FLAG_MUTE;
+       else
+               channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+                       ~PIM_OIF_FLAG_MUTE;
+
        /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
         * valid to get installed in kernel.
         */
        if (channel_oil->oil.mfcc_parent != MAXVIFS) {
-               if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+               if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
                        if (PIM_DEBUG_MROUTE) {
                                char group_str[INET_ADDRSTRLEN];
                                char source_str[INET_ADDRSTRLEN];
@@ -557,8 +577,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
                pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
                               source_str, sizeof(source_str));
                zlog_debug(
-                       "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
-                       __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+                       "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
+                       __PRETTY_FUNCTION__, caller, source_str, group_str,
                        proto_mask, oif->name, pim_ifp->mroute_vif_index);
        }
 
@@ -567,19 +587,15 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
 
 int pim_channel_oil_empty(struct channel_oil *c_oil)
 {
-       static uint32_t zero[MAXVIFS];
-       static int inited = 0;
+       static struct mfcctl null_oil;
 
        if (!c_oil)
                return 1;
-       /*
-        * Not sure that this is necessary, but I would rather ensure
-        * that this works.
-        */
-       if (!inited) {
-               memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
-               inited = 1;
-       }
 
-       return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
+       /* exclude pimreg from the OIL when checking if the inherited_oil is
+        * non-NULL.
+        * pimreg device (in all vrfs) uses a vifi of
+        * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
+       return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1],
+               sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0]));
 }
index 319a1c91a32189afda223f42aab73de658316e24..788ddaa16c511e84a4cf8a9676cb892e44c09277 100644 (file)
@@ -21,6 +21,7 @@
 #define PIM_OIL_H
 
 #include "pim_mroute.h"
+#include "pim_iface.h"
 
 /*
  * Where did we get this (S,G) from?
@@ -38,6 +39,8 @@
        (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM      \
         | PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN)
 
+/* OIF is present in the OIL but must not be used for forwarding traffic */
+#define PIM_OIF_FLAG_MUTE         (1 << 4)
 /*
  * We need a pimreg vif id from the kernel.
  * Since ifindex == vif id for most cases and the number
@@ -87,10 +90,13 @@ struct channel_counts {
   installed: indicate if this entry is installed in the kernel.
 
 */
+PREDECL_RBTREE_UNIQ(rb_pim_oil)
 
 struct channel_oil {
        struct pim_instance *pim;
 
+       struct rb_pim_oil_item oil_rb;
+
        struct mfcctl oil;
        int installed;
        int oil_inherited_rescan;
@@ -103,6 +109,12 @@ struct channel_oil {
        time_t mroute_creation;
 };
 
+extern int pim_channel_oil_compare(const struct channel_oil *c1,
+                                  const struct channel_oil *c2);
+DECLARE_RBTREE_UNIQ(rb_pim_oil, struct channel_oil, oil_rb,
+                    pim_channel_oil_compare)
+
+
 extern struct list *pim_channel_oil_list;
 
 void pim_oil_init(struct pim_instance *pim);
@@ -113,18 +125,27 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
                                         struct prefix_sg *sg);
 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                                        struct prefix_sg *sg,
-                                       int input_vif_index, const char *name);
+                                       const char *name);
 void pim_channel_oil_change_iif(struct pim_instance *pim,
                                struct channel_oil *c_oil, int input_vif_index,
                                const char *name);
-void pim_channel_oil_del(struct channel_oil *c_oil, const char *name);
+struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
+               const char *name);
 
 int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif,
-                       uint32_t proto_mask);
+                       uint32_t proto_mask, const char *caller);
 int pim_channel_del_oif(struct channel_oil *c_oil, struct interface *oif,
-                       uint32_t proto_mask);
+                       uint32_t proto_mask, const char *caller);
 
 int pim_channel_oil_empty(struct channel_oil *c_oil);
 
 char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size);
+
+void pim_channel_update_oif_mute(struct channel_oil *c_oil,
+               struct pim_interface *pim_ifp);
+
+void pim_channel_oil_upstream_deref(struct channel_oil *c_oil);
+void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
+               struct interface *oif, const char *caller);
+
 #endif /* PIM_OIL_H */
index aff3fbed6c5b41b05261daf85f6e41961be39b6c..013f04bb00ce13392d3438e4e681c874b23bbab6 100644 (file)
@@ -59,7 +59,7 @@ void pim_register_join(struct pim_upstream *up)
        }
 
        pim_channel_add_oif(up->channel_oil, pim->regiface,
-                           PIM_OIF_FLAG_PROTO_PIM);
+                           PIM_OIF_FLAG_PROTO_PIM, __func__);
        up->reg_state = PIM_REG_JOIN;
        pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
 }
@@ -145,7 +145,7 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
        case PIM_REG_JOIN:
                upstream->reg_state = PIM_REG_PRUNE;
                pim_channel_del_oif(upstream->channel_oil, pim->regiface,
-                                   PIM_OIF_FLAG_PROTO_PIM);
+                                   PIM_OIF_FLAG_PROTO_PIM, __func__);
                pim_upstream_start_register_stop_timer(upstream, 0);
                pim_vxlan_update_sg_reg_state(pim, upstream,
                        false/*reg_join*/);
@@ -452,10 +452,9 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr,
                }
 
                if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
-                   || ((SwitchToSptDesired(pim_ifp->pim, &sg))
+                   || ((SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))
                        && pim_upstream_inherited_olist(pim_ifp->pim, upstream)
                                   == 0)) {
-                       // pim_scan_individual_oil (upstream->channel_oil);
                        pim_register_stop_send(ifp, &sg, dest_addr, src_addr);
                        sentRegisterStop = 1;
                } else {
@@ -464,7 +463,7 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr,
                                           upstream->sptbit);
                }
                if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
-                   || (SwitchToSptDesired(pim_ifp->pim, &sg))) {
+                   || (SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))) {
                        if (sentRegisterStop) {
                                pim_upstream_keep_alive_timer_start(
                                        upstream,
index 09529055e059b8bdeda25a69a2fce5559fa0cd28..2db39bac4bba1a0a362f57855a4f82f4b23557d8 100644 (file)
@@ -274,6 +274,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
 static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim)
 {
        pim_msdp_i_am_rp_changed(pim);
+       pim_upstream_reeval_use_rpt(pim);
 }
 
 void pim_rp_prefix_list_update(struct pim_instance *pim,
@@ -381,27 +382,17 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up)
 
        old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface;
 
-       rpf_result = pim_rpf_update(pim, up, &old_rpf);
+       rpf_result = pim_rpf_update(pim, up, &old_rpf, __func__);
        if (rpf_result == PIM_RPF_FAILURE)
                pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
 
        /* update kernel multicast forwarding cache (MFC) */
-       if (up->rpf.source_nexthop.interface && up->channel_oil) {
-               ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
-               int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
-               /* Pass Current selected NH vif index to mroute download */
-               if (vif_index)
-                       pim_scan_individual_oil(up->channel_oil, vif_index);
-               else {
-                       if (PIM_DEBUG_PIM_NHT)
-                               zlog_debug(
-                                 "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
-                                 __PRETTY_FUNCTION__, up->sg_str,
-                                 up->rpf.source_nexthop.interface->name);
-               }
-       }
+       if (up->rpf.source_nexthop.interface && up->channel_oil)
+               pim_upstream_mroute_iif_update(up->channel_oil, __func__);
 
-       if (rpf_result == PIM_RPF_CHANGED)
+       if (rpf_result == PIM_RPF_CHANGED ||
+                       (rpf_result == PIM_RPF_FAILURE &&
+                        old_rpf.source_nexthop.interface))
                pim_zebra_upstream_rpf_changed(pim, up, &old_rpf);
 
        pim_zebra_update_all_interfaces(pim);
@@ -455,7 +446,6 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr,
        struct prefix nht_p;
        struct route_node *rn;
        struct pim_upstream *up;
-       struct listnode *upnode;
 
        if (rp_addr.s_addr == INADDR_ANY ||
            rp_addr.s_addr == INADDR_NONE)
@@ -563,8 +553,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr,
                                        __PRETTY_FUNCTION__, buf, buf1);
                        }
 
-                       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
-                                                 up)) {
+                       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                                /* Find (*, G) upstream whose RP is not
                                 * configured yet
                                 */
@@ -659,7 +648,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr,
                           rn->lock);
        }
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (up->sg.src.s_addr == INADDR_ANY) {
                        struct prefix grp;
                        struct rp_info *trp_info;
@@ -732,7 +721,6 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr,
        bool was_plist = false;
        struct rp_info *trp_info;
        struct pim_upstream *up;
-       struct listnode *upnode;
        struct bsgrp_node *bsgrp = NULL;
        struct bsm_rpinfo *bsrp = NULL;
        char grp_str[PREFIX2STR_BUFFER];
@@ -809,7 +797,7 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr,
        rp_all = pim_rp_find_match_group(pim, &g_all);
 
        if (rp_all == rp_info) {
-               for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+               frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                        /* Find the upstream (*, G) whose upstream address is
                         * same as the deleted RP
                         */
@@ -861,7 +849,7 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr,
 
        pim_rp_refresh_group_to_rp_mapping(pim);
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                /* Find the upstream (*, G) whose upstream address is same as
                 * the deleted RP
                 */
@@ -902,7 +890,6 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr,
        int result = 0;
        struct rp_info *rp_info = NULL;
        struct pim_upstream *up;
-       struct listnode *upnode;
 
        rn = route_node_lookup(pim->rp_table, &group);
        if (!rn) {
@@ -951,7 +938,7 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr,
 
        listnode_add_sort(pim->rp_list, rp_info);
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (up->sg.src.s_addr == INADDR_ANY) {
                        struct prefix grp;
                        struct rp_info *trp_info;
@@ -1057,6 +1044,7 @@ void pim_rp_check_on_if_add(struct pim_interface *pim_ifp)
 
        if (i_am_rp_changed) {
                pim_msdp_i_am_rp_changed(pim);
+               pim_upstream_reeval_use_rpt(pim);
        }
 }
 
@@ -1099,6 +1087,7 @@ void pim_i_am_rp_re_evaluate(struct pim_instance *pim)
 
        if (i_am_rp_changed) {
                pim_msdp_i_am_rp_changed(pim);
+               pim_upstream_reeval_use_rpt(pim);
        }
 }
 
index d383ef5249454a2dda68c47449fc43fdb4f9c409..1eb5006b9406e373b67f62d31be8cc00dbaacef4 100644 (file)
@@ -195,7 +195,8 @@ static int nexthop_mismatch(const struct pim_nexthop *nh1,
 }
 
 enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
-                                  struct pim_upstream *up, struct pim_rpf *old)
+               struct pim_upstream *up, struct pim_rpf *old,
+               const char *caller)
 {
        struct pim_rpf *rpf = &up->rpf;
        struct pim_rpf saved;
@@ -207,13 +208,17 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
                return PIM_RPF_OK;
 
        if (up->upstream_addr.s_addr == INADDR_ANY) {
-               zlog_debug("%s: RP is not configured yet for %s",
-                       __PRETTY_FUNCTION__, up->sg_str);
+               zlog_debug("%s(%s): RP is not configured yet for %s",
+                       __func__, caller, up->sg_str);
                return PIM_RPF_OK;
        }
 
        saved.source_nexthop = rpf->source_nexthop;
        saved.rpf_addr = rpf->rpf_addr;
+       if (old) {
+               old->source_nexthop = saved.source_nexthop;
+               old->rpf_addr = saved.rpf_addr;
+       }
 
        nht_p.family = AF_INET;
        nht_p.prefixlen = IPV4_MAX_BITLEN;
@@ -238,8 +243,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
        rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up);
        if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) {
                /* RPF'(S,G) not found */
-               zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
-                          __FILE__, __PRETTY_FUNCTION__, up->sg_str);
+               zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
+                          __func__, caller, up->sg_str);
                /* warning only */
        }
 
@@ -251,8 +256,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
                        pim_addr_dump("<addr?>",
                                      &rpf->source_nexthop.mrib_nexthop_addr,
                                      nhaddr_str, sizeof(nhaddr_str));
-                       zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
-                __FILE__, __PRETTY_FUNCTION__,
+                       zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+                __func__, caller,
                 up->sg_str,
                 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
                 nhaddr_str,
@@ -269,8 +274,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
        if (saved.source_nexthop.interface != rpf->source_nexthop.interface) {
 
                if (PIM_DEBUG_ZEBRA) {
-                       zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
-                __FILE__, __PRETTY_FUNCTION__,
+                       zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
+                __func__, caller,
                 up->sg_str,
                 saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "<oldif?>",
                 rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
@@ -286,11 +291,6 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
            || saved.source_nexthop
                               .interface != rpf->source_nexthop.interface) {
 
-               /* return old rpf to caller ? */
-               if (old) {
-                       old->source_nexthop = saved.source_nexthop;
-                       old->rpf_addr = saved.rpf_addr;
-               }
                return PIM_RPF_CHANGED;
        }
 
@@ -307,12 +307,6 @@ void pim_upstream_rpf_clear(struct pim_instance *pim,
                            struct pim_upstream *up)
 {
        if (up->rpf.source_nexthop.interface) {
-               if (up->channel_oil)
-                       pim_channel_oil_change_iif(pim, up->channel_oil,
-                                                  MAXVIFS,
-                                                  __PRETTY_FUNCTION__);
-
-               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;
@@ -321,6 +315,7 @@ void pim_upstream_rpf_clear(struct pim_instance *pim,
                up->rpf.source_nexthop.mrib_route_metric =
                        router->infinite_assert_metric.route_metric;
                up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
+               pim_upstream_mroute_iif_update(up->channel_oil, __func__);
        }
 }
 
index df7b249289eeb9539c4c9ccd70a2b0eb82a468b5..f006519b7105b1415f8a866e5eece8df27a94e9b 100644 (file)
@@ -63,7 +63,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop,
                        struct in_addr addr, int neighbor_needed);
 enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
                                   struct pim_upstream *up,
-                                  struct pim_rpf *old);
+                                  struct pim_rpf *old, const char *caller);
 void pim_upstream_rpf_clear(struct pim_instance *pim,
                            struct pim_upstream *up);
 int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf);
index e3138360c825feb45a2e85adcf6bc358d8f77f5f..7b121c9136bbc2708251ab1609c145f6ef1d5c18 100644 (file)
@@ -138,7 +138,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif,
                        } else {
                                /* input interface changed */
                                s_route->iif = iif_index;
-                               pim_channel_oil_change_iif(pim, &s_route->c_oil,
+                               pim_static_mroute_iif_update(&s_route->c_oil,
                                                           iif_index,
                                                           __PRETTY_FUNCTION__);
 
@@ -179,7 +179,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif,
 
        s_route->c_oil.pim = pim;
 
-       if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
+       if (pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
                char gifaddr_str[INET_ADDRSTRLEN];
                char sifaddr_str[INET_ADDRSTRLEN];
                pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
@@ -264,7 +264,7 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif,
                        if (s_route->c_oil.oil_ref_count <= 0
                                    ? pim_mroute_del(&s_route->c_oil,
                                                     __PRETTY_FUNCTION__)
-                                   : pim_mroute_add(&s_route->c_oil,
+                                   : pim_static_mroute_add(&s_route->c_oil,
                                                     __PRETTY_FUNCTION__)) {
                                char gifaddr_str[INET_ADDRSTRLEN];
                                char sifaddr_str[INET_ADDRSTRLEN];
index 40b7e3d23657c03b4d0b9bff0b68b2ed0959193e..afd10bd3dba6293b73b0f9873d196e2468ce37fd 100644 (file)
@@ -78,8 +78,13 @@ static void pim_upstream_remove_children(struct pim_instance *pim,
                        child = pim_upstream_del(pim, child,
                                                 __PRETTY_FUNCTION__);
                }
-               if (child)
+               if (child) {
                        child->parent = NULL;
+                       if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+                               pim_upstream_mroute_iif_update(
+                                               child->channel_oil,
+                                               __func__);
+               }
        }
        list_delete(&up->sources);
 }
@@ -93,7 +98,6 @@ static void pim_upstream_find_new_children(struct pim_instance *pim,
                                           struct pim_upstream *up)
 {
        struct pim_upstream *child;
-       struct listnode *ch_node;
 
        if ((up->sg.src.s_addr != INADDR_ANY)
            && (up->sg.grp.s_addr != INADDR_ANY))
@@ -103,12 +107,16 @@ static void pim_upstream_find_new_children(struct pim_instance *pim,
            && (up->sg.grp.s_addr == INADDR_ANY))
                return;
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, ch_node, child)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, child) {
                if ((up->sg.grp.s_addr != INADDR_ANY)
                    && (child->sg.grp.s_addr == up->sg.grp.s_addr)
                    && (child != up)) {
                        child->parent = up;
                        listnode_add_sort(up->sources, child);
+                       if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+                               pim_upstream_mroute_iif_update(
+                                               child->channel_oil,
+                                               __func__);
                }
        }
 }
@@ -141,14 +149,22 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim,
 
 static void upstream_channel_oil_detach(struct pim_upstream *up)
 {
-       if (up->channel_oil) {
+       struct channel_oil *channel_oil = up->channel_oil;
+
+       if (channel_oil) {
                /* Detaching from channel_oil, channel_oil may exist post del,
                   but upstream would not keep reference of it
                 */
-               up->channel_oil->up = NULL;
-               pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__);
+               channel_oil->up = NULL;
                up->channel_oil = NULL;
+
+               /* attempt to delete channel_oil; if channel_oil is being held
+                * because of other references cleanup info such as "Mute"
+                * inferred from the parent upstream
+                */
+               pim_channel_oil_upstream_deref(channel_oil);
        }
+
 }
 
 struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
@@ -173,6 +189,10 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
        if (up->ref_count >= 1)
                return up;
 
+       if (PIM_DEBUG_TRACE)
+               zlog_debug("pim_upstream free vrf:%s %s flags 0x%x",
+                          pim->vrf->name, up->sg_str, up->flags);
+
        THREAD_OFF(up->t_ka_timer);
        THREAD_OFF(up->t_rs_timer);
        THREAD_OFF(up->t_msdp_reg_timer);
@@ -213,8 +233,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
                listnode_delete(up->parent->sources, up);
        up->parent = NULL;
 
-       listnode_delete(pim->upstream_list, up);
-       hash_release(pim->upstream_hash, up);
+       rb_pim_upstream_del(&pim->upstream_head, up);
 
        if (notify_msdp) {
                pim_msdp_up_del(pim, &up->sg);
@@ -316,7 +335,7 @@ static void join_timer_stop(struct pim_upstream *up)
                                        up->rpf.rpf_addr.u.prefix4);
 
        if (nbr)
-               pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+               pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
 
        pim_jp_agg_upstream_verification(up, false);
 }
@@ -338,7 +357,7 @@ void join_timer_start(struct pim_upstream *up)
        }
 
        if (nbr)
-               pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1);
+               pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr);
        else {
                THREAD_OFF(up->t_join_timer);
                thread_add_timer(router->master, on_join_timer, up,
@@ -511,15 +530,15 @@ static int pim_upstream_could_register(struct pim_upstream *up)
  * we re-revaluate register setup for existing upstream entries */
 void pim_upstream_register_reevaluate(struct pim_instance *pim)
 {
-       struct listnode *upnode;
        struct pim_upstream *up;
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                /* If FHR is set CouldRegister is True. Also check if the flow
                 * is actually active; if it is not kat setup will trigger
                 * source
                 * registration whenever the flow becomes active. */
-               if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer)
+               if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+                       !pim_upstream_is_kat_running(up))
                        continue;
 
                if (pim_is_grp_ssm(pim, up->sg.grp)) {
@@ -532,7 +551,8 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim)
                                /* remove regiface from the OIL if it is there*/
                                pim_channel_del_oif(up->channel_oil,
                                                    pim->regiface,
-                                                   PIM_OIF_FLAG_PROTO_PIM);
+                                                   PIM_OIF_FLAG_PROTO_PIM,
+                                                       __func__);
                                up->reg_state = PIM_REG_NOINFO;
                        }
                } else {
@@ -544,13 +564,86 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim)
                                                up->sg_str);
                                pim_channel_add_oif(up->channel_oil,
                                                    pim->regiface,
-                                                   PIM_OIF_FLAG_PROTO_PIM);
+                                                   PIM_OIF_FLAG_PROTO_PIM,
+                                                       __func__);
                                up->reg_state = PIM_REG_JOIN;
                        }
                }
        }
 }
 
+/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
+ * forward a S -
+ * 1. along the SPT if SPTbit is set
+ * 2. and along the RPT if SPTbit is not set
+ * If forwarding is hw accelerated i.e. control and dataplane components
+ * are separate you may not be able to reliably set SPT bit on intermediate
+ * routers while still fowarding on the (S,G,rpt).
+ *
+ * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
+ * criteria to decide between using the RPT vs. SPT for forwarding.
+ */
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+                       bool update_mroute)
+{
+       bool old_use_rpt;
+       bool new_use_rpt;
+
+       if (up->sg.src.s_addr == INADDR_ANY)
+               return;
+
+       old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+
+       /* We will use the SPT (IIF=RPF_interface(S) if -
+        * 1. We have decided to join the SPT
+        * 2. We are FHR
+        * 3. Source is directly connected
+        * 4. We are RP (parent's IIF is lo or vrf-device)
+        * In all other cases the source will stay along the RPT and
+        * IIF=RPF_interface(RP).
+        */
+       if (up->join_state == PIM_UPSTREAM_JOINED ||
+                       PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+                       pim_if_connected_to_source(
+                               up->rpf.source_nexthop.interface,
+                               up->sg.src) ||
+                       /* XXX - need to switch this to a more efficient
+                        * lookup API
+                        */
+                       I_am_RP(up->pim, up->sg.grp))
+               /* use SPT */
+               PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags);
+       else
+               /* use RPT */
+               PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags);
+
+       new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+       if (old_use_rpt != new_use_rpt) {
+               if (PIM_DEBUG_PIM_EVENTS)
+                       zlog_debug("%s switched from %s to %s",
+                                       up->sg_str,
+                                       old_use_rpt?"RPT":"SPT",
+                                       new_use_rpt?"RPT":"SPT");
+               if (update_mroute)
+                       pim_upstream_mroute_add(up->channel_oil, __func__);
+       }
+}
+
+/* some events like RP change require re-evaluation of SGrpt across
+ * all groups
+ */
+void pim_upstream_reeval_use_rpt(struct pim_instance *pim)
+{
+       struct pim_upstream *up;
+
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+               if (up->sg.src.s_addr == INADDR_ANY)
+                       continue;
+
+               pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+       }
+}
+
 void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
                         enum pim_upstream_state new_state)
 {
@@ -603,21 +696,47 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
                                join_timer_start(up);
                        }
                }
+               if (old_state != new_state)
+                       pim_upstream_update_use_rpt(up, true /*update_mroute*/);
        } else {
+               bool old_use_rpt;
+               bool new_use_rpt;
+               bool send_xg_jp = false;
 
                forward_off(up);
                if (old_state == PIM_UPSTREAM_JOINED)
                        pim_msdp_up_join_state_changed(pim, up);
 
+               if (old_state != new_state) {
+                       old_use_rpt =
+                               !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+                       pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+                       new_use_rpt =
+                               !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+                       if (new_use_rpt &&
+                                       (new_use_rpt != old_use_rpt) &&
+                                       up->parent)
+                               /* we have decided to switch from the SPT back
+                                * to the RPT which means we need to cancel
+                                * any previously sent SGrpt prunes immediately
+                                */
+                               send_xg_jp = true;
+               }
+
                /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
                   RP.
                   If I am RP for G then send S,G prune to its IIF. */
-               if (pim_upstream_is_sg_rpt(up) && up->parent
-                   && !I_am_RP(pim, up->sg.grp)) {
+               if (pim_upstream_is_sg_rpt(up) && up->parent &&
+                               !I_am_RP(pim, up->sg.grp))
+                       send_xg_jp = true;
+               else
+                       pim_jp_agg_single_upstream_send(&up->rpf, up,
+                                                       0 /* prune */);
+
+               if (send_xg_jp) {
                        if (PIM_DEBUG_PIM_TRACE_DETAIL)
                                zlog_debug(
-                                 "%s: *,G IIF %s S,G IIF %s ",
-                                 __PRETTY_FUNCTION__,
+                                 "re-join RPT; *,G IIF %s S,G IIF %s ",
                                  up->parent->rpf.source_nexthop.interface ?
                                  up->parent->rpf.source_nexthop.interface->name
                                  : "Unknown",
@@ -627,18 +746,14 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
                        pim_jp_agg_single_upstream_send(&up->parent->rpf,
                                                        up->parent,
                                                        1 /* (W,G) Join */);
-               } else
-                       pim_jp_agg_single_upstream_send(&up->rpf, up,
-                                                       0 /* prune */);
+               }
                join_timer_stop(up);
        }
 }
 
-int pim_upstream_compare(void *arg1, void *arg2)
+int pim_upstream_compare(const struct pim_upstream *up1,
+                        const struct pim_upstream *up2)
 {
-       const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
-       const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
-
        if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
                return -1;
 
@@ -683,12 +798,13 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
 
        up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
 
+       up->pim = pim;
        up->sg = *sg;
        pim_str_sg_set(sg, up->sg_str);
        if (ch)
                ch->upstream = up;
 
-       up = hash_get(pim->upstream_hash, up, hash_alloc_intern);
+       rb_pim_upstream_add(&pim->upstream_head, up);
        /* Set up->upstream_addr as INADDR_ANY, if RP is not
         * configured and retain the upstream data structure
         */
@@ -702,7 +818,8 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
        up->parent = pim_upstream_find_parent(pim, up);
        if (up->sg.src.s_addr == INADDR_ANY) {
                up->sources = list_new();
-               up->sources->cmp = pim_upstream_compare;
+               up->sources->cmp =
+                       (int (*)(void *, void *))pim_upstream_compare;
        } else
                up->sources = NULL;
 
@@ -717,7 +834,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
        up->reg_state = PIM_REG_NOINFO;
        up->state_transition = pim_time_monotonic_sec();
        up->channel_oil =
-               pim_channel_oil_add(pim, &up->sg, MAXVIFS, __PRETTY_FUNCTION__);
+               pim_channel_oil_add(pim, &up->sg, __PRETTY_FUNCTION__);
        up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
 
        up->rpf.source_nexthop.interface = NULL;
@@ -742,15 +859,17 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
                pim_upstream_fill_static_iif(up, incoming);
                pim_ifp = up->rpf.source_nexthop.interface->info;
                assert(pim_ifp);
-               pim_channel_oil_change_iif(pim, up->channel_oil,
-                                          pim_ifp->mroute_vif_index,
-                                          __PRETTY_FUNCTION__);
+               pim_upstream_update_use_rpt(up,
+                               false /*update_mroute*/);
+               pim_upstream_mroute_iif_update(up->channel_oil, __func__);
 
                if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags))
                        pim_upstream_keep_alive_timer_start(
                                up, pim->keep_alive_time);
        } else if (up->upstream_addr.s_addr != INADDR_ANY) {
-               rpf_result = pim_rpf_update(pim, up, NULL);
+               pim_upstream_update_use_rpt(up,
+                               false /*update_mroute*/);
+               rpf_result = pim_rpf_update(pim, up, NULL, __func__);
                if (rpf_result == PIM_RPF_FAILURE) {
                        if (PIM_DEBUG_PIM_TRACE)
                                zlog_debug(
@@ -759,17 +878,11 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
                }
 
                if (up->rpf.source_nexthop.interface) {
-                       pim_ifp = up->rpf.source_nexthop.interface->info;
-                       if (pim_ifp)
-                               pim_channel_oil_change_iif(
-                                       pim, up->channel_oil,
-                                       pim_ifp->mroute_vif_index,
-                                       __PRETTY_FUNCTION__);
+                       pim_upstream_mroute_iif_update(up->channel_oil,
+                                       __func__);
                }
        }
 
-       listnode_add_sort(pim->upstream_list, up);
-
        if (PIM_DEBUG_PIM_TRACE) {
                zlog_debug(
                        "%s: Created Upstream %s upstream_addr %s ref count %d increment",
@@ -787,40 +900,40 @@ struct pim_upstream *pim_upstream_find(struct pim_instance *pim,
        struct pim_upstream *up = NULL;
 
        lookup.sg = *sg;
-       up = hash_lookup(pim->upstream_hash, &lookup);
+       up = rb_pim_upstream_find(&pim->upstream_head, &lookup);
        return up;
 }
 
 struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
-                                             struct interface *incoming,
-                                             int flags, const char *name)
+               struct interface *incoming,
+               int flags, const char *name)
 {
-       struct pim_upstream *up;
-       struct pim_interface *pim_ifp;
-
-       pim_ifp = incoming->info;
-
-       up = pim_upstream_find(pim_ifp->pim, sg);
-
-       if (up) {
-               if (!(up->flags & flags)) {
-                       up->flags |= flags;
-                       up->ref_count++;
-                       if (PIM_DEBUG_PIM_TRACE)
-                               zlog_debug(
-                                       "%s(%s): upstream %s ref count %d increment",
-                                       __PRETTY_FUNCTION__, name, up->sg_str,
-                                       up->ref_count);
-               }
-       } else
-               up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
-                                     NULL);
+       struct pim_interface *pim_ifp = incoming->info;
 
-       return up;
+       return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
+                               NULL));
 }
 
 void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
 {
+       /* when we go from non-FHR to FHR we need to re-eval traffic
+        * forwarding path
+        */
+       if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) &&
+                       PIM_UPSTREAM_FLAG_TEST_FHR(flags)) {
+               PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+               pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+       }
+
+       /* re-eval joinDesired; clearing peer-msdp-sa flag can
+        * cause JD to change
+        */
+       if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) &&
+                       PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) {
+               PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags);
+               pim_upstream_update_join_desired(up->pim, up);
+       }
+
        up->flags |= flags;
        ++up->ref_count;
        if (PIM_DEBUG_PIM_TRACE)
@@ -864,6 +977,36 @@ struct pim_upstream *pim_upstream_add(struct pim_instance *pim,
        return up;
 }
 
+/*
+ * Passed in up must be the upstream for ch.  starch is NULL if no
+ * information
+ * This function is copied over from
+ * pim_upstream_evaluate_join_desired_interface but limited to
+ * parent (*,G)'s includes/joins.
+ */
+int pim_upstream_eval_inherit_if(struct pim_upstream *up,
+                                                struct pim_ifchannel *ch,
+                                                struct pim_ifchannel *starch)
+{
+       /* if there is an explicit prune for this interface we cannot
+        * add it to the OIL
+        */
+       if (ch) {
+               if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+                       return 0;
+       }
+
+       /* Check if the OIF can be inherited fron the (*,G) entry
+        */
+       if (starch) {
+               if (!pim_macro_ch_lost_assert(starch)
+                   && pim_macro_chisin_joins_or_include(starch))
+                       return 1;
+       }
+
+       return 0;
+}
+
 /*
  * Passed in up must be the upstream for ch.  starch is NULL if no
  * information
@@ -885,8 +1028,14 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up,
         * joins (*,G)
         */
        if (starch) {
+               /* XXX: check on this with donald
+                * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
+                * upstream flags?
+                */
+#if 0
                if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags))
                        return 0;
+#endif
 
                if (!pim_macro_ch_lost_assert(starch)
                    && pim_macro_chisin_joins_or_include(starch))
@@ -896,56 +1045,77 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up,
        return 0;
 }
 
-/*
-  Evaluate JoinDesired(S,G):
-
-  JoinDesired(S,G) is true if there is a downstream (S,G) interface I
-  in the set:
-
-  inherited_olist(S,G) =
-  joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
-
-  JoinDesired(S,G) may be affected by changes in the following:
-
-  pim_ifp->primary_address
-  pim_ifp->pim_dr_addr
-  ch->ifassert_winner_metric
-  ch->ifassert_winner
-  ch->local_ifmembership
-  ch->ifjoin_state
-  ch->upstream->rpf.source_nexthop.mrib_metric_preference
-  ch->upstream->rpf.source_nexthop.mrib_route_metric
-  ch->upstream->rpf.source_nexthop.interface
-
-  See also pim_upstream_update_join_desired() below.
+/* Returns true if immediate OIL is empty and is used to evaluate
+ * JoinDesired. See pim_upstream_evaluate_join_desired.
  */
-int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim,
                                       struct pim_upstream *up)
 {
        struct interface *ifp;
-       struct pim_ifchannel *ch, *starch;
-       struct pim_upstream *starup = up->parent;
-       int ret = 0;
+       struct pim_ifchannel *ch;
 
        FOR_ALL_INTERFACES (pim->vrf, ifp) {
                if (!ifp->info)
                        continue;
 
                ch = pim_ifchannel_find(ifp, &up->sg);
-
-               if (starup)
-                       starch = pim_ifchannel_find(ifp, &starup->sg);
-               else
-                       starch = NULL;
-
-               if (!ch && !starch)
+               if (!ch)
                        continue;
 
-               ret += pim_upstream_evaluate_join_desired_interface(up, ch,
-                                                                   starch);
+               /* If we have even one immediate OIF we can return with
+                * not-empty
+                */
+               if (pim_upstream_evaluate_join_desired_interface(up, ch,
+                                           NULL /* starch */))
+                       return false;
        } /* scan iface channel list */
 
-       return ret; /* false */
+       /* immediate_oil is empty */
+       return true;
+}
+
+
+static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up)
+{
+       return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags);
+}
+
+/*
+ *   bool JoinDesired(*,G) {
+ *       if (immediate_olist(*,G) != NULL)
+ *           return TRUE
+ *       else
+ *           return FALSE
+ *   }
+ *
+ *   bool JoinDesired(S,G) {
+ *       return( immediate_olist(S,G) != NULL
+ *           OR ( KeepaliveTimer(S,G) is running
+ *           AND inherited_olist(S,G) != NULL ) )
+ *   }
+ */
+int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+                                      struct pim_upstream *up)
+{
+       bool empty_imm_oil;
+       bool empty_inh_oil;
+
+       empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up);
+
+       /* (*,G) */
+       if (up->sg.src.s_addr == INADDR_ANY)
+               return !empty_imm_oil;
+
+       /* (S,G) */
+       if (!empty_imm_oil)
+               return true;
+       empty_inh_oil = pim_upstream_empty_inherited_olist(up);
+       if (!empty_inh_oil &&
+                       (pim_upstream_is_kat_running(up) ||
+                        pim_upstream_is_msdp_peer_sa(up)))
+               return true;
+
+       return false;
 }
 
 /*
@@ -990,15 +1160,12 @@ void pim_upstream_update_join_desired(struct pim_instance *pim,
 void pim_upstream_rpf_genid_changed(struct pim_instance *pim,
                                    struct in_addr neigh_addr)
 {
-       struct listnode *up_node;
-       struct listnode *up_nextnode;
        struct pim_upstream *up;
 
        /*
         * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
         */
-       for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
-
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (PIM_DEBUG_PIM_TRACE) {
                        char neigh_str[INET_ADDRSTRLEN];
                        char rpf_addr_str[PREFIX_STRLEN];
@@ -1116,7 +1283,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim,
        THREAD_OFF(up->t_rs_timer);
        /* remove regiface from the OIL if it is there*/
        pim_channel_del_oif(up->channel_oil, pim->regiface,
-                           PIM_OIF_FLAG_PROTO_PIM);
+                           PIM_OIF_FLAG_PROTO_PIM, __func__);
        /* clear the register state */
        up->reg_state = PIM_REG_NOINFO;
        PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
@@ -1136,6 +1303,7 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
                PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
                if (up->reg_state == PIM_REG_NOINFO)
                        pim_register_join(up);
+               pim_upstream_update_use_rpt(up, true /*update_mroute*/);
        }
 }
 
@@ -1171,6 +1339,9 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc(
        /* source is no longer active - pull the SA from MSDP's cache */
        pim_msdp_sa_local_del(pim, &up->sg);
 
+       /* JoinDesired can change when KAT is started or stopped */
+       pim_upstream_update_join_desired(pim, up);
+
        /* if entry was created because of activity we need to deref it */
        if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
                pim_upstream_fhr_kat_expiry(pim, up);
@@ -1233,6 +1404,8 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time)
        /* any time keepalive is started against a SG we will have to
         * re-evaluate our active source database */
        pim_msdp_sa_local_update(up);
+       /* JoinDesired can change when KAT is started or stopped */
+       pim_upstream_update_join_desired(up->pim, up);
 }
 
 /* MSDP on RP needs to know if a source is registerable to this RP */
@@ -1282,7 +1455,7 @@ void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
  *  SwitchToSptDesired(S,G) return true once a single packet has been
  *  received for the source and group.
  */
-int pim_upstream_switch_to_spt_desired(struct pim_instance *pim,
+int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
                                       struct prefix_sg *sg)
 {
        if (I_am_RP(pim, sg->grp))
@@ -1445,7 +1618,8 @@ static int pim_upstream_register_stop_timer(struct thread *t)
        case PIM_REG_JOIN_PENDING:
                up->reg_state = PIM_REG_JOIN;
                pim_channel_add_oif(up->channel_oil, pim->regiface,
-                                   PIM_OIF_FLAG_PROTO_PIM);
+                                   PIM_OIF_FLAG_PROTO_PIM,
+                                       __func__);
                pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
                break;
        case PIM_REG_JOIN:
@@ -1546,7 +1720,8 @@ int pim_upstream_inherited_olist_decide(struct pim_instance *pim,
                        if (!ch)
                                flag = PIM_OIF_FLAG_PROTO_STAR;
 
-                       pim_channel_add_oif(up->channel_oil, ifp, flag);
+                       pim_channel_add_oif(up->channel_oil, ifp, flag,
+                                       __func__);
                        output_intf++;
                }
        }
@@ -1581,9 +1756,9 @@ int pim_upstream_inherited_olist(struct pim_instance *pim,
         * switch on a stick so turn on forwarding to just accept the
         * incoming packets so we don't bother the other stuff!
         */
-       if (output_intf)
-               pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED);
-       else
+       pim_upstream_update_join_desired(pim, up);
+
+       if (!output_intf)
                forward_on(up);
 
        return output_intf;
@@ -1602,14 +1777,14 @@ int pim_upstream_empty_inherited_olist(struct pim_upstream *up)
  */
 void pim_upstream_find_new_rpf(struct pim_instance *pim)
 {
-       struct listnode *up_node;
-       struct listnode *up_nextnode;
        struct pim_upstream *up;
+       struct pim_rpf old;
+       enum pim_rpf_result rpf_result;
 
        /*
         * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
         */
-       for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (up->upstream_addr.s_addr == INADDR_ANY) {
                        if (PIM_DEBUG_PIM_TRACE)
                                zlog_debug(
@@ -1623,9 +1798,19 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim)
                                zlog_debug(
                                        "%s: Upstream %s without a path to send join, checking",
                                        __PRETTY_FUNCTION__, up->sg_str);
-                       pim_rpf_update(pim, up, NULL);
+                       old.source_nexthop.interface =
+                               up->rpf.source_nexthop.interface;
+                       rpf_result = pim_rpf_update(pim, up, &old, __func__);
+                       if (rpf_result == PIM_RPF_CHANGED ||
+                                       (rpf_result == PIM_RPF_FAILURE &&
+                                        old.source_nexthop.interface))
+                               pim_zebra_upstream_rpf_changed(pim, up, &old);
+                       /* update kernel multicast forwarding cache (MFC) */
+                       pim_upstream_mroute_iif_update(up->channel_oil,
+                                       __func__);
                }
        }
+       pim_zebra_update_all_interfaces(pim);
 }
 
 unsigned int pim_upstream_hash_key(const void *arg)
@@ -1639,18 +1824,11 @@ void pim_upstream_terminate(struct pim_instance *pim)
 {
        struct pim_upstream *up;
 
-       if (pim->upstream_list) {
-               while (pim->upstream_list->count) {
-                       up = listnode_head(pim->upstream_list);
-                       pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
-               }
-
-               list_delete(&pim->upstream_list);
+       while ((up = rb_pim_upstream_first(&pim->upstream_head))) {
+               pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
        }
 
-       if (pim->upstream_hash)
-               hash_free(pim->upstream_hash);
-       pim->upstream_hash = NULL;
+       rb_pim_upstream_fini(&pim->upstream_head);
 
        if (pim->upstream_sg_wheel)
                wheel_delete(pim->upstream_sg_wheel);
@@ -1685,30 +1863,30 @@ bool pim_upstream_equal(const void *arg1, const void *arg2)
  */
 static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
 {
-       struct pim_instance *pim = up->channel_oil->pim;
+       struct channel_oil *c_oil = up->channel_oil;
+       struct interface *ifp = up->rpf.source_nexthop.interface;
+       struct pim_interface *pim_ifp;
+
+       /* "iif == RPF_interface(S)" check is not easy to do as the info
+        * we get from the kernel/ASIC is really a "lookup/key hit".
+        * So we will do an approximate check here to avoid starting KAT
+        * because of (S,G,rpt) forwarding on a non-LHR.
+        */
+       if (!ifp)
+               return false;
 
-       /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
-        * so we will skip that here */
-       if (up->rpf.source_nexthop.interface &&
-               pim_if_connected_to_source(up->rpf.source_nexthop.interface,
+       pim_ifp = ifp->info;
+       if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent)
+               return false;
+
+       if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
                                       up->sg.src)) {
                return true;
        }
 
        if ((up->join_state == PIM_UPSTREAM_JOINED)
-           && !pim_upstream_empty_inherited_olist(up)) {
-               /* XXX: I have added this RP check just for 3.2 and it's a
-                * digression from
-                * what rfc-4601 says. Till now we were only running KAT on FHR
-                * and RP and
-                * there is some angst around making the change to run it all
-                * routers that
-                * maintain the (S, G) state. This is tracked via CM-13601 and
-                * MUST be
-                * removed to handle spt turn-arounds correctly in a 3-tier clos
-                */
-               if (I_am_RP(pim, up->sg.grp))
-                       return true;
+                       && !pim_upstream_empty_inherited_olist(up)) {
+               return true;
        }
 
        return false;
@@ -1793,9 +1971,8 @@ static void pim_upstream_sg_running(void *arg)
 void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim)
 {
        struct pim_upstream *up;
-       struct listnode *node;
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (up->sg.src.s_addr != INADDR_ANY)
                        continue;
 
@@ -1803,7 +1980,7 @@ void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim)
                        continue;
 
                pim_channel_add_oif(up->channel_oil, pim->regiface,
-                                   PIM_OIF_FLAG_PROTO_IGMP);
+                                   PIM_OIF_FLAG_PROTO_IGMP, __func__);
        }
 }
 
@@ -1833,7 +2010,6 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim,
                                         const char *nlist)
 {
        struct pim_upstream *up;
-       struct listnode *node;
        struct prefix_list *np;
        struct prefix g;
        enum prefix_list_type apply_new;
@@ -1843,7 +2019,7 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim,
        g.family = AF_INET;
        g.prefixlen = IPV4_MAX_PREFIXLEN;
 
-       for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) {
+       frr_each (rb_pim_upstream, &pim->upstream_head, up) {
                if (up->sg.src.s_addr != INADDR_ANY)
                        continue;
 
@@ -1852,17 +2028,18 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim,
 
                if (!nlist) {
                        pim_channel_del_oif(up->channel_oil, pim->regiface,
-                                           PIM_OIF_FLAG_PROTO_IGMP);
+                                       PIM_OIF_FLAG_PROTO_IGMP, __func__);
                        continue;
                }
                g.u.prefix4 = up->sg.grp;
                apply_new = prefix_list_apply(np, &g);
                if (apply_new == PREFIX_DENY)
                        pim_channel_add_oif(up->channel_oil, pim->regiface,
-                                           PIM_OIF_FLAG_PROTO_IGMP);
+                                           PIM_OIF_FLAG_PROTO_IGMP,
+                                               __func__);
                else
                        pim_channel_del_oif(up->channel_oil, pim->regiface,
-                                           PIM_OIF_FLAG_PROTO_IGMP);
+                                       PIM_OIF_FLAG_PROTO_IGMP, __func__);
        }
 }
 
@@ -1876,11 +2053,5 @@ void pim_upstream_init(struct pim_instance *pim)
                wheel_init(router->master, 31000, 100, pim_upstream_hash_key,
                           pim_upstream_sg_running, name);
 
-       snprintf(name, 64, "PIM %s Upstream Hash",
-                pim->vrf->name);
-       pim->upstream_hash = hash_create_size(8192, pim_upstream_hash_key,
-                                             pim_upstream_equal, name);
-
-       pim->upstream_list = list_new();
-       pim->upstream_list->cmp = pim_upstream_compare;
+       rb_pim_upstream_init(&pim->upstream_head);
 }
index c6c9291eed493a1559f74b73728465d56107375d..1eb2052bb3380ac550f0923c479c6ab41d6029ea 100644 (file)
  * associated with an upstream
  */
 #define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE             (1 << 19)
+/* By default as SG entry will use the SPT for forwarding traffic
+ * unless it was setup as a result of a Prune(S,G,rpt) from a
+ * downstream router and has JoinDesired(S,G) as False.
+ * This flag is only relevant for (S,G) entries.
+ */
+#define PIM_UPSTREAM_FLAG_MASK_USE_RPT                 (1 << 20)
 
 #define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF
 
 #define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
 #define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
 #define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT)
 
 #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
 #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
 #define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)
 #define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
 #define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
+#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT)
 
 #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
 #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
 #define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
 #define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
 #define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT)
 
 enum pim_upstream_state {
        PIM_UPSTREAM_NOTJOINED,
@@ -160,6 +169,7 @@ enum pim_upstream_sptbit {
        PIM_UPSTREAM_SPTBIT_TRUE
 };
 
+PREDECL_RBTREE_UNIQ(rb_pim_upstream);
 /*
   Upstream (S,G) channel in Joined state
   (S,G) in the "Not Joined" state is not represented
@@ -188,6 +198,8 @@ enum pim_upstream_sptbit {
 
 */
 struct pim_upstream {
+       struct pim_instance *pim;
+       struct rb_pim_upstream_item upstream_rb;
        struct pim_upstream *parent;
        struct in_addr upstream_addr;     /* Who we are talking to */
        struct in_addr upstream_register; /*Who we received a register from*/
@@ -232,6 +244,11 @@ struct pim_upstream {
        int64_t state_transition; /* Record current state uptime */
 };
 
+static inline bool pim_upstream_is_kat_running(struct pim_upstream *up)
+{
+       return (up->t_ka_timer != NULL);
+}
+
 struct pim_upstream *pim_upstream_find(struct pim_instance *pim,
                                       struct prefix_sg *sg);
 struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
@@ -252,6 +269,9 @@ int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
 int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up,
                                                 struct pim_ifchannel *ch,
                                                 struct pim_ifchannel *starch);
+int pim_upstream_eval_inherit_if(struct pim_upstream *up,
+                                                struct pim_ifchannel *ch,
+                                                struct pim_ifchannel *starch);
 void pim_upstream_update_join_desired(struct pim_instance *pim,
                                      struct pim_upstream *up);
 
@@ -274,9 +294,9 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up);
 void pim_upstream_keep_alive_timer_start(struct pim_upstream *up,
                                         uint32_t time);
 
-int pim_upstream_switch_to_spt_desired(struct pim_instance *pim,
+int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
                                       struct prefix_sg *sg);
-#define SwitchToSptDesired(pim, sg) pim_upstream_switch_to_spt_desired (pim, sg)
+#define SwitchToSptDesiredOnRp(pim, sg) pim_upstream_switch_to_spt_desired_on_rp (pim, sg)
 int pim_upstream_is_sg_rpt(struct pim_upstream *up);
 
 void pim_upstream_set_sptbit(struct pim_upstream *up,
@@ -308,7 +328,11 @@ void pim_upstream_init(struct pim_instance *pim);
 void pim_upstream_terminate(struct pim_instance *pim);
 
 void join_timer_start(struct pim_upstream *up);
-int pim_upstream_compare(void *arg1, void *arg2);
+int pim_upstream_compare(const struct pim_upstream *up1,
+                        const struct pim_upstream *up2);
+DECLARE_RBTREE_UNIQ(rb_pim_upstream, struct pim_upstream, upstream_rb,
+                   pim_upstream_compare)
+
 void pim_upstream_register_reevaluate(struct pim_instance *pim);
 
 void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim);
@@ -324,4 +348,7 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc(
                struct pim_upstream *up);
 void pim_upstream_fill_static_iif(struct pim_upstream *up,
                                struct interface *incoming);
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+               bool update_mroute);
+void pim_upstream_reeval_use_rpt(struct pim_instance *pim);
 #endif /* PIM_UPSTREAM_H */
index 1c4ecf299f60c3726d3ab4c9fe139532bd402648..c48ec373f844c526f049a22dc9f9c77b5339bc96 100644 (file)
@@ -379,13 +379,19 @@ int pim_interface_config_write(struct vty *vty)
                                                        ij->group_addr,
                                                        group_str,
                                                        sizeof(group_str));
-                                               inet_ntop(AF_INET,
-                                                         &ij->source_addr,
-                                                         source_str,
-                                                         sizeof(source_str));
-                                               vty_out(vty,
-                                                       " ip igmp join %s %s\n",
-                                                       group_str, source_str);
+                                               if (ij->source_addr.s_addr == INADDR_ANY) {
+                                                       vty_out(vty,
+                                                               " ip igmp join %s\n",
+                                                               group_str);
+                                               } else {
+                                                       inet_ntop(AF_INET,
+                                                                 &ij->source_addr,
+                                                                 source_str,
+                                                                 sizeof(source_str));
+                                                       vty_out(vty,
+                                                               " ip igmp join %s %s\n",
+                                                               group_str, source_str);
+                                               }
                                                ++writes;
                                        }
                                }
index 33e3b2340c2ec0253c2cd72aa756f6f0cd2fb259..fc34f3f6006f8d058cb38e7b7cf159b1da00a3b3 100644 (file)
@@ -245,26 +245,20 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
                 * for nht
                 */
                if (up)
-                       pim_rpf_update(vxlan_sg->pim, up, NULL);
+                       pim_rpf_update(vxlan_sg->pim, up, NULL, __func__);
        }
 }
 
 static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg)
 {
-       int vif_index;
-
        /* update MFC with the new IIF */
        pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif);
-       vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim,
-                       vxlan_sg->iif->ifindex);
-       if (vif_index > 0)
-               pim_scan_individual_oil(vxlan_sg->up->channel_oil,
-                               vif_index);
+       pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__);
 
        if (PIM_DEBUG_VXLAN)
-               zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d",
+               zlog_debug("vxlan SG %s orig mroute-up updated with iif %s",
                        vxlan_sg->sg_str,
-                       vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index);
+                       vxlan_sg->iif?vxlan_sg->iif->name:"-");
 
 }
 
@@ -292,6 +286,7 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
        struct pim_upstream *up;
        int flags = 0;
        struct prefix nht_p;
+       struct pim_instance *pim = vxlan_sg->pim;
 
        if (vxlan_sg->up) {
                /* nothing to do */
@@ -346,9 +341,15 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
                        pim_delete_tracked_nexthop(vxlan_sg->pim,
                                &nht_p, up, NULL, false);
                }
+               /* We are acting FHR; clear out use_rpt setting if any */
+               pim_upstream_update_use_rpt(up, false /*update_mroute*/);
                pim_upstream_ref(up, flags, __PRETTY_FUNCTION__);
                vxlan_sg->up = up;
                pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
+               /* mute pimreg on origination mroutes */
+               if (pim->regiface)
+                       pim_channel_update_oif_mute(up->channel_oil,
+                                       pim->regiface->info);
        } else {
                up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
                                vxlan_sg->iif, flags,
@@ -373,6 +374,8 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
 
        /* update the inherited OIL */
        pim_upstream_inherited_olist(vxlan_sg->pim, up);
+       if (!up->channel_oil->installed)
+               pim_upstream_mroute_add(up->channel_oil, __func__);
 }
 
 static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
@@ -386,7 +389,8 @@ static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
 
        vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
        pim_channel_add_oif(vxlan_sg->up->channel_oil,
-               vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN);
+               vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN,
+               __func__);
 }
 
 static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
@@ -405,7 +409,7 @@ static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
 
        vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
        pim_channel_del_oif(vxlan_sg->up->channel_oil,
-               orig_oif, PIM_OIF_FLAG_PROTO_VXLAN);
+                       orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__);
 }
 
 static inline struct interface *pim_vxlan_orig_mr_oif_get(
index f0a66e6b77a4f4323ca872f75161f80d360ccb6e..c6507a474c97637154cf44a8ab6d5b0a0466e066 100644 (file)
@@ -115,6 +115,13 @@ static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg)
        return (vxlan_sg->sg.src.s_addr != 0);
 }
 
+static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up)
+{
+       return (up->sg.src.s_addr != INADDR_ANY) &&
+               up->rpf.source_nexthop.interface &&
+               if_is_loopback_or_vrf(up->rpf.source_nexthop.interface);
+}
+
 extern struct pim_vxlan *pim_vxlan_p;
 extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim,
                                            struct prefix_sg *sg);
index b999188a9b804f178ed135c7f95329ab901a6064..06507b1f4c60c0144187d609b3c03358ab6d4d73 100644 (file)
@@ -272,7 +272,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
                nbr = pim_neighbor_find(old->source_nexthop.interface,
                                        old->rpf_addr.u.prefix4);
                if (nbr)
-                       pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+                       pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
 
                /*
                 * We have detected a case where we might need
@@ -290,7 +290,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
                         * so install it.
                         */
                        if (!up->channel_oil->installed)
-                               pim_mroute_add(up->channel_oil,
+                               pim_upstream_mroute_add(up->channel_oil,
                                        __PRETTY_FUNCTION__);
 
                        /*
@@ -325,8 +325,12 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim,
                        up->channel_oil->oil_inherited_rescan = 0;
                }
 
+               if (up->join_state == PIM_UPSTREAM_JOINED)
+                       pim_jp_agg_switch_interface(old, &up->rpf, up);
+
                if (!up->channel_oil->installed)
-                       pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+                       pim_upstream_mroute_add(up->channel_oil,
+                                       __PRETTY_FUNCTION__);
        }
 
        /* FIXME can join_desired actually be changed by pim_rpf_update()
@@ -386,138 +390,15 @@ static void pim_zebra_vxlan_replay(void)
        zclient_send_message(zclient);
 }
 
-void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index)
-{
-       struct in_addr vif_source;
-       int input_iface_vif_index;
-
-       pim_rp_set_upstream_addr(c_oil->pim, &vif_source,
-                                     c_oil->oil.mfcc_origin,
-                                     c_oil->oil.mfcc_mcastgrp);
-
-       if (in_vif_index)
-               input_iface_vif_index = in_vif_index;
-       else {
-               struct prefix src, grp;
-
-               src.family = AF_INET;
-               src.prefixlen = IPV4_MAX_BITLEN;
-               src.u.prefix4 = vif_source;
-               grp.family = AF_INET;
-               grp.prefixlen = IPV4_MAX_BITLEN;
-               grp.u.prefix4 = c_oil->oil.mfcc_mcastgrp;
-
-               if (PIM_DEBUG_ZEBRA) {
-                       char source_str[INET_ADDRSTRLEN];
-                       char group_str[INET_ADDRSTRLEN];
-                       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
-                                      source_str, sizeof(source_str));
-                       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
-                                      group_str, sizeof(group_str));
-                       zlog_debug(
-                               "%s: channel_oil (%s,%s) upstream info is not present.",
-                               __PRETTY_FUNCTION__, source_str, group_str);
-               }
-               input_iface_vif_index = pim_ecmp_fib_lookup_if_vif_index(
-                       c_oil->pim, &src, &grp);
-       }
-
-       if (input_iface_vif_index < 1) {
-               if (PIM_DEBUG_ZEBRA) {
-                       char source_str[INET_ADDRSTRLEN];
-                       char group_str[INET_ADDRSTRLEN];
-                       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
-                                      source_str, sizeof(source_str));
-                       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
-                                      group_str, sizeof(group_str));
-                       zlog_debug(
-                               "%s %s: could not find input interface(%d) for (S,G)=(%s,%s)",
-                               __FILE__, __PRETTY_FUNCTION__,
-                               c_oil->oil.mfcc_parent, source_str, group_str);
-               }
-               pim_mroute_del(c_oil, __PRETTY_FUNCTION__);
-               return;
-       }
-
-       if (input_iface_vif_index == c_oil->oil.mfcc_parent) {
-               if (!c_oil->installed)
-                       pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
-
-               /* RPF unchanged */
-               return;
-       }
-
-       if (PIM_DEBUG_ZEBRA) {
-               struct interface *old_iif = pim_if_find_by_vif_index(
-                       c_oil->pim, c_oil->oil.mfcc_parent);
-               struct interface *new_iif = pim_if_find_by_vif_index(
-                       c_oil->pim, input_iface_vif_index);
-               char source_str[INET_ADDRSTRLEN];
-               char group_str[INET_ADDRSTRLEN];
-               pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str,
-                              sizeof(source_str));
-               pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str,
-                              sizeof(group_str));
-               zlog_debug(
-                       "%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
-                       __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
-                       (old_iif) ? old_iif->name : "<old_iif?>",
-                       c_oil->oil.mfcc_parent,
-                       (new_iif) ? new_iif->name : "<new_iif?>",
-                       input_iface_vif_index);
-       }
-
-       /* new iif loops to existing oif ? */
-       if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) {
-               struct interface *new_iif = pim_if_find_by_vif_index(
-                       c_oil->pim, input_iface_vif_index);
-
-               if (PIM_DEBUG_ZEBRA) {
-                       char source_str[INET_ADDRSTRLEN];
-                       char group_str[INET_ADDRSTRLEN];
-                       pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
-                                      source_str, sizeof(source_str));
-                       pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
-                                      group_str, sizeof(group_str));
-                       zlog_debug(
-                               "%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
-                               __FILE__, __PRETTY_FUNCTION__, source_str,
-                               group_str,
-                               (new_iif) ? new_iif->name : "<new_iif?>",
-                               input_iface_vif_index);
-               }
-       }
-
-       /* update iif vif_index */
-       pim_channel_oil_change_iif(c_oil->pim, c_oil, input_iface_vif_index,
-                                  __PRETTY_FUNCTION__);
-       pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
-}
-
 void pim_scan_oil(struct pim_instance *pim)
 {
-       struct listnode *node;
-       struct listnode *nextnode;
        struct channel_oil *c_oil;
-       ifindex_t ifindex;
-       int vif_index = 0;
 
        pim->scan_oil_last = pim_time_monotonic_sec();
        ++pim->scan_oil_events;
 
-       for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) {
-               if (c_oil->up && c_oil->up->rpf.source_nexthop.interface) {
-                       ifindex = c_oil->up->rpf.source_nexthop
-                                         .interface->ifindex;
-                       vif_index =
-                               pim_if_find_vifindex_by_ifindex(pim, ifindex);
-                       /* Pass Current selected NH vif index to mroute
-                        * download */
-                       if (vif_index)
-                               pim_scan_individual_oil(c_oil, vif_index);
-               } else
-                       pim_scan_individual_oil(c_oil, 0);
-       }
+       frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil)
+               pim_upstream_mroute_iif_update(c_oil, __func__);
 }
 
 static int on_rpf_cache_refresh(struct thread *t)
@@ -758,7 +639,7 @@ void igmp_source_forward_start(struct pim_instance *pim,
                                              source->source_addr, sg.grp)) {
                        /*Create a dummy channel oil */
                        source->source_channel_oil = pim_channel_oil_add(
-                               pim, &sg, MAXVIFS, __PRETTY_FUNCTION__);
+                               pim, &sg, __PRETTY_FUNCTION__);
                }
 
                else {
@@ -809,7 +690,7 @@ void igmp_source_forward_start(struct pim_instance *pim,
                                }
                                source->source_channel_oil =
                                        pim_channel_oil_add(
-                                               pim, &sg, MAXVIFS,
+                                               pim, &sg,
                                                __PRETTY_FUNCTION__);
                        }
 
@@ -843,7 +724,7 @@ void igmp_source_forward_start(struct pim_instance *pim,
 
                                source->source_channel_oil =
                                        pim_channel_oil_add(
-                                               pim, &sg, input_iface_vif_index,
+                                               pim, &sg,
                                                __PRETTY_FUNCTION__);
                                if (!source->source_channel_oil) {
                                        if (PIM_DEBUG_IGMP_TRACE) {
@@ -862,7 +743,7 @@ void igmp_source_forward_start(struct pim_instance *pim,
        if (PIM_I_am_DR(pim_oif)) {
                result = pim_channel_add_oif(source->source_channel_oil,
                                             group->group_igmp_sock->interface,
-                                            PIM_OIF_FLAG_PROTO_IGMP);
+                                            PIM_OIF_FLAG_PROTO_IGMP, __func__);
                if (result) {
                        if (PIM_DEBUG_MROUTE) {
                                zlog_warn("%s: add_oif() failed with return=%d",
@@ -891,7 +772,7 @@ void igmp_source_forward_start(struct pim_instance *pim,
 
                pim_channel_del_oif(source->source_channel_oil,
                                    group->group_igmp_sock->interface,
-                                   PIM_OIF_FLAG_PROTO_IGMP);
+                                   PIM_OIF_FLAG_PROTO_IGMP, __func__);
                return;
        }
 
@@ -942,7 +823,8 @@ void igmp_source_forward_stop(struct igmp_source *source)
        */
        result = pim_channel_del_oif(source->source_channel_oil,
                                     group->group_igmp_sock->interface,
-                                    PIM_OIF_FLAG_PROTO_IGMP);
+                                        PIM_OIF_FLAG_PROTO_IGMP,
+                                        __func__);
        if (result) {
                if (PIM_DEBUG_IGMP_TRACE)
                        zlog_debug(
@@ -985,7 +867,8 @@ void pim_forward_start(struct pim_ifchannel *ch)
        if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
                mask = PIM_OIF_FLAG_PROTO_IGMP;
 
-       pim_channel_add_oif(up->channel_oil, ch->interface, mask);
+       pim_channel_add_oif(up->channel_oil, ch->interface,
+                       mask, __func__);
 }
 
 void pim_forward_stop(struct pim_ifchannel *ch, bool install_it)
@@ -1004,13 +887,13 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it)
         */
        if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent))
                pim_channel_add_oif(up->channel_oil, ch->interface,
-                                   PIM_OIF_FLAG_PROTO_PIM);
+                                   PIM_OIF_FLAG_PROTO_PIM, __func__);
        else
                pim_channel_del_oif(up->channel_oil, ch->interface,
-                                   PIM_OIF_FLAG_PROTO_PIM);
+                                   PIM_OIF_FLAG_PROTO_PIM, __func__);
 
        if (install_it && !up->channel_oil->installed)
-               pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+               pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
 }
 
 void pim_zebra_zclient_update(struct vty *vty)
index 77526281d18c1c8a2a479c4fc766bb03d864faa2..0454c0d69ebe2a64f2d3bbe245a5adade75434ec 100644 (file)
@@ -180,6 +180,14 @@ static int zclient_read_nexthop(struct pim_instance *pim,
                        zclient_lookup_failed(zlookup);
                        return -1;
                }
+
+               if (command == ZEBRA_ERROR) {
+                       enum zebra_error_types error;
+
+                       zapi_error_decode(s, &error);
+                       /* Do nothing with it for now */
+                       return -1;
+               }
        }
 
        raddr.s_addr = stream_get_ipv4(s);
@@ -338,7 +346,8 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim,
 
        s = zlookup->obuf;
        stream_reset(s);
-       zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, pim->vrf_id);
+       zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB,
+                             pim->vrf->vrf_id);
        stream_put_in_addr(s, &addr);
        stream_putw_at(s, 0, stream_get_endp(s));
 
index 5407e566a5ad89acc4ee8ae6a4db251c38725680..b5d135d032655bb5020b99cd32a29458f9cbaf78 100644 (file)
@@ -9,7 +9,7 @@ bin_PROGRAMS += pimd/mtracebis
 noinst_PROGRAMS += pimd/test_igmpv3_join
 dist_examples_DATA += pimd/pimd.conf.sample
 vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c
-man8 += $(MANBUILD)/pimd.8
+man8 += $(MANBUILD)/frr-pimd.8
 man8 += $(MANBUILD)/mtracebis.8
 endif
 
index b3f9ac763085dc8157cb5d63fcc008fca53577a8..670bc6f4c91060b7c6f1bb4dd8d721866be7149d 100644 (file)
@@ -169,9 +169,15 @@ BuildRequires:  libyang-devel >= 0.16.74
 BuildRequires:  python27-devel
 BuildRequires:  python27-sphinx
 %else
+%if 0%{?rhel} && 0%{?rhel} > 7
+BuildRequires:  python2-devel
+#platform-python-devel is needed for /usr/bin/pathfix.py
+BuildRequires:  platform-python-devel
+%else
 BuildRequires:  python-devel >= 2.7
 BuildRequires:  python-sphinx
 %endif
+%endif
 Requires:       initscripts
 %if %{with_pam}
 BuildRequires:  pam-devel
@@ -217,8 +223,13 @@ Contributed/3rd party tools which may be of use with frr.
 
 %package pythontools
 Summary: python tools for frr
+%if 0%{?rhel} && 0%{?rhel} > 7
+BuildRequires: python2
+Requires: python2-ipaddress
+%else
 BuildRequires: python
 Requires: python-ipaddress
+%endif
 Group: System Environment/Daemons
 
 %description pythontools
@@ -397,6 +408,11 @@ install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr
 install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr
 install -d -m750 %{buildroot}%{rundir}
 
+%if 0%{?rhel} && 0%{?rhel} > 7
+# avoid `ERROR: ambiguous python shebang in` errors
+pathfix.py -pni "%{__python2} %{py2_shbang_opts}" %{buildroot}/usr/lib/frr/*.py
+%py_byte_compile %{__python2} %{buildroot}/usr/lib/frr/*.py
+%endif
 
 %pre
 # add vty_group
@@ -428,7 +444,7 @@ zebra_spec_add_service ()
 {
     # Add port /etc/services entry if it isn't already there
     if [ -f %{_sysconfdir}/services ] && \
-        ! %__sed -e 's/#.*$//' %{_sysconfdir}/services | %__grep -wq $1 ; then
+        ! %__sed -e 's/#.*$//' %{_sysconfdir}/services 2>/dev/null | %__grep -wq $1 ; then
         echo "$1        $2          # $3"  >> %{_sysconfdir}/services
     fi
 }
@@ -633,6 +649,7 @@ fi
 %if %{with_rpki}
     %{_libdir}/frr/modules/bgpd_rpki.so
 %endif
+%{_libdir}/frr/modules/zebra_cumulus_mlag.so
 %{_libdir}/frr/modules/zebra_irdp.so
 %{_libdir}/frr/modules/bgpd_bmp.so
 %{_bindir}/*
@@ -681,7 +698,10 @@ fi
 
 
 %changelog
-* Sun May 28 2018 Rafael Zalamena <rzalamena@opensourcerouting.org> - %{version}
+* Fri Dec 27 2019 Donatas Abraitis <donatas.abraitis@gmail.com>
+- Add CentOS 8 support
+
+* Mon May 28 2018 Rafael Zalamena <rzalamena@opensourcerouting.org> - %{version}
 - Add BFDd support
 
 * Sun May 20 2018 Martin Winter <mwinter@opensourcerouting.org>
index 5c26c0cef73ceec970471523e27f0762a62d0dba..7e2394f4732a5634fd8b14ee117c34ae3d316e89 100644 (file)
@@ -1001,6 +1001,7 @@ DEFPY (clear_ip_rip,
        VRF_CMD_HELP_STR)
 {
        struct list *input;
+       int ret;
 
        input = list_new();
        if (vrf) {
@@ -1011,7 +1012,11 @@ DEFPY (clear_ip_rip,
                listnode_add(input, yang_vrf);
        }
 
-       return nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL);
+       ret = nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL);
+
+       list_delete(&input);
+
+       return ret;
 }
 
 void rip_cli_init(void)
index 26504a857d564a22aabee3fc07ec9ebbb1025db5..060bb76585c1aac4434a3ae4937fdd3736377b13 100644 (file)
@@ -25,7 +25,6 @@
 #include "thread.h"
 #include "command.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "prefix.h"
 #include "filter.h"
 #include "keychain.h"
index 90ee667f05a104d0a624ed6c1f7d8d20bb192285..e07d218860b522f0c03a97931fac5c20110f09ba 100644 (file)
@@ -151,8 +151,8 @@ static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS)
 
 void rip_redistribute_conf_update(struct rip *rip, int type)
 {
-       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type,
-                            0, rip->vrf->vrf_id);
+       zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP,
+                               type, 0, rip->vrf->vrf_id);
 }
 
 void rip_redistribute_conf_delete(struct rip *rip, int type)
index dfdfc88a56e288cd414ce879797f764b83e983d5..00984672ed82f40c5416593725bc55e410228254 100644 (file)
@@ -15,7 +15,7 @@ vtysh_scan += \
 if SNMP
 module_LTLIBRARIES += ripd/ripd_snmp.la
 endif
-man8 += $(MANBUILD)/ripd.8
+man8 += $(MANBUILD)/frr-ripd.8
 endif
 
 ripd_librip_a_SOURCES = \
index 2d9930e3576d76a93f2d81f489e30a90e8b0c2fc..b3d92fb0d90337164d0ec56d89f6b37f13c4b122 100644 (file)
@@ -485,6 +485,7 @@ DEFPY (clear_ipv6_rip,
        VRF_CMD_HELP_STR)
 {
        struct list *input;
+       int ret;
 
        input = list_new();
        if (vrf) {
@@ -495,7 +496,11 @@ DEFPY (clear_ipv6_rip,
                listnode_add(input, yang_vrf);
        }
 
-       return nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL);
+       ret = nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL);
+
+       list_delete(&input);
+
+       return ret;
 }
 
 void ripng_cli_init(void)
index 002434550032e85d411f31988150c888823b99d3..9daeeb9580da39a51a0ca4a5c2a6e045ba4f334c 100644 (file)
@@ -27,7 +27,6 @@
 #include "vty.h"
 #include "command.h"
 #include "memory.h"
-#include "memory_vty.h"
 #include "thread.h"
 #include "log.h"
 #include "prefix.h"
index fa61d69caae5863a16c63961928982dfdc843cc2..f9bd56d1dfd1636536d766eb5d686b39ace01386 100644 (file)
@@ -152,8 +152,8 @@ static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS)
 
 void ripng_redistribute_conf_update(struct ripng *ripng, int type)
 {
-       zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, type, 0,
-                            ripng->vrf->vrf_id);
+       zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6,
+                               type, 0, ripng->vrf->vrf_id);
 }
 
 void ripng_redistribute_conf_delete(struct ripng *ripng, int type)
index 07d0cb892c63910aff6e9bb1731edf468f4e6f2d..4e219c994772a5e409205242433c655a4233b5c2 100644 (file)
@@ -10,7 +10,7 @@ vtysh_scan += \
        $(top_srcdir)/ripngd/ripng_debug.c \
        $(top_srcdir)/ripngd/ripngd.c \
        # end
-man8 += $(MANBUILD)/ripngd.8
+man8 += $(MANBUILD)/frr-ripngd.8
 endif
 
 ripngd_libripng_a_SOURCES = \
index cd577e9051e851ad9ef54c77825bc21d5e52cc86..4fc8f40ae1c0cb631012f4979ec295db967a4432 100644 (file)
@@ -243,6 +243,8 @@ void route_add(struct prefix *p, vrf_id_t vrf_id,
                api_nh = &api.nexthops[i];
                api_nh->vrf_id = nh->vrf_id;
                api_nh->type = nh->type;
+               api_nh->weight = nh->weight;
+
                switch (nh->type) {
                case NEXTHOP_TYPE_IPV4:
                        api_nh->gate = nh->gate;
@@ -265,6 +267,17 @@ void route_add(struct prefix *p, vrf_id_t vrf_id,
                        api_nh->bh_type = nh->bh_type;
                        break;
                }
+
+               if (nh->nh_label && nh->nh_label->num_labels > 0) {
+                       int j;
+
+                       SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
+                       api_nh->label_num = nh->nh_label->num_labels;
+                       for (j = 0; j < nh->nh_label->num_labels; j++)
+                               api_nh->labels[j] = nh->nh_label->label[j];
+               }
+
                i++;
        }
        api.nexthop_num = i;
index 4a9028f6feee69207c2b131fecab167bd20bf714..89b183d832c7f982dc29794626e5e84c908b9075 100644 (file)
@@ -7,7 +7,7 @@ noinst_LIBRARIES += sharpd/libsharp.a
 sbin_PROGRAMS += sharpd/sharpd
 dist_examples_DATA += sharpd/sharpd.conf.sample
 vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c
-man8 += $(MANBUILD)/sharpd.8
+man8 += $(MANBUILD)/frr-sharpd.8
 endif
 
 sharpd_libsharp_a_SOURCES = \
index a8644b3145c17615935f7d9cda54064b3e02fc14..9f7995350aa6d290cdbc244a6a7f3ab48681a714 100644 (file)
@@ -8,10 +8,10 @@ d none @mandir@=$DESTDIR/@mandir@ 0755 root bin
 d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin
 f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin
 d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin
-f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin
-f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin
-f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin
-f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin
-f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin
-f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin
-f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin
+f none @mandir@/man8/frr-bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin
+f none @mandir@/man8/frr-ospf6d.8=$DESTDIR/@mandir@/man8/frr-ospf6d.8 0644 root bin
+f none @mandir@/man8/frr-ospfd.8=$DESTDIR/@mandir@/man8/frr-ospfd.8 0644 root bin
+f none @mandir@/man8/frr-ripd.8=$DESTDIR/@mandir@/man8/frr-ripd.8 0644 root bin
+f none @mandir@/man8/frr-ripngd.8=$DESTDIR/@mandir@/man8/frr-ripngd.8 0644 root bin
+f none @mandir@/man8/frr-zebra.8=$DESTDIR/@mandir@/man8/frr-zebra.8 0644 root bin
+f none @mandir@/man8/frr-isisd.8=$DESTDIR/@mandir@/man8/frr-isisd.8 0644 root bin
diff --git a/staticd/static_debug.c b/staticd/static_debug.c
new file mode 100644 (file)
index 0000000..9906e80
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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/command.h"
+#include "lib/debug.h"
+
+#include "static_debug.h"
+
+/*
+ * Debug infra: a debug struct for each category, and a corresponding
+ * string.
+ */
+
+/* clang-format off */
+struct debug static_dbg_events = {0, "Staticd events"};
+
+struct debug *static_debug_arr[] =  {
+       &static_dbg_events
+};
+
+const char *static_debugs_conflines[] = {
+       "debug static events"
+};
+/* clang-format on */
+
+
+/*
+ * Set or unset all staticd debugs
+ *
+ * flags
+ *    The flags to set
+ *
+ * set
+ *    Whether to set or unset the specified flags
+ */
+static void static_debug_set_all(uint32_t flags, bool set)
+{
+       for (unsigned int i = 0; i < array_size(static_debug_arr); i++) {
+               DEBUG_FLAGS_SET(static_debug_arr[i], flags, set);
+
+               /* if all modes have been turned off, don't preserve options */
+               if (!DEBUG_MODE_CHECK(static_debug_arr[i], DEBUG_MODE_ALL))
+                       DEBUG_CLEAR(static_debug_arr[i]);
+       }
+}
+
+static int static_debug_config_write_helper(struct vty *vty, bool config)
+{
+       uint32_t mode = DEBUG_MODE_ALL;
+
+       if (config)
+               mode = DEBUG_MODE_CONF;
+
+       for (unsigned int i = 0; i < array_size(static_debug_arr); i++)
+               if (DEBUG_MODE_CHECK(static_debug_arr[i], mode))
+                       vty_out(vty, "%s\n", static_debugs_conflines[i]);
+
+       return 0;
+}
+
+int static_config_write_debug(struct vty *vty)
+{
+       return static_debug_config_write_helper(vty, true);
+}
+
+int static_debug_status_write(struct vty *vty)
+{
+       return static_debug_config_write_helper(vty, false);
+}
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ *    vty->node
+ *
+ * onoff
+ *    Whether to turn the specified debugs on or off
+ *
+ * events
+ *    Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events)
+{
+       uint32_t mode = DEBUG_NODE2MODE(vtynode);
+
+       if (events)
+               DEBUG_MODE_SET(&static_dbg_events, mode, onoff);
+}
+
+/*
+ * Debug lib initialization
+ */
+
+struct debug_callbacks static_dbg_cbs = {
+       .debug_set_all = static_debug_set_all
+};
+
+void static_debug_init(void)
+{
+       debug_init(&static_dbg_cbs);
+}
diff --git a/staticd/static_debug.h b/staticd/static_debug.h
new file mode 100644 (file)
index 0000000..6e58118
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See 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 _STATIC_DEBUG_H
+#define _STATIC_DEBUG_H
+
+
+#include <zebra.h>
+
+#include "lib/debug.h"
+
+/* staticd debugging records */
+struct debug static_dbg_events;
+
+/*
+ * Initialize staticd debugging.
+ *
+ * Installs VTY commands and registers callbacks.
+ */
+void static_debug_init(void);
+
+/*
+ * Print staticd debugging configuration.
+ *
+ * vty
+ *    VTY to print debugging configuration to.
+ */
+int static_config_write_debug(struct vty *vty);
+
+/*
+ * Print staticd debugging configuration, human readable form.
+ *
+ * vty
+ *    VTY to print debugging configuration to.
+ */
+int static_debug_status_write(struct vty *vty);
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ *    vty->node
+ *
+ * onoff
+ *    Whether to turn the specified debugs on or off
+ *
+ * events
+ *    Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events);
+
+
+#endif /* _STATIC_DEBUG_H */
index 18cb9638c975845fc098529db3c27d30ad51405a..43cb7db51dfd1823ba8deae95823fe4303d12a4d 100644 (file)
@@ -36,6 +36,7 @@
 #include "static_vty.h"
 #include "static_routes.h"
 #include "static_zebra.h"
+#include "static_debug.h"
 
 char backup_config_file[256];
 
@@ -141,6 +142,7 @@ int main(int argc, char **argv, char **envp)
        master = frr_init();
 
        access_list_init();
+       static_debug_init();
        static_vrf_init();
 
        static_zebra_init();
index 8db37589af7e47fa5c105caf9b7db70e1b18fdc6..6390fd811f6487dcedf3c941db157c0e57ea2dae 100644 (file)
 #include "static_memory.h"
 #include "static_vty.h"
 #include "static_routes.h"
+#include "static_debug.h"
 #ifndef VTYSH_EXTRACT_PL
 #include "staticd/static_vty_clippy.c"
 #endif
+
+#define STATICD_STR "Static route daemon\n"
+
 static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty,
                                                     const char *vrf_name)
 {
@@ -1439,21 +1443,43 @@ DEFPY(ipv6_route_vrf,
                from_str, gate_str, ifname, flag, tag_str, distance_str, label,
                table_str, false);
 }
+DEFPY(debug_staticd,
+      debug_staticd_cmd,
+      "[no] debug static [{events$events}]",
+      NO_STR
+      DEBUG_STR
+      STATICD_STR
+      "Debug events\n")
+{
+       /* If no specific category, change all */
+       if (strmatch(argv[argc - 1]->text, "static"))
+               static_debug_set(vty->node, !no, true);
+       else
+               static_debug_set(vty->node, !no, !!events);
 
-DEFUN_NOSH (show_debugging_staticd,
-           show_debugging_staticd_cmd,
+       return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_static,
+           show_debugging_static_cmd,
            "show debugging [static]",
            SHOW_STR
            DEBUG_STR
            "Static Information\n")
 {
-       vty_out(vty, "Static debugging status\n");
+       vty_out(vty, "Staticd debugging status\n");
+
+       static_debug_status_write(vty);
 
        return CMD_SUCCESS;
 }
 
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+
 void static_vty_init(void)
 {
+       install_node(&debug_node, static_config_write_debug);
+
        install_element(CONFIG_NODE, &ip_mroute_dist_cmd);
 
        install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
@@ -1470,7 +1496,9 @@ void static_vty_init(void)
        install_element(CONFIG_NODE, &ipv6_route_cmd);
        install_element(VRF_NODE, &ipv6_route_vrf_cmd);
 
-       install_element(VIEW_NODE, &show_debugging_staticd_cmd);
+       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;
index a474613b4da311a6bb7442b1fdd808464de6d56d..42646d15bca7b54a11925052c47b20d7f6248e68 100644 (file)
@@ -388,7 +388,8 @@ extern void static_zebra_route_add(struct route_node *rn,
                        continue;
 
                api_nh->vrf_id = si->nh_vrf_id;
-               api_nh->onlink = si->onlink;
+               if (si->onlink)
+                       SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
 
                si->state = STATIC_SENT_TO_ZEBRA;
 
@@ -441,7 +442,7 @@ extern void static_zebra_route_add(struct route_node *rn,
                if (si->snh_label.num_labels) {
                        int i;
 
-                       SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
+                       SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
                        api_nh->label_num = si->snh_label.num_labels;
                        for (i = 0; i < api_nh->label_num; i++)
                                api_nh->labels[i] = si->snh_label.label[i];
index 17c4536fe9fc2b393f63f38d6fc8f7354845aac4..30c69231c915cef2e7cf9cac2ca1e397967bdf89 100644 (file)
@@ -7,10 +7,11 @@ noinst_LIBRARIES += staticd/libstatic.a
 sbin_PROGRAMS += staticd/staticd
 dist_examples_DATA += staticd/staticd.conf.sample
 vtysh_scan += $(top_srcdir)/staticd/static_vty.c
-man8 += $(MANBUILD)/staticd.8
+man8 += $(MANBUILD)/frr-staticd.8
 endif
 
 staticd_libstatic_a_SOURCES = \
+       staticd/static_debug.c \
        staticd/static_memory.c \
        staticd/static_nht.c \
        staticd/static_routes.c \
@@ -20,6 +21,7 @@ staticd_libstatic_a_SOURCES = \
        # end
 
 noinst_HEADERS += \
+       staticd/static_debug.h \
        staticd/static_memory.h \
        staticd/static_nht.h \
        staticd/static_zebra.h \
index 6252cea823aace5395a706abb584b2f79159778a..5414cb8cc914a1b510631c0a7ed3dd3b2415a11b 100644 (file)
@@ -45,6 +45,7 @@
 /lib/test_timer_performance
 /lib/test_ttable
 /lib/test_typelist
+/lib/test_versioncmp
 /lib/test_zlog
 /lib/test_zmq
 /ospf6d/test_lsdb
index 96e398512b75d556bdd2650a9f43c74babd8f0d7..1b3f90434bd56735df74df7a550b91dce3b24d96 100644 (file)
@@ -923,7 +923,7 @@ int main(void)
        if (fileno(stdout) >= 0)
                tty = isatty(fileno(stdout));
 
-       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
                return -1;
 
        peer = peer_create_accept(bgp);
index fbf2a9fed28be457202c858d92f49e40d0beac1a..c97ea57150fb6d7f9c4d7b6d3b41b5e8b76e49c6 100644 (file)
@@ -1027,7 +1027,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type)
                parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri);
                break;
        case BGP_ATTR_PREFIX_SID:
-               parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri);
+               parse_ret = bgp_attr_prefix_sid(&attr_args, &nlri);
                break;
        default:
                printf("unknown type");
@@ -1087,7 +1087,7 @@ int main(void)
        if (fileno(stdout) >= 0)
                tty = isatty(fileno(stdout));
 
-       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
                return -1;
 
        peer = peer_create_accept(bgp);
index 7a038fb02e31dd9af6bce2ca89a5e7099b51a1df..d2c093fbea2487eb600904825db89ddf4767180a 100644 (file)
@@ -63,7 +63,7 @@ int main(int argc, char *argv[])
        vrf_init(NULL, NULL, NULL, NULL, NULL);
        bgp_option_set(BGP_OPT_NO_LISTEN);
 
-       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+       if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
                return -1;
 
        peer = peer_create_accept(bgp);
index b1dcfcf7075c38d9724e92c44c3b6f0a03dcac7e..2de29cbdbcdbc0da9426d32be09e42c61d002e58 100644 (file)
@@ -24,7 +24,7 @@
 #include "vty.h"
 #include "command.h"
 #include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 
 extern void test_init(void);
 
@@ -154,7 +154,7 @@ int main(int argc, char **argv)
        /* Library inits. */
        cmd_init(1);
        vty_init(master, false);
-       memory_init();
+       lib_cmd_init();
        yang_init();
        nb_init(master, NULL, 0);
 
index 2071ae08cd7e80820d41e306c42c1a4c08b5b484..e091372ab8766bee6d3eaf0a4760f61a997c1f84 100644 (file)
@@ -25,7 +25,7 @@
 #include "vty.h"
 #include "command.h"
 #include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 #include "log.h"
 
 #include "common_cli.h"
@@ -83,7 +83,7 @@ int main(int argc, char **argv)
        cmd_domainname_set("test.domain");
 
        vty_init(master, false);
-       memory_init();
+       lib_cmd_init();
        yang_init();
        nb_init(master, NULL, 0);
 
index 88126e84bce8196fefd0e36ec6b7ad6cd36dd031..391ccd9268af72002311a4cfca762df296a3cdc9 100644 (file)
 #include "lib/json.h"
 #include "lib/keychain.h"
 #include "lib/lib_errors.h"
+#include "lib/lib_vty.h"
 #include "lib/libfrr.h"
 #include "lib/libospf.h"
 #include "lib/linklist.h"
 #include "lib/log.h"
 #include "lib/md5.h"
 #include "lib/memory.h"
-#include "lib/memory_vty.h"
 #include "lib/mlag.h"
 #include "lib/module.h"
 #include "lib/monotime.h"
index a28830e088468e6014e7ece0a366226812c5a2d2..18d3180889b406b6054fcd58f7563838b11c6a66 100644 (file)
@@ -23,7 +23,7 @@
 #include "vty.h"
 #include "command.h"
 #include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 #include "log.h"
 #include "northbound.h"
 
@@ -412,7 +412,7 @@ int main(int argc, char **argv)
        cmd_init(1);
        cmd_hostname_set("test");
        vty_init(master, false);
-       memory_init();
+       lib_cmd_init();
        yang_init();
        nb_init(master, modules, array_size(modules));
 
index b56cc30cf3ad99c727fe5e98eb25d86aaa3e139b..7fb9a769d3e18088f5c39f3cbbb8d092a10d96c1 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <zebra.h>
 #include <memory.h>
-#include <memory_vty.h>
+#include <lib_vty.h>
 #include <buffer.h>
 
 struct thread_master *master;
@@ -32,7 +32,7 @@ int main(int argc, char **argv)
        char junk[3];
        char c = 'a';
 
-       memory_init();
+       lib_cmd_init();
 
        if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) {
                fprintf(stderr, "Usage: %s <number of chars to simulate>\n",
index de638bc67a3e0df1792587ee5e3987c13ecf90f1..c06ebbeb38e86d35fdb4ff8c73f4860f1a67c282 100644 (file)
@@ -22,7 +22,7 @@
 #include "getopt.h"
 #include "privs.h"
 #include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
 
 zebra_capabilities_t _caps_p[] = {
        ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE,
@@ -105,7 +105,7 @@ int main(int argc, char **argv)
        }
 
        /* Library inits. */
-       memory_init();
+       lib_cmd_init();
        zprivs_preinit(&test_privs);
        zprivs_init(&test_privs);
 
index f20bbc52d9bff902b242b0839d66d82de49d4f89..9039fa8a464d56647b9e3f1d6475fce0625d8e0f 100644 (file)
@@ -98,12 +98,13 @@ static void ts_hash(const char *text, const char *expect)
        unsigned i = 0;
        uint8_t hash[32];
        char hashtext[65];
-       uint32_t count;
+       uint32_t swap_count, count;
 
-       count = htonl(list_count(&head));
+       count = list_count(&head);
+       swap_count = htonl(count);
 
        SHA256_Init(&ctx);
-       SHA256_Update(&ctx, &count, sizeof(count));
+       SHA256_Update(&ctx, &swap_count, sizeof(swap_count));
 
        frr_each (list, &head, item) {
                struct {
@@ -115,7 +116,7 @@ static void ts_hash(const char *text, const char *expect)
                };
                SHA256_Update(&ctx, &hashitem, sizeof(hashitem));
                i++;
-               assert(i < count);
+               assert(i <= count);
        }
        SHA256_Final(hash, &ctx);
 
diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c
new file mode 100644 (file)
index 0000000..bb819e3
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * frr_version_cmp() tests
+ * Copyright (C) 2018  David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public 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 <zebra.h>
+#include <defaults.h>
+
+static const char *rel(int x)
+{
+       if (x < 0)
+               return "<";
+       if (x > 0)
+               return ">";
+       return "==";
+}
+
+static int fail;
+
+static void compare(const char *a, const char *b, int expect)
+{
+       int result = frr_version_cmp(a, b);
+
+       if (expect == result)
+               printf("\"%s\" %s \"%s\"\n", a, rel(result), b);
+       else {
+               printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b,
+                               rel(expect));
+               fail = 1;
+       }
+}
+
+int main(int argc, char **argv)
+{
+       compare("", "", 0);
+       compare("1", "1", 0);
+       compare("1.0", "1.00", 0);
+       compare("10.0", "1", 1);
+       compare("10.0", "2", 1);
+       compare("2.1", "10.0", -1);
+       compare("1.1.1", "1.1.0", 1);
+       compare("1.0a", "1.0", 1);
+       compare("1.0a", "1.0b", -1);
+       compare("1.0a10", "1.0a2", 1);
+       compare("1.00a2", "1.0a2", 0);
+       compare("1.00a2", "1.0a3", -1);
+       compare("1.0-dev", "1.0", 1);
+       compare("1.0~foo", "1.0", -1);
+       compare("1.0~1", "1.0~0", 1);
+       compare("1.00~1", "1.0~0", 1);
+       printf("final tally: %s\n", fail ? "FAILED" : "ok");
+       return fail;
+}
diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py
new file mode 100644 (file)
index 0000000..0990757
--- /dev/null
@@ -0,0 +1,6 @@
+import frrtest
+
+class TestVersionCmp(frrtest.TestMultiOut):
+    program = './test_versioncmp'
+
+TestVersionCmp.exit_cleanly()
index 270c0811b49cb169de79f3310c9f387ccb532d5c..d87d348949378044d2f9968ca06c1412ad2b8867 100644 (file)
@@ -69,6 +69,7 @@ check_PROGRAMS = \
        tests/lib/test_timer_performance \
        tests/lib/test_ttable \
        tests/lib/test_typelist \
+       tests/lib/test_versioncmp \
        tests/lib/test_zlog \
        tests/lib/test_graph \
        tests/lib/cli/test_cli \
@@ -293,6 +294,10 @@ tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD)
 tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c
+tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c
 tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD)
@@ -344,6 +349,7 @@ EXTRA_DIST += \
        tests/lib/test_ttable.py \
        tests/lib/test_ttable.refout \
        tests/lib/test_typelist.py \
+       tests/lib/test_versioncmp.py \
        tests/lib/test_zlog.py \
        tests/lib/test_graph.py \
        tests/lib/test_graph.refout \
index a7d6fe11a6dd313915e9742ad93fbcb116ec098d..61d17a61b3c3bd244659ba4068daf9d2b1d2373d 100644 (file)
@@ -10,6 +10,8 @@ C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX
 C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX
 O   192.168.0.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX
 O   192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX
+S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
+S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth1, XX:XX:XX
 S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
 S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
 S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
index 85c867696485e02a9c07965ba78ccc1ebb5d6390..fbf827604f2f1e43ff6e0d56324d0427e55cb1db 100644 (file)
@@ -26,6 +26,11 @@ ipv6 route 4:5::6:12/128 r1-eth0
 # by zebra but not installed.
 ip route 4.5.6.15/32 192.168.0.2 255
 ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255
+
+# Routes to put into a nexthop-group
+ip route 1.1.1.1/32 r1-eth0
+ip route 1.1.1.2/32 r1-eth1
+
 !
 interface r1-eth0
  description to sw0 - no routing protocol
index 9658c080c0c396a5f33ba1c34533db4943eff9ec..16609221c195334b596a20127169a4d5f0df55da 100755 (executable)
@@ -120,6 +120,7 @@ def setup_module(module):
         if net['r%s' % i].daemon_available('ldpd'):
             # Only test LDPd if it's installed and Kernel >= 4.5
             net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i))
+        net['r%s' % i].loadConf('sharpd')
         net['r%s' % i].startRouter()
 
     # For debugging after starting Quagga/FRR daemons, uncomment the next line
@@ -346,6 +347,36 @@ def test_converge_protocols():
     # For debugging after starting FRR/Quagga daemons, uncomment the next line
     ## CLI(net)
 
+def test_nexthop_groups():
+    global fatal_error
+    global net
+
+    # Skip if previous fatal error condition is raised
+    if (fatal_error != ""):
+        pytest.skip(fatal_error)
+
+    print("\n\n** Verifying Nexthop Groups")
+    print("******************************************\n")
+
+    # Create a lib nexthop-group
+    net["r1"].cmd('vtysh -c "c t" -c "nexthop-group red" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"')
+
+    # Create with sharpd using nexthop-group
+    net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group red 1"')
+
+    # Verify route and that zebra created NHGs for and they are valid/installed
+    output = net["r1"].cmd('vtysh -c "show ip route 2.2.2.1/32 nexthop-group"')
+    match = re.search(r"Nexthop Group ID: (\d+)", output);
+    assert match is not None, "Nexthop Group ID not found for sharpd route 2.2.2.1/32"
+
+    nhe_id = int(match.group(1))
+
+    output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhe_id)
+    match = re.search(r"Valid", output)
+    assert match is not None, "Nexthop Group ID=%d not marked Valid" % nhe_id
+
+    match = re.search(r"Installed", output)
+    assert match is not None, "Nexthop Group ID=%d not marked Installed" % nhe_id
 
 def test_rip_status():
     global fatal_error
diff --git a/tests/topotests/bgp_default-route_route-map/__init__.py b/tests/topotests/bgp_default-route_route-map/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..a9925ab
--- /dev/null
@@ -0,0 +1,9 @@
+router bgp 65000
+  neighbor 192.168.255.2 remote-as 65001
+  address-family ipv4 unicast
+    neighbor 192.168.255.2 default-originate route-map default
+  exit-address-family
+!
+route-map default permit 10
+  set metric 123
+!
diff --git a/tests/topotests/bgp_default-route_route-map/r1/zebra.conf b/tests/topotests/bgp_default-route_route-map/r1/zebra.conf
new file mode 100644 (file)
index 0000000..0a283c0
--- /dev/null
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..a8a6c49
--- /dev/null
@@ -0,0 +1,6 @@
+router bgp 65001
+  neighbor 192.168.255.1 remote-as 65000
+  address-family ipv4 unicast
+    redistribute connected
+  exit-address-family
+!
diff --git a/tests/topotests/bgp_default-route_route-map/r2/zebra.conf b/tests/topotests/bgp_default-route_route-map/r2/zebra.conf
new file mode 100644 (file)
index 0000000..606c17b
--- /dev/null
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py b/tests/topotests/bgp_default-route_route-map/test_bgp_default-originate_route-map.py
new file mode 100644 (file)
index 0000000..992ee85
--- /dev/null
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# bgp_default-originate_route-map.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND 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.
+#
+
+"""
+bgp_default-originate_route-map.py:
+
+Test if works the following commands:
+router bgp 65031
+  address-family ipv4 unicast
+    neighbor 192.168.255.2 default-originate route-map default
+
+route-map default permit 10
+  set metric 123
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+    def build(self, *_args, **_opts):
+        tgen = get_topogen(self)
+
+        for routern in range(1, 3):
+            tgen.add_router('r{}'.format(routern))
+
+        switch = tgen.add_switch('s1')
+        switch.add_link(tgen.gears['r1'])
+        switch.add_link(tgen.gears['r2'])
+
+def setup_module(mod):
+    tgen = Topogen(TemplateTopo, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+        router.load_config(
+            TopoRouter.RD_ZEBRA,
+            os.path.join(CWD, '{}/zebra.conf'.format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_BGP,
+            os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+        )
+
+    tgen.start_router()
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+def test_bgp_default_originate_route_map():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    router = tgen.gears['r2']
+
+    def _bgp_converge(router):
+        output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+        expected = {
+            '192.168.255.1': {
+                'bgpState': 'Established',
+                'addressFamilyInfo': {
+                    'ipv4Unicast': {
+                        'acceptedPrefixCounter': 1
+                    }
+                }
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    def _bgp_default_route_has_metric(router):
+        output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+        expected = {
+            'paths': [
+                {
+                    'med': 123
+                }
+            ]
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge, router)
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+    assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_default_route_has_metric, router)
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+    assert result is None, 'Failed to see applied metric for default route in "{}"'.format(router)
+
+if __name__ == '__main__':
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf
new file mode 100644 (file)
index 0000000..99e6b68
--- /dev/null
@@ -0,0 +1,5 @@
+router bgp 65000
+  neighbor 192.168.255.2 remote-as 65000
+  address-family ipv4 unicast
+    redistribute connected
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf
new file mode 100644 (file)
index 0000000..7ef77a6
--- /dev/null
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r5-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf
new file mode 100644 (file)
index 0000000..164f975
--- /dev/null
@@ -0,0 +1,3 @@
+router bgp 65000
+  bgp ebgp-requires-policy
+  neighbor 192.168.255.1 remote-as 65000
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf
new file mode 100644 (file)
index 0000000..1c617c4
--- /dev/null
@@ -0,0 +1,6 @@
+!
+interface r6-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
index eecacfd00cdedb5e1f5f3f396bc467b55cd60734..6660b4e8668e5c021242093db5b46fc85bde45b1 100644 (file)
@@ -5,7 +5,7 @@
 # Part of NetDEF Topology Tests
 #
 # Copyright (c) 2019 by
-# Network Device Education Foundation, Inc. ("NetDEF")
+# Donatas Abraitis <donatas.abraitis@gmail.com>
 #
 # Permission to use, copy, modify, and/or distribute this software
 # for any purpose with or without fee is hereby granted, provided
@@ -34,6 +34,7 @@ import sys
 import json
 import time
 import pytest
+import functools
 
 CWD = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.join(CWD, '../'))
@@ -48,7 +49,7 @@ class TemplateTopo(Topo):
     def build(self, *_args, **_opts):
         tgen = get_topogen(self)
 
-        for routern in range(1, 5):
+        for routern in range(1, 7):
             tgen.add_router('r{}'.format(routern))
 
         switch = tgen.add_switch('s1')
@@ -59,6 +60,10 @@ class TemplateTopo(Topo):
         switch.add_link(tgen.gears['r3'])
         switch.add_link(tgen.gears['r4'])
 
+        switch = tgen.add_switch('s3')
+        switch.add_link(tgen.gears['r5'])
+        switch.add_link(tgen.gears['r6'])
+
 def setup_module(mod):
     tgen = Topogen(TemplateTopo, mod.__name__)
     tgen.start_topology()
@@ -81,32 +86,57 @@ def teardown_module(mod):
     tgen = get_topogen()
     tgen.stop_topology()
 
-def test_bgp_remove_private_as():
+def test_ebgp_requires_policy():
     tgen = get_topogen()
 
     if tgen.routers_have_failure():
         pytest.skip(tgen.errors)
 
     def _bgp_converge(router):
-        while True:
-            cmd = "show ip bgp neighbor 192.168.255.1 json"
-            output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
-            if output['192.168.255.1']['bgpState'] == 'Established':
-                time.sleep(3)
-                return True
-
-    def _bgp_ebgp_requires_policy(router):
-        cmd = "show ip bgp 172.16.255.254/32 json"
-        output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
-        if 'prefix' in output:
-            return True
-        return False
-
-    if _bgp_converge('r2'):
-        assert _bgp_ebgp_requires_policy('r2') == True
-
-    if _bgp_converge('r4'):
-        assert _bgp_ebgp_requires_policy('r4') == False
+        output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+        expected = {
+            '192.168.255.1': {
+                'bgpState': 'Established'
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    def _bgp_has_routes(router):
+        output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json"))
+        expected = {
+            'routes': {
+                '172.16.255.254/32': [
+                    {
+                        'valid': True
+                    }
+                ]
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge, 'r2')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is True, 'Failed bgp convergence (r2) in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_has_routes, 'r2')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is True, 'eBGP policy is not working (r2) in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_converge, 'r4')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is True, 'Failed bgp convergence (r4) in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_has_routes, 'r4')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is False, 'eBGP policy is not working (r4) in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_converge, 'r6')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is True, 'Failed bgp convergence (r6) in "{}"'.format(router)
+
+    test_func = functools.partial(_bgp_has_routes, 'r6')
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert success is True, 'eBGP policy is not working (r6) in "{}"'.format(router)
 
 if __name__ == '__main__':
     args = ["-s"] + sys.argv[1:]
index 02fba97316d438018afb8216b2667fd2b96bddde..e62d139a0c31d16417b5fc46f4b9512d0d2ad525 100644 (file)
@@ -153,8 +153,10 @@ def ltemplatePreRouterStartHook():
     if topotest.version_cmp(krel, '4.15') >= 0 and \
        topotest.version_cmp(krel, '4.18') <= 0:
         l3mdev_accept = 1
-    else:
-        l3mdev_accept = 0
+
+    if topotest.version_cmp(krel, '5.0') >= 0:
+        l3mdev_accept = 1
+
     logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
     #check for mpls
     if tgen.hasmpls != True:
index 21543ab78e4ccc04f96713cbf4fcb7c9f4486e0a..4ecaa4c026e393d099a597ba2c7fc4ebb9085bf5 100644 (file)
@@ -23,15 +23,16 @@ for rtr in rtrs:
         mem_z[rtr] = {'value': int(found.group(1)), 'units': found.group(2)}
         mem_b[rtr] = {'value': int(found.group(3)), 'units': found.group(4)}
 
-luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'none','check if sharpd running')
-doSharp = True
+luCommand('ce1', 'vtysh -c "show mem"', 'qmem sharpd', 'none','check if sharpd running')
+doSharp = False
 found = luLast()
 if ret != False and found != None:
     if len(found.group()):
-        luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'pass','sharpd NOT running, skipping test')
-        doSharp = False
+        doSharp = True
 
-if doSharp == True:
+if doSharp != True:
+    luCommand('ce1', 'vtysh -c "sharp data nexthop"', '.', 'pass','sharpd NOT running, skipping test')
+else:
     luCommand('ce1', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),'','pass','Adding {} routes'.format(num))
     luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num))
     rtrs = ['ce1', 'ce2', 'ce3']
diff --git a/tests/topotests/bgp_maximum_prefix_out/__init__.py b/tests/topotests/bgp_maximum_prefix_out/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..9a68809
--- /dev/null
@@ -0,0 +1,9 @@
+!
+router bgp 65001
+  neighbor 192.168.255.1 remote-as 65002
+  address-family ipv4 unicast
+    redistribute connected
+    neighbor 192.168.255.1 maximum-prefix-out 2
+  exit-address-family
+  !
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf
new file mode 100644 (file)
index 0000000..2416225
--- /dev/null
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip address 172.16.255.250/32
+ ip address 172.16.255.251/32
+ ip address 172.16.255.252/32
+ ip address 172.16.255.253/32
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..1659c4b
--- /dev/null
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+  neighbor 192.168.255.2 remote-as 65001
+  exit-address-family
+  !
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf
new file mode 100644 (file)
index 0000000..08dd374
--- /dev/null
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
new file mode 100644 (file)
index 0000000..d77aa5a
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_maximum_prefix_out.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND 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 if `neighbor <X.X.X.X> maximum-prefix-out <Y>` is working
+correctly.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+    def build(self, *_args, **_opts):
+        tgen = get_topogen(self)
+
+        for routern in range(1, 3):
+            tgen.add_router('r{}'.format(routern))
+
+        switch = tgen.add_switch('s1')
+        switch.add_link(tgen.gears['r1'])
+        switch.add_link(tgen.gears['r2'])
+
+def setup_module(mod):
+    tgen = Topogen(TemplateTopo, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+        router.load_config(
+            TopoRouter.RD_ZEBRA,
+            os.path.join(CWD, '{}/zebra.conf'.format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_BGP,
+            os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+        )
+
+    tgen.start_router()
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+def test_bgp_maximum_prefix_out():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    router = tgen.gears['r2']
+
+    def _bgp_converge(router):
+        output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+        expected = {
+            '192.168.255.2': {
+                'bgpState': 'Established',
+                'addressFamilyInfo': {
+                    'ipv4Unicast': {
+                        'acceptedPrefixCounter': 2
+                    }
+                }
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge, router)
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+    assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+
+if __name__ == '__main__':
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 59ffd36ef396ac8013d51c801781e3e896238c82..f5119468e01b7c3f86eb1dfabb6a601b1c33fbfd 100644 (file)
@@ -5,7 +5,7 @@
 # Part of NetDEF Topology Tests
 #
 # Copyright (c) 2019 by
-# Network Device Education Foundation, Inc. ("NetDEF")
+# Donatas Abraitis <donatas.abraitis@gmail.com>
 #
 # Permission to use, copy, modify, and/or distribute this software
 # for any purpose with or without fee is hereby granted, provided
@@ -33,6 +33,7 @@ import sys
 import json
 import time
 import pytest
+import functools
 
 CWD = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.join(CWD, '../'))
@@ -76,33 +77,40 @@ def teardown_module(mod):
     tgen = get_topogen()
     tgen.stop_topology()
 
-def test_bgp_maximum_prefix_invalid():
+def test_bgp_show_ip_bgp_hostname():
     tgen = get_topogen()
 
     if tgen.routers_have_failure():
         pytest.skip(tgen.errors)
 
-    def _bgp_converge(router, neighbor):
-        cmd = "show ip bgp neighbor {0} json".format(neighbor)
-        while True:
-            output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
-            if output[neighbor]['bgpState'] == 'Established':
-                time.sleep(3)
+    router = tgen.gears['r2']
+
+    def _bgp_converge(router):
+        output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+        expected = {
+            '192.168.255.1': {
+                'bgpState': 'Established',
+                'addressFamilyInfo': {
+                    'ipv4Unicast': {
+                        'acceptedPrefixCounter': 2
+                    }
+                }
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    def _bgp_show_nexthop_hostname_and_ip(router):
+        output = json.loads(router.vtysh_cmd("show ip bgp json"))
+        for nh in output['routes']['172.16.255.253/32'][0]['nexthops']:
+            if 'hostname' in nh and 'ip' in nh:
                 return True
+        return False
 
-    def _bgp_show_nexthop(router, prefix):
-        cmd = "show ip bgp json"
-        output = json.loads(tgen.gears[router].vtysh_cmd(cmd))
-        for nh in output['routes'][prefix][0]['nexthops']:
-            if 'fqdn' in nh:
-                return 'fqdn'
-        return 'ip'
+    test_func = functools.partial(_bgp_converge, router)
+    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
 
-    if _bgp_converge('r2', '192.168.255.1'):
-        assert _bgp_show_nexthop('r2', '172.16.255.254/32') == 'fqdn'
-
-    if _bgp_converge('r1', '192.168.255.2'):
-        assert _bgp_show_nexthop('r1', '172.16.255.253/32') == 'ip'
+    assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+    assert _bgp_show_nexthop_hostname_and_ip(router) == True
 
 if __name__ == '__main__':
     args = ["-s"] + sys.argv[1:]
index 6b4df78c6908e424d482301489053ad3f18f4902..2944b3729c18993f58a68ff1fb353c15cbb5c8fc 100644 (file)
@@ -31,6 +31,7 @@ import sys
 import json
 from functools import partial
 import pytest
+import platform
 
 # Save the Current Working Directory to find configuration files.
 CWD = os.path.dirname(os.path.realpath(__file__))
@@ -68,6 +69,19 @@ def setup_module(mod):
     router_list = tgen.routers()
 
     logger.info('Testing with VRF Lite support')
+    krel = platform.release()
+
+    # May need to adjust handling of vrf traffic depending on kernel version
+    l3mdev_accept = 0
+    if topotest.version_cmp(krel, '4.15') >= 0 and \
+       topotest.version_cmp(krel, '4.18') <= 0:
+        l3mdev_accept = 1
+
+    if topotest.version_cmp(krel, '5.0') >= 0:
+        l3mdev_accept = 1
+
+    logger.info('krel \'{0}\' setting net.ipv4.tcp_l3mdev_accept={1}'.format(
+        krel, l3mdev_accept))
 
     cmds = ['ip link add {0}-cust1 type vrf table 1001',
             'ip link add loop1 type dummy',
@@ -78,6 +92,15 @@ def setup_module(mod):
         for cmd in cmds:
             output = tgen.net[rname].cmd(cmd.format(rname))
 
+        output = tgen.net[rname].cmd('sysctl -n net.ipv4.tcp_l3mdev_accept')
+        logger.info(
+            'router {0}: existing tcp_l3mdev_accept was {1}'.format(
+                rname, output))
+
+        if l3mdev_accept:
+            output = tgen.net[rname].cmd(
+                'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
+
     for rname, router in router_list.iteritems():
         router.load_config(
             TopoRouter.RD_ZEBRA,
index 9f2fef52ea8679b79656bb2821caff9f58988868..fc7581b1f292decb7681fc61c79dd987570a3ffe 100644 (file)
@@ -28,6 +28,7 @@ from subprocess import PIPE as SUB_PIPE
 from subprocess import Popen
 from functools import wraps
 from re import search as re_search
+from tempfile import mkdtemp
 
 import StringIO
 import os
@@ -276,11 +277,19 @@ def reset_config_on_routers(tgen, routerName=None):
 
         run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname)
         init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname)
-        command = "/usr/lib/frr/frr-reload.py  --input {} --test {} > {}". \
-            format(run_cfg_file, init_cfg_file, dname)
+
+        tempdir = mkdtemp()
+        with open(os.path.join(tempdir, 'vtysh.conf'), 'w') as fd:
+            pass
+
+        command = "/usr/lib/frr/frr-reload.py --confdir {} --input {} --test {} > {}". \
+            format(tempdir, run_cfg_file, init_cfg_file, dname)
         result = call(command, shell=True, stderr=SUB_STDOUT,
                       stdout=SUB_PIPE)
 
+        os.unlink(os.path.join(tempdir, 'vtysh.conf'))
+        os.rmdir(tempdir)
+
         # Assert if command fail
         if result > 0:
             logger.error("Delta file creation failed. Command executed %s",
index 1d12d11a2670c3765abf86c9eac736a361a53321..a76d8e4b08a07a8f9b77f20f962666844d75f0f0 100644 (file)
@@ -134,6 +134,7 @@ def teardown_module(mod):
     tgen = get_topogen()
 
     if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True:
+        luShowResults(logger.info)
         print(luFinish())
 
     # This function tears down the whole topology.
index 7c89ada013d38d1135862bdd9f1dd72d6c16d9d4..4ea97a3692c88c94f3658414918e826cd0f416c8 100755 (executable)
@@ -336,6 +336,14 @@ def luNumPass():
 def luResult(target, success, str, logstr=None):
     return LUtil.result(target, success, str, logstr)
 
+def luShowResults(prFunction):
+    printed = 0
+    sf = open(LUtil.fsum_name, 'r')
+    for line in sf:
+        printed+=1
+        prFunction(line.rstrip())
+    sf.close()
+
 def luShowFail():
     printed = 0
     sf = open(LUtil.fsum_name, 'r')
diff --git a/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref
new file mode 100644 (file)
index 0000000..11fd9fe
--- /dev/null
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
index b70ae02266f433923d9a52848f6a1b95a318cb8e..cb0c4af221bff13184a3d1b4f019ac5ec420cb61 100755 (executable)
@@ -89,7 +89,7 @@ sys.path.append(os.path.join(CWD, '../'))
 from lib import topotest
 from lib.topogen import Topogen, TopoRouter, get_topogen
 from lib.topolog import logger
-
+import platform
 
 #####################################################
 ##
@@ -319,7 +319,10 @@ def test_linux_ipv6_kernel_routingTable():
     # Now compare the routing tables (after substituting link-local addresses)
 
     for i in range(1, 5):
-        refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i))
+       if topotest.version_cmp(platform.release(), '5.3') < 0:
+            refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i))
+       else:
+           refTableFile = os.path.join(CWD, 'r{}/ip_6_address.nhg.ref'.format(i))
         if os.path.isfile(refTableFile):
 
             expected = open(refTableFile).read().rstrip()
@@ -333,6 +336,7 @@ def test_linux_ipv6_kernel_routingTable():
                 actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
             # Mask out protocol name or number
             actual = re.sub(r"[ ]+proto [0-9a-z]+ +", "  proto XXXX ", actual)
+            actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
             # Remove ff00::/8 routes (seen on some kernels - not from FRR)
             actual = re.sub(r'ff00::/8.*', '', actual)
 
diff --git a/tests/topotests/pim-basic/r1/bgpd.conf b/tests/topotests/pim-basic/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..8acaac9
--- /dev/null
@@ -0,0 +1,3 @@
+router bgp 65001
+  neighbor 10.0.30.3 remote-as external
+  redistribute connected
index 5740c66e24212792d90dbaca3ea2d61f5120cbe8..cec765699d8bc7abeac57dbe09da9978746fbab5 100644 (file)
@@ -2,9 +2,12 @@ hostname r1
 !
 interface r1-eth0
   ip igmp
-  ip pim sm
+  ip pim
+!
+interface r1-eth1
+  ip pim
 !
 interface lo
-  ip pim sm
+  ip pim
 !
-ip pim rp 10.254.0.1
+ip pim rp 10.254.0.3
diff --git a/tests/topotests/pim-basic/r1/rp-info.json b/tests/topotests/pim-basic/r1/rp-info.json
new file mode 100644 (file)
index 0000000..1f713c2
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "10.254.0.3":[
+    {
+      "outboundInterface":"r1-eth1",
+      "group":"224.0.0.0\/4",
+      "source":"Static"
+    }
+  ]
+}
index 2bf71294d03f6fe2d4ad91321a80fbcccfcf0620..b0a25f12aad4e076d14e3aee337919a3277f1e1d 100644 (file)
@@ -3,6 +3,9 @@ hostname r1
 interface r1-eth0
  ip address 10.0.20.1/24
 !
+interface r1-eth1
+ ip address 10.0.30.1/24
+!
 interface lo
  ip address 10.254.0.1/32
 !
diff --git a/tests/topotests/pim-basic/rp/bgpd.conf b/tests/topotests/pim-basic/rp/bgpd.conf
new file mode 100644 (file)
index 0000000..6b16c06
--- /dev/null
@@ -0,0 +1,3 @@
+router bgp 65003
+   neighbor 10.0.30.1 remote-as external
+   redistribute connected
diff --git a/tests/topotests/pim-basic/rp/pimd.conf b/tests/topotests/pim-basic/rp/pimd.conf
new file mode 100644 (file)
index 0000000..3f1b4d6
--- /dev/null
@@ -0,0 +1,9 @@
+hostname rp
+!
+interface rp-eth0
+  ip pim
+!
+interface lo
+  ip pim
+!
+ip pim rp 10.254.0.3
diff --git a/tests/topotests/pim-basic/rp/upstream.json b/tests/topotests/pim-basic/rp/upstream.json
new file mode 100644 (file)
index 0000000..c33dea4
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "229.1.1.1":{
+    "10.0.20.2":{
+      "sourceStream":true,
+      "inboundInterface":"rp-eth0",
+      "rpfAddress":"10.0.20.2",
+      "source":"10.0.20.2",
+      "group":"229.1.1.1",
+      "state":"NotJ",
+      "joinState":"NotJoined",
+      "regState":"RegNoInfo",
+      "resetTimer":"--:--:--",
+      "refCount":1,
+      "sptBit":0
+    }
+  }
+}
diff --git a/tests/topotests/pim-basic/rp/zebra.conf b/tests/topotests/pim-basic/rp/zebra.conf
new file mode 100644 (file)
index 0000000..0a1359e
--- /dev/null
@@ -0,0 +1,8 @@
+hostname rp
+!
+interface rp-eth0
+ ip address 10.0.30.3/24
+!
+interface lo
+ ip address 10.254.0.3/32
+!
index 6d54b8f2f022f0120569203fb854fff640c9208f..0e0569e23461fe4ae84a82dadabb501eb65aebea 100644 (file)
@@ -28,6 +28,8 @@ test_pim.py: Test pim
 import os
 import sys
 import pytest
+import json
+from functools import partial
 
 CWD = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.join(CWD, '../'))
@@ -47,11 +49,27 @@ class PIMTopo(Topo):
         for routern in range(1, 3):
             tgen.add_router('r{}'.format(routern))
 
+        tgen.add_router('rp')
+
+        # r1 -> .1
+        # r2 -> .2
+        # rp -> .3
+        # loopback network is 10.254.0.X/32
+        #
         # r1 <- sw1 -> r2
+        # r1-eth0 <-> r2-eth0
+        # 10.0.20.0/24
         sw = tgen.add_switch('sw1')
         sw.add_link(tgen.gears['r1'])
         sw.add_link(tgen.gears['r2'])
 
+        # r1 <- sw2 -> rp
+        # r1-eth1 <-> rp-eth0
+        # 10.0.30.0/24
+        sw = tgen.add_switch('sw2')
+        sw.add_link(tgen.gears['r1'])
+        sw.add_link(tgen.gears['rp'])
+
 
 def setup_module(mod):
     "Sets up the pytest environment"
@@ -68,9 +86,14 @@ def setup_module(mod):
             TopoRouter.RD_PIM,
             os.path.join(CWD, '{}/pimd.conf'.format(rname))
         )
+        router.load_config(
+            TopoRouter.RD_BGP,
+            os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+            )
 
     # After loading the configurations, this function loads configured daemons.
     tgen.start_router()
+    #tgen.mininet_cli()
 
 
 def teardown_module(mod):
@@ -80,6 +103,22 @@ def teardown_module(mod):
     # This function tears down the whole topology.
     tgen.stop_topology()
 
+def test_pim_rp_setup():
+    "Ensure basic routing has come up and the rp has an outgoing interface"
+    #Ensure rp and r1 establish pim neighbor ship and bgp has come up
+    #Finally ensure that the rp has an outgoing interface on r1
+    tgen = get_topogen()
+
+    r1 = tgen.gears['r1']
+    json_file = '{}/{}/rp-info.json'.format(CWD, r1.name)
+    expected = json.loads(open(json_file).read())
+
+    test_func = partial(topotest.router_json_cmp,
+                        r1, 'show ip pim rp-info json', expected)
+    _, result = topotest.run_and_expect(test_func, None, count=15, wait=5)
+    assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+    assert result is None, assertmsg
+    #tgen.mininet_cli()
 
 def test_pim_send_mcast_stream():
     "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate"
@@ -90,6 +129,7 @@ def test_pim_send_mcast_stream():
     if tgen.routers_have_failure():
         pytest.skip(tgen.errors)
 
+    rp = tgen.gears['rp']
     r2 = tgen.gears['r2']
     r1 = tgen.gears['r1']
 
@@ -111,7 +151,21 @@ def test_pim_send_mcast_stream():
     }
 
     assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
+    #tgen.mininet_cli()
+
+def test_pim_rp_sees_stream():
+    "Ensure that the RP sees the stream and has acted accordingly"
+    tgen = get_topogen()
+
+    rp = tgen.gears['rp']
+    json_file = '{}/{}/upstream.json'.format(CWD, rp.name)
+    expected = json.loads(open(json_file).read())
 
+    test_func = partial(topotest.router_json_cmp,
+                        rp, 'show ip pim upstream json', expected)
+    _, result = topotest.run_and_expect(test_func, None, count=20, wait=.5)
+    assertmsg = '"{}" JSON output mismatches'.format(rp.name)
+    assert result is None, assertmsg
 
 def test_pim_igmp_report():
     "Send a igmp report from r2->r1 and ensure that the *,G state is created on r1"
index 79c52d30d103de5fd332f1505997432ec85db793..8bec3c5bb68d6a3da38ff300a6a9e4b20319ba61 100644 (file)
@@ -56,6 +56,11 @@ bfdd_options="   -A 127.0.0.1"
 fabricd_options="-A 127.0.0.1"
 vrrpd_options="  -A 127.0.0.1"
 
+# configuration profile
+#
+#frr_profile="traditional"
+#frr_profile="datacenter"
+
 #
 # This is the maximum number of FD's that will be available.
 # Upon startup this is read by the control files and ulimit
index 412cde091099e543441aecba048c790dd472480f..45843faf1363a3aca281a3b1aea8edb89d666ba2 100755 (executable)
@@ -155,7 +155,7 @@ class Config(object):
         try:
             config_text = subprocess.check_output(
                 bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -",
-                shell=True, stderr=subprocess.STDOUT)
+                shell=True)
         except subprocess.CalledProcessError as e:
             ve = VtyshMarkException(e)
             ve.output = e.output
@@ -934,6 +934,16 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
                     lines_to_del_to_del.append((ctx_keys, route_target_export_line))
                     lines_to_add_to_del.append((ctx_keys, route_target_both_line))
 
+        # Deleting static routes under a vrf can lead to time-outs if each is sent
+        # as separate vtysh -c commands. Change them from being in lines_to_del and
+        # put the "no" form in lines_to_add
+        if ctx_keys[0].startswith('vrf ') and line:
+            if (line.startswith('ip route') or
+                line.startswith('ipv6 route')):
+                add_cmd = ('no ' + line)
+                lines_to_add.append((ctx_keys, add_cmd))
+                lines_to_del_to_del.append((ctx_keys, line))
+
         if not deleted:
             found_add_line = line_exist(lines_to_add, ctx_keys, line)
 
@@ -1054,6 +1064,19 @@ def compare_context_objects(newconf, running):
                 for line in running_ctx.lines:
                     lines_to_del.append((running_ctx_keys, line))
 
+            # Some commands can happen at higher counts that make
+            # doing vtysh -c inefficient (and can time out.)  For
+            # these commands, instead of adding them to lines_to_del,
+            # add the "no " version to lines_to_add.
+            elif (running_ctx_keys[0].startswith('ip route') or
+                  running_ctx_keys[0].startswith('ipv6 route') or
+                  running_ctx_keys[0].startswith('access-list') or
+                  running_ctx_keys[0].startswith('ipv6 access-list') or
+                  running_ctx_keys[0].startswith('ip prefix-list') or
+                  running_ctx_keys[0].startswith('ipv6 prefix-list')):
+                add_cmd = ('no ' + running_ctx_keys[0],)
+                lines_to_add.append((add_cmd, None))
+
             # Non-global context
             elif running_ctx_keys and not any("address-family" in key for key in running_ctx_keys):
                 lines_to_del.append((running_ctx_keys, None))
@@ -1105,7 +1128,7 @@ def vtysh_config_available(bindir, confdir):
 
     try:
         cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t']
-        output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
+        output = subprocess.check_output(cmd).strip()
 
         if 'VTY configuration is locked by other VTY' in output.decode('utf-8'):
             print(output)
@@ -1363,7 +1386,7 @@ if __name__ == '__main__':
 
                     while True:
                         try:
-                            _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+                            _ = subprocess.check_output(cmd)
 
                         except subprocess.CalledProcessError:
 
@@ -1392,6 +1415,11 @@ if __name__ == '__main__':
                     if line == '!':
                         continue
 
+                    # Don't run "no" commands twice since they can error
+                    # out the second time due to first deletion
+                    if x == 1 and ctx_keys[0].startswith('no '):
+                        continue
+
                     cmd = line_for_vtysh_file(ctx_keys, line, False)
                     lines_to_configure.append(cmd)
 
@@ -1408,7 +1436,7 @@ if __name__ == '__main__':
                             fh.write(line + '\n')
 
                     try:
-                        subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename], stderr=subprocess.STDOUT)
+                        subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename])
                     except subprocess.CalledProcessError as e:
                         log.warning("frr-reload.py failed due to\n%s" % e.output)
                         reload_ok = False
index 50f7ecaa9f61b2640d77f085edd38aa93a91b9e6..d9816c2568197ef9035d133851e32db5ae46aafa 100755 (executable)
@@ -21,6 +21,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh
 FRR_USER="@enable_user@" # frr
 FRR_GROUP="@enable_group@" # frr
 FRR_VTY_GROUP="@enable_vty_group@" # frrvty
+FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
 
 # Local Daemon selection may be done by using /etc/frr/daemons.
 # See /usr/share/doc/frr/README.Debian.gz for further information.
@@ -151,7 +152,7 @@ start()
                        --pidfile=`pidfile $dmn-$inst` \
                        --exec "$D_PATH/$dmn" \
                        -- \
-                       `eval echo "$""$dmn""_options"` -n "$inst"
+                       `eval echo "$""$dmn""_options"` $frr_global_options -n "$inst"
        else
                if ! check_daemon $dmn; then
                        echo -n " (binary does not exist)"
@@ -164,14 +165,14 @@ start()
                                --pidfile=`pidfile $dmn` \
                                --exec "$valgrind" \
                                -- --trace-children=no --leak-check=full --log-file=/var/log/frr/$dmn-valgrind.log $D_PATH/$dmn \
-                               `eval echo "$""$dmn""_options"`
+                               `eval echo "$""$dmn""_options"` $frr_global_options
                else
                        ${SSD} \
                                --start \
                                --pidfile=`pidfile $dmn` \
                                --exec "$D_PATH/$dmn" \
                                -- \
-                               `eval echo "$""$dmn""_options"`
+                               `eval echo "$""$dmn""_options"` $frr_global_options
                fi
        fi
 
@@ -188,7 +189,7 @@ start()
                        --pidfile=`pidfile staticd` \
                        --exec "$D_PATH/staticd" \
                        -- \
-                       `eval echo "$"staticd"_options"`
+                       `eval echo "$"staticd"_options"` $frr_global_options
        fi
 }
 
@@ -511,6 +512,18 @@ fi
 # Read configuration variable file if it is present
 [ -r /etc/default/frr ] && . /etc/default/frr
 
+if test -z "$frr_profile"; then
+       # try to autodetect config profile
+       if test -d /etc/cumulus; then
+               frr_profile=datacenter
+       # elif test ...; then
+       # -- add your distro/system here
+       elif test -n "$FRR_DEFAULT_PROFILE"; then
+               frr_profile="$FRR_DEFAULT_PROFILE"
+       fi
+fi
+test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile"
+
 MAX_INSTANCES=${MAX_INSTANCES:=5}
 
 # Set priority of un-startable daemons to 'no' and substitute 'yes' to '0'
@@ -561,7 +574,7 @@ case "$1" in
                        stop_prio 0 $dmn
                fi
 
-               if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then
+               if [ -n "$dmn" -a "$dmn" != "zebra" ]; then
                        [ -n "$dmn" ] && eval "${dmn/-/_}=0"
                        start_watchfrr
                fi
index 3fc38d4bed807bedd3149d7f2beafbf42cee14f3..0dfdfd0efabeebaacc019ae896aca38ed62481f6 100644 (file)
@@ -24,6 +24,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh
 FRR_USER="@enable_user@" # frr
 FRR_GROUP="@enable_group@" # frr
 FRR_VTY_GROUP="@enable_vty_group@" # frrvty
+FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
 
 # ORDER MATTERS FOR $DAEMONS!
 # - keep zebra first
@@ -151,7 +152,7 @@ daemon_start() {
        instopt="${inst:+-n $inst}"
        eval args="\$${daemon}_options"
 
-       if eval "$all_wrap $wrap $bin -d $instopt $args"; then
+       if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then
                log_success_msg "Started $dmninst"
                vtysh_b "$daemon"
        else
@@ -298,6 +299,18 @@ if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'
        unset watchfrr_options
 fi
 
+if test -z "$frr_profile"; then
+       # try to autodetect config profile
+       if test -d /etc/cumulus; then
+               frr_profile=datacenter
+       # elif test ...; then
+       # -- add your distro/system here
+       elif test -n "$FRR_DEFAULT_PROFILE"; then
+               frr_profile="$FRR_DEFAULT_PROFILE"
+       fi
+fi
+test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile"
+
 #
 # other defaults and dispatch
 #
diff --git a/tools/symalyzer.html b/tools/symalyzer.html
new file mode 100644 (file)
index 0000000..eefeee3
--- /dev/null
@@ -0,0 +1,347 @@
+<html>
+<!--
+  - 2019 by David Lamparter, placed in public domain
+  -->
+  <head>
+    <title>Symalyzer report</title>
+    <style type="text/css">
+html {
+  margin:auto;
+  max-width:70em;
+  font-family:Fira Sans, sans-serif;
+}
+dl {
+  display:grid;
+  grid-template-columns: 1.4em 1.4em 1fr 1fr;
+  grid-auto-rows: auto;
+}
+dt.dir {
+  background-color:#ff8;
+  color:#000;
+  border:1px solid #000;
+  border-bottom:2px solid #000;
+  font-size:14pt;
+  padding:2pt 15pt;
+  margin:0pt;
+  margin-top:10pt;
+  grid-column:1 / -1;
+}
+dt.file {
+  background-color:#ffa;
+  color:#000;
+  border-bottom:1px solid #000;
+  font-size:12pt;
+  padding:2pt 15pt;
+  margin:5pt 0pt;
+  grid-column:1 / -1;
+}
+dt.file.filehidden {
+  background-color:#ffc;
+  font-size:10pt;
+  padding:0.5pt 15pt;
+  margin-bottom:-5pt;
+}
+dd {
+  display:inline-block;
+  vertical-align:middle;
+  margin:0;
+}
+dd.symtype {
+  grid-column:1;
+
+  border:1px solid #666;
+  text-align:center;
+}
+dd.symklass {
+  grid-column:2;
+
+  border:1px solid #666;
+  text-align:center;
+}
+dd.symname {
+  grid-column:3;
+
+  font-family:monospace;
+  padding:0 0.5em;
+  padding-top:2px;
+  border-bottom:1px dashed #ccc;
+}
+dd.symloc {
+  grid-column:4;
+
+  padding:0 0.5em;
+  border-bottom:1px dashed #ccc;
+}
+.symloc-unknown {
+  font-style:italic;
+  color:#aaa;
+}
+
+.symtype.sym-static {
+  background-color:#cf4;
+  color:#000;
+}
+.symtype.sym-extrastatic {
+  background-color:#fe8;
+  color:#000;
+}
+.symtype.sym-liblocal {
+  background-color:#fc6;
+  color:#000;
+}
+
+.symklass.symk-T {
+  background-color:#ddd;
+  color:#000;
+}
+.symklass.symk-B,
+.symklass.symk-C,
+.symklass.symk-D {
+  background-color:#faa;
+  color:#000;
+}
+.symklass.symk-R {
+  background-color:#fd8;
+  color:#000;
+}
+
+.symtype.sym-api {
+  background-color:#d9f;
+  color:#000;
+}
+.symname.sym-api,
+.symloc.sym-api {
+  background-color:#f8e8ff;
+}
+
+dt.file.dirhidden,
+dd.dirhidden {
+  display:none;
+}
+dd.filehidden {
+  display:none;
+}
+dd.symhidden {
+  display:none;
+}
+
+ul {
+  font-size:10pt;
+}
+li {
+  margin-bottom:6pt;
+  text-indent:-2.5em;
+  margin-left:2.5em;
+}
+code {
+  background-color:#eee;
+  color:#060;
+  text-decoration:underline;
+}
+b.symtype,
+b.symklass {
+  display:inline-block;
+  text-align:center;
+  border:1px solid #666;
+  width:1.4em;
+  text-indent:0;
+}
+    </style>
+    <script src="jquery-3.4.1.min.js"></script>
+    <script>
+
+function dirtoggle(elem, visible) {
+  if (visible) {
+    elem.removeClass("dirhidden");
+  } else {
+    elem.addClass("dirhidden");
+  }
+
+  var next = elem.next();
+  while (next.is("dd") || next.is("dt.file")) {
+    if (visible) {
+      next.removeClass("dirhidden");
+    } else {
+      next.addClass("dirhidden");
+    }
+    next = next.next();
+  }
+}
+
+function filetoggle(elem, visible) {
+  if (visible) {
+    elem.removeClass("filehidden");
+  } else {
+    elem.addClass("filehidden");
+  }
+
+  var next = elem.next();
+  while (next.is("dd")) {
+    if (visible) {
+      next.removeClass("filehidden");
+    } else {
+      next.addClass("filehidden");
+    }
+    next = next.next();
+  }
+}
+
+function symtoggle(elem, visible) {
+  if (visible) {
+    elem.removeClass("symhidden");
+  } else {
+    elem.addClass("symhidden");
+  }
+
+  var next = elem.next();
+  while (next.is(".symklass") || next.is(".symname") || next.is(".symloc")) {
+    if (visible) {
+      next.removeClass("symhidden");
+    } else {
+      next.addClass("symhidden");
+    }
+    next = next.next();
+  }
+}
+
+
+$(document).ready(function(){
+  $("dt.dir").each(function(){
+    var elem = $(this);
+
+    elem.click(function(){
+      dirtoggle(elem, elem.is(".dirhidden"));
+    });
+
+    dirtoggle(elem, false);
+  });
+
+  $("dt.file").each(function(){
+    var elem = $(this);
+
+    elem.click(function(){
+      filetoggle(elem, elem.is(".filehidden"));
+    });
+
+    /* filetoggle(elem, false); */
+  });
+
+  $("#f_hide_all").click(function(){
+    $("dt.file").each(function(){
+      filetoggle($(this), false);
+    });
+  });
+  $("#f_show_all").click(function(){
+    $("dt.file").each(function(){
+      filetoggle($(this), true);
+    });
+  });
+
+  $("#s_show_all").click(function(){
+    $("dd.symtype").each(function(){
+      symtoggle($(this), true);
+    });
+  });
+  $("#s_hide_all").click(function(){
+    $("dd.symtype").each(function(){
+      symtoggle($(this), false);
+    });
+  });
+  $("#s_show_vars").click(function(){
+    $("dd.symtype").each(function(){
+      var elem_type = $(this);
+      if (elem_type.text() === "A") {
+        return;
+      }
+
+      var elem_klass = elem_type.next();
+
+      if ("BbCDdGgnRrSs".indexOf(elem_klass.text()) >= 0) {
+        symtoggle(elem_type, true);
+      }
+    });
+  });
+  $("#s_show_funcs").click(function(){
+    $("dd.symtype").each(function(){
+      var elem_type = $(this);
+      if (elem_type.text() === "A") {
+        return;
+      }
+
+      var elem_klass = elem_type.next();
+
+      if ("Tt".indexOf(elem_klass.text()) >= 0) {
+        symtoggle(elem_type, true);
+      }
+    });
+  });
+  $("#s_show_api").click(function(){
+    $("dd.sym-api").each(function(){
+      symtoggle($(this), true);
+    });
+  });
+
+  $("#jsbuttons").show();
+});
+    </script>
+  </head>
+  <body>
+    <table style="display:none" id="jsbuttons">
+      <tr><td>Files</td><td>
+          <button type="button" id="f_hide_all">Hide all</button>
+          <button type="button" id="f_show_all">Show all</button>
+        </td></tr>
+      <tr><td>Symbols</td><td>
+          <button type="button" id="s_hide_all">Hide all</button>
+          <button type="button" id="s_show_all">Show all</button><br>
+          <button type="button" id="s_show_vars">Show variables</button>
+          <button type="button" id="s_show_funcs">Show functions</button>
+          <button type="button" id="s_show_api">Show module/API usage</button>
+        </td></tr>
+    </table>
+    <div style="display:grid;grid-template-columns:1fr 1fr;">
+      <ul>
+        <li><b class="symtype sym-static">S</b> means the symbol is not used outside its own file.
+          It could either be completely unused or used locally.  It might be appropriate to make it
+          <code>static</code>.</li>
+        <li><b class="symtype sym-extrastatic">Z</b> means the symbol is not used outside its own file,
+          and it's not visible to the outside of the library or daemon (i.e. ELF hidden linkage.)
+          It could still be completely unused, or used within the library.  It might be appropriate to make it
+          <code>static</code>.</li>
+        <li><b class="symtype sym-liblocal">L</b> means the symbol is used from other files in the library,
+          but not from outside.  It might be appropriate to make it <code>DSO_LOCAL</code>.</li>
+        <li><b class="symtype sym-api">A</b> means the symbol is used from some other file, most likely a
+          loadable module.  Note this is only flagged for symbols in executable files, not libraries.</li>
+      </ul>
+      <ul>
+        <li><b class="symklass symk-T">T</b> are normal functions ("program <u>T</u>ext")</li>
+        <li style="text-indent:0;margin-left:0">
+          <b class="symklass symk-B">B</b> (<u>B</u>SS),<br>
+          <b class="symklass symk-C">C</b> (<u>C</u>ommon),<br>
+          <b class="symklass symk-D">D</b> (<u>D</u>ata)<br>
+          are various types of writable global variables</li>
+        <li><b class="symklass symk-R">R</b> are read-only global variables ("<u>R</u>odata")</li>
+      </ul>
+    </div>
+    <dl>
+      {%- for subdir, subreport in dirgroups.items()|sort %}
+        <dt class="dir">{{ subdir }}</dt>
+        {%- for obj, reports in subreport.items()|sort %}
+          <dt class="file">{{ obj }}</dt>
+          {%- for report in reports|sort %}
+          {#- <dd class="{{ report.idlong }}"> #}
+              <dd class="sym-{{ report.idlong }} symtype" title="{{ report.title }}">{{ report.idshort }}</dd>
+              <dd class="sym-{{ report.idlong }} symk-{{ report.sym.klass }} symklass" title="{{ klasses.get(report.sym.klass, '???') }}">{{ report.sym.klass }}</dd>
+              <dd class="sym-{{ report.idlong }} symname">{{ report.sym.name }}</dd>
+              {% if report.sym.loc %}
+              <dd class="sym-{{ report.idlong }} symloc">{{ report.sym.loc }}</dd>
+              {% else %}
+              <dd class="sym-{{ report.idlong }} symloc symloc-unknown">unknown</dd>
+              {% endif %}
+          {#- </dd> #}
+          {%- endfor %}
+        {%- endfor %}
+      {%- endfor %}
+    </dl>
+  </body>
+</html>
diff --git a/tools/symalyzer.py b/tools/symalyzer.py
new file mode 100755 (executable)
index 0000000..b3b5c4e
--- /dev/null
@@ -0,0 +1,383 @@
+#!/usr/bin/python3
+#
+# 2019 by David Lamparter, placed in public domain
+#
+# This tool generates a report of possibly unused symbols in the build.  It's
+# particularly useful for libfrr to find bitrotting functions that aren't even
+# used anywhere anymore.
+#
+# Note that the tool can't distinguish between "a symbol is completely unused"
+# and "a symbol is used only in its file" since file-internal references are
+# invisible in nm output.  However, the compiler will warn you if a static
+# symbol is unused.
+#
+# This tool is only tested on Linux, it probably needs `nm` from GNU binutils
+# (as opposed to BSD `nm`).  Could use pyelftools instead but that's a lot of
+# extra work.
+#
+# This is a developer tool, please don't put it in any packages :)
+
+import sys, os, subprocess
+import re
+from collections import namedtuple
+
+class MakeVars(object):
+    '''
+    makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile
+    '''
+    def __init__(self):
+        self._data = dict()
+
+    def getvars(self, varlist):
+        '''
+        get a batch list of variables from make.  faster than individual calls.
+        '''
+        rdfd, wrfd = os.pipe()
+
+        shvars = ['shvar-%s' % s for s in varlist]
+        make = subprocess.Popen(['make', '-s', 'VARFD=%d' % wrfd] + shvars, pass_fds = [wrfd])
+        os.close(wrfd)
+        data = b''
+
+        rdf = os.fdopen(rdfd, 'rb')
+        while True:
+            rdata = rdf.read()
+            if len(rdata) == 0:
+                break
+            data += rdata
+
+        del rdf
+        make.wait()
+
+        data = data.decode('US-ASCII').strip().split('\n')
+        for row in data:
+            k, v = row.split('=', 1)
+            v = v[1:-1]
+            self._data[k] = v
+
+    def __getitem__(self, k):
+        if k not in self._data:
+            self.getvars([k])
+        return self._data[k]
+
+    def get(self, k, defval = None):
+        if k not in self._data:
+            self.getvars([k])
+        return self._data[k] or defval
+
+SymRowBase = namedtuple('SymRow', ['target', 'object', 'name', 'address', 'klass', 'typ', 'size', 'line', 'section', 'loc'])
+class SymRow(SymRowBase):
+    '''
+    wrapper around a line of `nm` output
+    '''
+    lib_re = re.compile(r'/lib[^/]+\.(so|la)$')
+    def is_global(self):
+        return self.klass.isupper() or self.klass in 'uvw'
+    def scope(self):
+        if self.lib_re.search(self.target) is None:
+            return self.target
+        # "global"
+        return None
+
+    def is_export(self):
+        '''
+        FRR-specific list of symbols which are considered "externally used"
+
+        e.g. hooks are by design APIs for external use, same for qobj_t_*
+        frr_inet_ntop is here because it's used through an ELF alias to
+        "inet_ntop()"
+        '''
+        if self.name in ['main', 'frr_inet_ntop', '_libfrr_version']:
+            return True
+        if self.name.startswith('_hook_'):
+            return True
+        if self.name.startswith('qobj_t_'):
+            return True
+        return False
+
+class Symbols(dict):
+    '''
+    dict of all symbols in all libs & executables
+    '''
+
+    from_re = re.compile(r'^Symbols from (.*?):$')
+    lt_re = re.compile(r'^(.*/)([^/]+)\.l[oa]$')
+
+    def __init__(self):
+        super().__init__()
+
+    class ReportSym(object):
+        def __init__(self, sym):
+            self.sym = sym
+        def __repr__(self):
+            return '<%-25s %-40s [%s]>' % (self.__class__.__name__ + ':', self.sym.name, self.sym.loc)
+        def __lt__(self, other):
+            return self.sym.name.__lt__(other.sym.name)
+
+    class ReportSymCouldBeStaticAlreadyLocal(ReportSym):
+        idshort = 'Z'
+        idlong = 'extrastatic'
+        title = "symbol is local to library, but only used in its source file (make static?)"
+    class ReportSymCouldBeStatic(ReportSym):
+        idshort = 'S'
+        idlong = 'static'
+        title = "symbol is only used in its source file (make static?)"
+    class ReportSymCouldBeLibLocal(ReportSym):
+        idshort = 'L'
+        idlong = 'liblocal'
+        title = "symbol is only used inside of library"
+    class ReportSymModuleAPI(ReportSym):
+        idshort = 'A'
+        idlong = 'api'
+        title = "symbol (in executable) is referenced externally from a module"
+
+    class Symbol(object):
+        def __init__(self, name):
+            super().__init__()
+            self.name = name
+            self.defs = {}
+            self.refs = []
+
+        def process(self, row):
+            scope = row.scope()
+            if row.section == '*UND*':
+                self.refs.append(row)
+            else:
+                self.defs.setdefault(scope, []).append(row)
+
+        def evaluate(self, out):
+            '''
+            generate output report
+
+            invoked after all object files have been read in, so it can look
+            at inter-object-file relationships
+            '''
+            if len(self.defs) == 0:
+                out.extsyms.add(self.name)
+                return
+
+            for scopename, symdefs in self.defs.items():
+                common_defs = [symdef for symdef in symdefs if symdef.section == '*COM*']
+                proper_defs = [symdef for symdef in symdefs if symdef.section != '*COM*']
+
+                if len(proper_defs) > 1:
+                    print(self.name, ' DUPLICATE')
+                    print('\tD: %s %s' % (scopename, '\n\t\t'.join([repr(s) for s in symdefs])))
+                    for syms in self.refs:
+                        print('\tR: %s' % (syms, ))
+                    return
+
+                if len(proper_defs):
+                    primary_def = proper_defs[0]
+                elif len(common_defs):
+                    # "common" = global variables without initializer;
+                    # they can occur in multiple .o files and the linker will
+                    # merge them into one variable/storage location.
+                    primary_def = common_defs[0]
+                else:
+                    # undefined symbol, e.g. libc
+                    continue
+
+                if scopename is not None and len(self.refs) > 0:
+                    for ref in self.refs:
+                        if ref.target != primary_def.target and ref.target.endswith('.la'):
+                            outobj = out.report.setdefault(primary_def.object, [])
+                            outobj.append(out.ReportSymModuleAPI(primary_def))
+                            break
+
+                if len(self.refs) == 0:
+                    if primary_def.is_export():
+                        continue
+                    outobj = out.report.setdefault(primary_def.object, [])
+                    if primary_def.visible:
+                        outobj.append(out.ReportSymCouldBeStatic(primary_def))
+                    else:
+                        outobj.append(out.ReportSymCouldBeStaticAlreadyLocal(primary_def))
+                    continue
+
+                if scopename is None and primary_def.visible:
+                    # lib symbol
+                    for ref in self.refs:
+                        if ref.target != primary_def.target:
+                            break
+                    else:
+                        outobj = out.report.setdefault(primary_def.object, [])
+                        outobj.append(out.ReportSymCouldBeLibLocal(primary_def))
+
+
+    def evaluate(self):
+        self.extsyms = set()
+        self.report = {}
+
+        for sym in self.values():
+            sym.evaluate(self)
+
+    def load(self, target, files):
+        def libtoolmustdie(fn):
+            m = self.lt_re.match(fn)
+            if m is None:
+                return fn
+            return m.group(1) + '.libs/' + m.group(2) + '.o'
+
+        def libtooltargetmustdie(fn):
+            m = self.lt_re.match(fn)
+            if m is None:
+                a, b = fn.rsplit('/', 1)
+                return '%s/.libs/%s' % (a, b)
+            return m.group(1) + '.libs/' + m.group(2) + '.so'
+
+        files = list(set([libtoolmustdie(fn) for fn in files]))
+
+        def parse_nm_output(text):
+            filename = None
+            path_rel_to = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+            for line in text.split('\n'):
+                if line.strip() == '':
+                    continue
+                m = self.from_re.match(line)
+                if m is not None:
+                    filename = m.group(1)
+                    continue
+                if line.startswith('Name'):
+                    continue
+
+                items = [i.strip() for i in line.split('|')]
+                loc = None
+                if '\t' in items[-1]:
+                    items[-1], loc = items[-1].split('\t', 1)
+                    fn, lno = loc.rsplit(':', 1)
+                    fn = os.path.relpath(fn, path_rel_to)
+                    loc = '%s:%s' % (fn, lno)
+
+                items[1] = int(items[1] if items[1] != '' else '0', 16)
+                items[4] = int(items[4] if items[4] != '' else '0', 16)
+                items.append(loc)
+                row = SymRow(target, filename, *items)
+
+                if row.section == '.group' or row.name == '_GLOBAL_OFFSET_TABLE_':
+                    continue
+                if not row.is_global():
+                    continue
+
+                yield row
+
+        visible_syms = set()
+
+        # the actual symbol report uses output from the individual object files
+        # (e.g. lib/.libs/foo.o), but we also read the linked binary (e.g.
+        # lib/.libs/libfrr.so) to determine which symbols are actually visible
+        # in the linked result (this covers ELF "hidden"/"internal" linkage)
+
+        libfile = libtooltargetmustdie(target)
+        nmlib = subprocess.Popen(['nm', '-l', '-g', '--defined-only', '-f', 'sysv', libfile], stdout = subprocess.PIPE)
+        out = nmlib.communicate()[0].decode('US-ASCII')
+
+        for row in parse_nm_output(out):
+            visible_syms.add(row.name)
+
+        nm = subprocess.Popen(['nm', '-l', '-f', 'sysv'] + files, stdout = subprocess.PIPE)
+        out = nm.communicate()[0].decode('US-ASCII')
+
+        for row in parse_nm_output(out):
+            row.visible = row.name in visible_syms
+            sym = self.setdefault(row.name, self.Symbol(row.name))
+            sym.process(row)
+
+
+def write_html_report(syms):
+    try:
+        import jinja2
+    except ImportError:
+        sys.stderr.write('jinja2 could not be imported, not writing HTML report!\n')
+        return
+
+    self_path = os.path.dirname(os.path.abspath(__file__))
+    jenv = jinja2.Environment(loader=jinja2.FileSystemLoader(self_path))
+    template = jenv.get_template('symalyzer.html')
+
+    dirgroups = {}
+    for fn, reports in syms.report.items():
+        dirname, filename = fn.replace('.libs/', '').rsplit('/', 1)
+        dirgroups.setdefault(dirname, {})[fn] = reports
+
+    klasses = {
+        'T': 'code / plain old regular function (Text)',
+        'D': 'global variable, read-write, with nonzero initializer (Data)',
+        'B': 'global variable, read-write, with zero initializer (BSS)',
+        'C': 'global variable, read-write, with zero initializer (Common)',
+        'R': 'global variable, read-only (Rodata)',
+    }
+
+    with open('symalyzer_report.html.tmp', 'w') as fd:
+        fd.write(template.render(dirgroups = dirgroups, klasses = klasses))
+    os.rename('symalyzer_report.html.tmp', 'symalyzer_report.html')
+
+    if not os.path.exists('jquery-3.4.1.min.js'):
+        url = 'https://code.jquery.com/jquery-3.4.1.min.js'
+        sys.stderr.write(
+            'trying to grab a copy of jquery from %s\nif this fails, please get it manually (the HTML output is done.)\n' % (url))
+        import requests
+        r = requests.get('https://code.jquery.com/jquery-3.4.1.min.js')
+        if r.status_code != 200:
+            sys.stderr.write('failed -- please download jquery-3.4.1.min.js and put it next to the HTML report\n')
+        else:
+            with open('jquery-3.4.1.min.js.tmp', 'w') as fd:
+                fd.write(r.text)
+            os.rename('jquery-3.4.1.min.js.tmp', 'jquery-3.4.1.min.js.tmp')
+            sys.stderr.write('done.\n')
+
+def automake_escape(s):
+    return s.replace('.', '_').replace('/', '_')
+
+if __name__ == '__main__':
+    mv = MakeVars()
+
+    if not (os.path.exists('config.version') and os.path.exists('lib/.libs/libfrr.so')):
+        sys.stderr.write('please execute this script in the root directory of an FRR build tree\n')
+        sys.stderr.write('./configure && make need to have completed successfully\n')
+        sys.exit(1)
+
+    amtargets = ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES']
+    targets = []
+
+    mv.getvars(amtargets)
+    for amtarget in amtargets:
+        targets.extend([item for item in mv[amtarget].strip().split() if item != 'tools/ssd'])
+
+    mv.getvars(['%s_LDADD' % automake_escape(t) for t in targets])
+    ldobjs = targets[:]
+    for t in targets:
+        ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split()
+        for item in ldadd:
+            if item.startswith('-'):
+                continue
+            if item.endswith('.a'):
+                ldobjs.append(item)
+
+    mv.getvars(['%s_OBJECTS' % automake_escape(o) for o in ldobjs])
+
+    syms = Symbols()
+
+    for t in targets:
+        objs = mv['%s_OBJECTS' % automake_escape(t)].strip().split()
+        ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split()
+        for item in ldadd:
+            if item.startswith('-'):
+                continue
+            if item.endswith('.a'):
+                objs.extend(mv['%s_OBJECTS' % automake_escape(item)].strip().split())
+
+        sys.stderr.write('processing %s...\n' % t)
+        sys.stderr.flush()
+        #print(t, '\n\t', objs)
+        syms.load(t, objs)
+
+    syms.evaluate()
+
+    for obj, reports in sorted(syms.report.items()):
+        print('%s:' % obj)
+        for report in reports:
+            print('\t%r' % report)
+
+    write_html_report(syms)
index 57eec108cbc7263acad4fd19b5cbe4c49610ba7e..07358e0383801ed02a4d10fae1033387ce6c4f10 100644 (file)
@@ -7,7 +7,7 @@ noinst_LIBRARIES += vrrpd/libvrrp.a
 sbin_PROGRAMS += vrrpd/vrrpd
 # dist_examples_DATA += staticd/staticd.conf.sample
 vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c
-man8 += $(MANBUILD)/vrrpd.8
+man8 += $(MANBUILD)/frr-vrrpd.8
 endif
 
 vrrpd_libvrrp_a_SOURCES = \
@@ -15,6 +15,7 @@ vrrpd_libvrrp_a_SOURCES = \
        vrrpd/vrrp_arp.c \
        vrrpd/vrrp_debug.c \
        vrrpd/vrrp_ndisc.c \
+       vrrpd/vrrp_northbound.c \
        vrrpd/vrrp_packet.c \
        vrrpd/vrrp_vty.c \
        vrrpd/vrrp_zebra.c \
@@ -35,3 +36,6 @@ vrrpd/vrrp_vty.$(OBJEXT): vrrpd/vrrp_vty_clippy.c
 
 vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c
 vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@
+nodist_vrrpd_vrrpd_SOURCES = \
+       yang/frr-vrrpd.yang.c \
+       # end
index 42bb154f985e988c96d5d1e89ddd9d571a41caaa..03a08dd86b41ac43be9066c93c64569191918661 100644 (file)
@@ -211,7 +211,16 @@ static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp)
                return NULL;
        }
 
-       p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT);
+       p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id);
+
+       if (!p) {
+               DEBUGD(&vrrp_dbg_zebra,
+                      VRRP_LOGPFX
+                      "Tried to lookup interface %d, parent of %s, but it doesn't exist",
+                      mvl_ifp->link_ifindex, mvl_ifp->name);
+               return NULL;
+       }
+
        uint8_t vrid = mvl_ifp->hw_addr[5];
 
        return vrrp_lookup(p, vrid);
@@ -397,9 +406,10 @@ static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
        return false;
 }
 
-int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip)
+int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
 {
-       int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET;
+       struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6;
+       int af = r->family;
 
        assert(r->family == af);
        assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6));
@@ -443,7 +453,7 @@ int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
 
        ip.ipa_type = IPADDR_V4;
        ip.ipaddr_v4 = v4;
-       return vrrp_add_ip(vr->v4, &ip);
+       return vrrp_add_ip(vr, &ip);
 }
 
 int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
@@ -454,15 +464,17 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
 
        ip.ipa_type = IPADDR_V6;
        ip.ipaddr_v6 = v6;
-       return vrrp_add_ip(vr->v6, &ip);
+       return vrrp_add_ip(vr, &ip);
 }
 
-int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip)
+int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
 {
        struct listnode *ln, *nn;
        struct ipaddr *iter;
        int ret = 0;
 
+       struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6;
+
        if (!vrrp_has_ip(r->vr, ip))
                return 0;
 
@@ -488,7 +500,7 @@ int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
 
        ip.ipa_type = IPADDR_V6;
        ip.ipaddr_v6 = v6;
-       return vrrp_del_ip(vr->v6, &ip);
+       return vrrp_del_ip(vr, &ip);
 }
 
 int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
@@ -497,7 +509,7 @@ int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
 
        ip.ipa_type = IPADDR_V4;
        ip.ipaddr_v4 = v4;
-       return vrrp_del_ip(vr->v4, &ip);
+       return vrrp_del_ip(vr, &ip);
 }
 
 
@@ -525,8 +537,9 @@ static bool vrrp_attach_interface(struct vrrp_router *r)
        /* Search for existing interface with computed MAC address */
        struct interface **ifps;
 
-       size_t ifps_cnt = if_lookup_by_hwaddr(
-               r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT);
+       size_t ifps_cnt =
+               if_lookup_by_hwaddr(r->vmac.octet, sizeof(r->vmac.octet), &ifps,
+                                   r->vr->ifp->vrf_id);
 
        /*
         * Filter to only those macvlan interfaces whose parent is the base
@@ -649,12 +662,12 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr)
        XFREE(MTYPE_VRRP_RTR, vr);
 }
 
-struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid)
+struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid)
 {
        struct vrrp_vrouter vr;
 
        vr.vrid = vrid;
-       vr.ifp = ifp;
+       vr.ifp = (struct interface *)ifp;
 
        return hash_lookup(vrrp_vrouters_hash, &vr);
 }
@@ -1048,6 +1061,8 @@ done:
  *
  * This function:
  * - Creates two sockets, one for Tx, one for Rx
+ * - Binds the Tx socket to the macvlan device, if necessary (VRF case)
+ * - Binds the Rx socket to the base interface
  * - Joins the Rx socket to the appropriate VRRP multicast group
  * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for
  *   all transmitted IPvX packets
@@ -1074,8 +1089,10 @@ static int vrrp_socket(struct vrrp_router *r)
        bool failed = false;
 
        frr_with_privs(&vrrp_privs) {
-               r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
-               r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
+               r->sock_rx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP,
+                                       r->vr->ifp->vrf_id, NULL);
+               r->sock_tx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP,
+                                       r->vr->ifp->vrf_id, NULL);
        }
 
        if (r->sock_rx < 0 || r->sock_tx < 0) {
@@ -1088,6 +1105,27 @@ static int vrrp_socket(struct vrrp_router *r)
                goto done;
        }
 
+       /*
+        * Bind Tx socket to macvlan device - necessary for VRF support,
+        * otherwise the kernel will select the vrf device
+        */
+       if (r->vr->ifp->vrf_id != VRF_DEFAULT) {
+               frr_with_privs (&vrrp_privs) {
+                       ret = setsockopt(r->sock_tx, SOL_SOCKET,
+                                        SO_BINDTODEVICE, r->mvl_ifp->name,
+                                        strlen(r->mvl_ifp->name));
+               }
+
+               if (ret < 0) {
+                       zlog_warn(
+                               VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
+                               "Failed to bind Tx socket to macvlan device '%s'",
+                               r->vr->vrid, family2str(r->family),
+                               r->mvl_ifp->name);
+                       failed = true;
+                       goto done;
+               }
+       }
        /* Configure sockets */
        if (r->family == AF_INET) {
                /* Set Tx socket to always Tx with TTL set to 255 */
@@ -1410,7 +1448,7 @@ static void vrrp_change_state_initialize(struct vrrp_router *r)
        r->ndisc_pending = false;
 
        /* Disable ND Router Advertisements */
-       if (r->family == AF_INET6)
+       if (r->family == AF_INET6 && r->mvl_ifp)
                vrrp_zebra_radv_set(r, false);
 }
 
@@ -1618,7 +1656,8 @@ static int vrrp_shutdown(struct vrrp_router *r)
        THREAD_OFF(r->t_write);
 
        /* Protodown macvlan */
-       vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
+       if (r->mvl_ifp)
+               vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
 
        /* Throw away our source address */
        memset(&r->src, 0x00, sizeof(r->src));
@@ -1722,7 +1761,7 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp)
        struct interface *p;
        struct vrrp_vrouter *vr;
 
-       p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT);
+       p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id);
 
        if (!p)
                return NULL;
@@ -1999,11 +2038,13 @@ int vrrp_autoconfig(void)
        if (!vrrp_autoconfig_is_on)
                return 0;
 
-       struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+       struct vrf *vrf;
        struct interface *ifp;
 
-       FOR_ALL_INTERFACES (vrf, ifp)
-               vrrp_autoconfig_if_add(ifp);
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+               FOR_ALL_INTERFACES (vrf, ifp)
+                       vrrp_autoconfig_if_add(ifp);
+       }
 
        return 0;
 }
@@ -2258,71 +2299,6 @@ void vrrp_if_address_del(struct interface *ifp)
 
 /* Other ------------------------------------------------------------------- */
 
-int vrrp_config_write_interface(struct vty *vty)
-{
-       struct list *vrs = hash_to_list(vrrp_vrouters_hash);
-       struct listnode *ln, *ipln;
-       struct vrrp_vrouter *vr;
-       int writes = 0;
-
-       for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
-               vty_frame(vty, "interface %s\n", vr->ifp->name);
-               ++writes;
-
-               vty_out(vty, " vrrp %" PRIu8 "%s\n", vr->vrid,
-                       vr->version == 2 ? " version 2" : "");
-               ++writes;
-
-               if (vr->shutdown != vd.shutdown && ++writes)
-                       vty_out(vty, " %svrrp %" PRIu8 " shutdown\n",
-                               vr->shutdown ? "" : "no ", vr->vrid);
-
-               if (vr->preempt_mode != vd.preempt_mode && ++writes)
-                       vty_out(vty, " %svrrp %" PRIu8 " preempt\n",
-                               vr->preempt_mode ? "" : "no ", vr->vrid);
-
-               if (vr->accept_mode != vd.accept_mode && ++writes)
-                       vty_out(vty, " %svrrp %" PRIu8 " accept\n",
-                               vr->accept_mode ? "" : "no ", vr->vrid);
-
-               if (vr->advertisement_interval != vd.advertisement_interval
-                   && ++writes)
-                       vty_out(vty,
-                               " vrrp %" PRIu8
-                               " advertisement-interval %d\n",
-                               vr->vrid, vr->advertisement_interval * CS2MS);
-
-               if (vr->priority != vd.priority && ++writes)
-                       vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n",
-                               vr->vrid, vr->priority);
-
-               struct ipaddr *ip;
-
-               for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) {
-                       char ipbuf[INET6_ADDRSTRLEN];
-
-                       ipaddr2str(ip, ipbuf, sizeof(ipbuf));
-                       vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid,
-                               ipbuf);
-                       ++writes;
-               }
-
-               for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) {
-                       char ipbuf[INET6_ADDRSTRLEN];
-
-                       ipaddr2str(ip, ipbuf, sizeof(ipbuf));
-                       vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid,
-                               ipbuf);
-                       ++writes;
-               }
-               vty_endframe(vty, "!\n");
-       }
-
-       list_delete(&vrs);
-
-       return writes;
-}
-
 int vrrp_config_write_global(struct vty *vty)
 {
        unsigned int writes = 0;
@@ -2331,6 +2307,7 @@ int vrrp_config_write_global(struct vty *vty)
                vty_out(vty, "vrrp autoconfigure%s\n",
                        vrrp_autoconfig_version == 2 ? " version 2" : "");
 
+       /* 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);
 
@@ -2380,10 +2357,13 @@ static bool vrrp_hash_cmp(const void *arg1, const void *arg2)
 void vrrp_init(void)
 {
        /* Set default defaults */
-       vd.priority = VRRP_DEFAULT_PRIORITY;
-       vd.advertisement_interval = VRRP_DEFAULT_ADVINT;
-       vd.preempt_mode = VRRP_DEFAULT_PREEMPT;
-       vd.accept_mode = VRRP_DEFAULT_ACCEPT;
+       vd.version = yang_get_default_uint8("%s/version", VRRP_XPATH_FULL);
+       vd.priority = yang_get_default_uint8("%s/priority", VRRP_XPATH_FULL);
+       vd.advertisement_interval = yang_get_default_uint16(
+               "%s/advertisement-interval", VRRP_XPATH_FULL);
+       vd.preempt_mode = yang_get_default_bool("%s/preempt", VRRP_XPATH_FULL);
+       vd.accept_mode =
+               yang_get_default_bool("%s/accept-mode", VRRP_XPATH_FULL);
        vd.shutdown = VRRP_DEFAULT_SHUTDOWN;
 
        vrrp_autoconfig_version = 3;
index 79283bbb109d348cd5834297d3a368efc97ca182..502d7b82b6137f8dead0a4f7606635a7e9d9dd44 100644 (file)
@@ -28,6 +28,7 @@
 #include "lib/hook.h"
 #include "lib/if.h"
 #include "lib/linklist.h"
+#include "lib/northbound.h"
 #include "lib/privs.h"
 #include "lib/stream.h"
 #include "lib/thread.h"
@@ -46,6 +47,8 @@
 #define VRRP_LOGPFX_FAM "[%s] "
 
 /* Default defaults */
+#define VRRP_XPATH_FULL "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group"
+#define VRRP_XPATH "./frr-vrrpd:vrrp/vrrp-group"
 #define VRRP_DEFAULT_PRIORITY 100
 #define VRRP_DEFAULT_ADVINT 100
 #define VRRP_DEFAULT_PREEMPT true
 
 DECLARE_MGROUP(VRRPD)
 
+/* Northbound */
+extern const struct frr_yang_module_info frr_vrrpd_info;
+
 /* Configured defaults */
 struct vrrp_defaults {
+       uint8_t version;
        uint8_t priority;
        uint16_t advertisement_interval;
        bool preempt_mode;
@@ -340,7 +347,7 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
 /*
  * Add an IPvX address to a VRRP Virtual Router.
  *
- * r
+ * vr
  *    Virtual Router to add IPvx address to
  *
  * ip
@@ -354,7 +361,7 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
  *    -1 on error
  *     0 otherwise
  */
-int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip);
+int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip);
 
 /*
  * Add an IPv4 address to a VRRP Virtual Router.
@@ -397,7 +404,7 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
 /*
  * Remove an IP address from a VRRP Virtual Router.
  *
- * r
+ * vr
  *    Virtual Router to remove IP address from
  *
  * ip
@@ -413,7 +420,7 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
  *    -1 on error
  *     0 otherwise
  */
-int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip);
+int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip);
 
 /*
  * Remove an IPv4 address from a VRRP Virtual Router.
@@ -542,17 +549,6 @@ void vrrp_if_address_del(struct interface *ifp);
 
 /* Other ------------------------------------------------------------------- */
 
-/*
- * Write interface block-level configuration to vty.
- *
- * vty
- *    vty to write config to
- *
- * Returns:
- *    # of lines written
- */
-int vrrp_config_write_interface(struct vty *vty);
-
 /*
  * Write global level configuration to vty.
  *
@@ -567,6 +563,6 @@ int vrrp_config_write_global(struct vty *vty);
 /*
  * Find VRRP Virtual Router by Virtual Router ID
  */
-struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid);
+struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid);
 
 #endif /* __VRRP_H__ */
index fface1718f6ad9f492b7c391e8bd873b0446e98f..95b3cfad8f1e8b59538707857401277d030023ea 100644 (file)
@@ -33,6 +33,7 @@
 #include "lib/sigevent.h"
 #include "lib/thread.h"
 #include "lib/vrf.h"
+#include "lib/vty.h"
 
 #include "vrrp.h"
 #include "vrrp_debug.h"
@@ -64,10 +65,14 @@ struct option longopts[] = { {0} };
 /* Master of threads. */
 struct thread_master *master;
 
+static struct frr_daemon_info vrrpd_di;
+
 /* SIGHUP handler. */
 static void sighup(void)
 {
        zlog_info("SIGHUP received");
+
+       vty_read_config(NULL, vrrpd_di.config_file, config_default);
 }
 
 /* SIGINT / SIGTERM handler. */
@@ -107,6 +112,7 @@ struct quagga_signal_t vrrp_signals[] = {
 
 static const struct frr_yang_module_info *const vrrp_yang_modules[] = {
        &frr_interface_info,
+       &frr_vrrpd_info,
 };
 
 #define VRRP_VTY_PORT 2619
diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c
new file mode 100644 (file)
index 0000000..feaea6c
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * VRRP northbound bindings.
+ * Copyright (C) 2019  Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * 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 <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "northbound.h"
+#include "libfrr.h"
+#include "vrrp.h"
+#include "vrrp_vty.h"
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
+ */
+static int lib_interface_vrrp_vrrp_group_create(enum nb_event event,
+                                               const struct lyd_node *dnode,
+                                               union nb_resource *resource)
+{
+       struct interface *ifp;
+       uint8_t vrid;
+       uint8_t version = 3;
+       struct vrrp_vrouter *vr;
+
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(dnode, NULL, true);
+       vrid = yang_dnode_get_uint8(dnode, "./virtual-router-id");
+       version = yang_dnode_get_enum(dnode, "./version");
+       vr = vrrp_vrouter_create(ifp, vrid, version);
+       nb_running_set_entry(dnode, vr);
+
+       return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_destroy(enum nb_event event,
+                                                const struct lyd_node *dnode)
+{
+       struct vrrp_vrouter *vr;
+
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       vr = nb_running_unset_entry(dnode);
+       vrrp_vrouter_destroy(vr);
+
+       return NB_OK;
+}
+
+static const void *
+lib_interface_vrrp_vrrp_group_get_next(const void *parent_list_entry,
+                                      const void *list_entry)
+{
+       struct list *l = hash_to_list(vrrp_vrouters_hash);
+       struct listnode *ln;
+       const struct vrrp_vrouter *curr;
+       const struct interface *ifp = parent_list_entry;
+
+       /*
+        * If list_entry is null, we return the first vrrp instance with a
+        * matching interface
+        */
+       bool nextone = list_entry ? false : true;
+
+       for (ALL_LIST_ELEMENTS_RO(l, ln, curr)) {
+               if (curr == list_entry) {
+                       nextone = true;
+                       continue;
+               }
+
+               if (nextone && curr->ifp == ifp)
+                       goto done;
+       }
+
+       curr = NULL;
+
+done:
+       list_delete(&l);
+       return curr;
+}
+
+static int lib_interface_vrrp_vrrp_group_get_keys(const void *list_entry,
+                                                 struct yang_list_keys *keys)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       keys->num = 1;
+       snprintf(keys->key[0], sizeof(keys->key[0]), "%" PRIu32, vr->vrid);
+
+       return NB_OK;
+}
+
+static const void *
+lib_interface_vrrp_vrrp_group_lookup_entry(const void *parent_list_entry,
+                                          const struct yang_list_keys *keys)
+{
+       uint32_t vrid = strtoul(keys->key[0], NULL, 10);
+       const struct interface *ifp = parent_list_entry;
+
+       return vrrp_lookup(ifp, vrid);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version
+ */
+static int
+lib_interface_vrrp_vrrp_group_version_modify(enum nb_event event,
+                                            const struct lyd_node *dnode,
+                                            union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       uint8_t version;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+       vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+       version = yang_dnode_get_enum(dnode, NULL);
+       vr->version = version;
+
+       vrrp_check_start(vr);
+
+       return NB_OK;
+}
+
+/*
+ * Helper function for address list OP_MODIFY callbacks.
+ */
+static void vrrp_yang_add_del_virtual_address(const struct lyd_node *dnode,
+                                             bool add)
+{
+       struct vrrp_vrouter *vr;
+       struct ipaddr ip;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       yang_dnode_get_ip(&ip, dnode, NULL);
+       if (add)
+               vrrp_add_ip(vr, &ip);
+       else
+               vrrp_del_ip(vr, &ip);
+
+       vrrp_check_start(vr);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
+ */
+static int lib_interface_vrrp_vrrp_group_v4_virtual_address_create(
+       enum nb_event event, const struct lyd_node *dnode,
+       union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       vrrp_yang_add_del_virtual_address(dnode, true);
+
+       return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy(
+       enum nb_event event, const struct lyd_node *dnode)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       vrrp_yang_add_del_virtual_address(dnode, false);
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint8(xpath, vr->v4->priority);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem(const char *xpath,
+                                                        const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       struct yang_data *val = NULL;
+
+       if (vr->v4->mvl_ifp)
+               val = yang_data_new_string(xpath, vr->v4->mvl_ifp->name);
+
+       return val;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_source_address_get_elem(const char *xpath,
+                                                        const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+       struct yang_data *val = NULL;
+       struct ipaddr ip;
+
+       memset(&ip, 0x00, sizeof(ip));
+
+       if (memcmp(&vr->v4->src.ipaddr_v4, &ip.ipaddr_v4, sizeof(ip.ipaddr_v4)))
+               val = yang_data_new_ip(xpath, &vr->v4->src);
+
+       return val;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_state_get_elem(const char *xpath,
+                                               const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_enum(xpath, vr->v4->fsm.state);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint16(xpath, vr->v4->master_adver_interval);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem(const char *xpath,
+                                                   const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint16(xpath, vr->v4->skew_time);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v4->stats.trans_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v4->stats.adver_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v4->stats.garp_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v4->stats.adver_rx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
+ */
+static int lib_interface_vrrp_vrrp_group_v6_virtual_address_create(
+       enum nb_event event, const struct lyd_node *dnode,
+       union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       vrrp_yang_add_del_virtual_address(dnode, true);
+
+       return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy(
+       enum nb_event event, const struct lyd_node *dnode)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       vrrp_yang_add_del_virtual_address(dnode, false);
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint8(xpath, vr->v6->priority);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem(const char *xpath,
+                                                        const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+       struct yang_data *val = NULL;
+
+       if (vr->v6->mvl_ifp)
+               val = yang_data_new_string(xpath, vr->v6->mvl_ifp->name);
+
+       return val;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_source_address_get_elem(const char *xpath,
+                                                        const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+       struct yang_data *val = NULL;
+
+       if (ipaddr_isset(&vr->v6->src))
+               val = yang_data_new_ip(xpath, &vr->v6->src);
+
+       return val;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_state_get_elem(const char *xpath,
+                                               const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_enum(xpath, vr->v6->fsm.state);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint16(xpath, vr->v6->master_adver_interval);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem(const char *xpath,
+                                                   const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint16(xpath, vr->v6->skew_time);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v6->stats.trans_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       const struct vrrp_vrouter *vr = list_entry;
+
+       return yang_data_new_uint32(xpath, vr->v6->stats.adver_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       /* TODO: implement me. */
+       return NULL;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem(
+       const char *xpath, const void *list_entry)
+{
+       /* TODO: implement me. */
+       return NULL;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
+static int
+lib_interface_vrrp_vrrp_group_priority_modify(enum nb_event event,
+                                             const struct lyd_node *dnode,
+                                             union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       uint8_t priority;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       priority = yang_dnode_get_uint8(dnode, NULL);
+       vrrp_set_priority(vr, priority);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
+ */
+static int
+lib_interface_vrrp_vrrp_group_preempt_modify(enum nb_event event,
+                                            const struct lyd_node *dnode,
+                                            union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       bool preempt;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       preempt = yang_dnode_get_bool(dnode, NULL);
+       vr->preempt_mode = preempt;
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode
+ */
+static int
+lib_interface_vrrp_vrrp_group_accept_mode_modify(enum nb_event event,
+                                                const struct lyd_node *dnode,
+                                                union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       bool accept;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       accept = yang_dnode_get_bool(dnode, NULL);
+       vr->accept_mode = accept;
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
+static int lib_interface_vrrp_vrrp_group_advertisement_interval_modify(
+       enum nb_event event, const struct lyd_node *dnode,
+       union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       uint16_t advert_int;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       advert_int = yang_dnode_get_uint16(dnode, NULL);
+       vrrp_set_advertisement_interval(vr, advert_int);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
+ */
+static int
+lib_interface_vrrp_vrrp_group_shutdown_modify(enum nb_event event,
+                                             const struct lyd_node *dnode,
+                                             union nb_resource *resource)
+{
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       struct vrrp_vrouter *vr;
+       bool shutdown;
+
+       vr = nb_running_get_entry(dnode, NULL, true);
+       shutdown = yang_dnode_get_bool(dnode, NULL);
+
+       vr->shutdown = shutdown;
+
+       if (shutdown) {
+               vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+               vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+       } else {
+               vrrp_check_start(vr);
+       }
+
+       return NB_OK;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_vrrpd_info = {
+       .name = "frr-vrrpd",
+       .nodes = {
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group",
+                       .cbs = {
+                               .create = lib_interface_vrrp_vrrp_group_create,
+                               .destroy = lib_interface_vrrp_vrrp_group_destroy,
+                               .get_next = lib_interface_vrrp_vrrp_group_get_next,
+                               .get_keys = lib_interface_vrrp_vrrp_group_get_keys,
+                               .lookup_entry = lib_interface_vrrp_vrrp_group_lookup_entry,
+                               .cli_show = cli_show_vrrp,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_version_modify,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_priority_modify,
+                               .cli_show = cli_show_priority,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_preempt_modify,
+                               .cli_show = cli_show_preempt,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_accept_mode_modify,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_advertisement_interval_modify,
+                               .cli_show = cli_show_advertisement_interval,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown",
+                       .cbs = {
+                               .modify = lib_interface_vrrp_vrrp_group_shutdown_modify,
+                               .cli_show = cli_show_shutdown,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address",
+                       .cbs = {
+                               .create = lib_interface_vrrp_vrrp_group_v4_virtual_address_create,
+                               .destroy = lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy,
+                               .cli_show = cli_show_ip,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_source_address_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_state_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address",
+                       .cbs = {
+                               .create = lib_interface_vrrp_vrrp_group_v6_virtual_address_create,
+                               .destroy = lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy,
+                               .cli_show = cli_show_ipv6,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_source_address_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_state_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem,
+                       }
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement",
+                       .cbs = {
+                               .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem,
+                       }
+               },
+               {
+                       .xpath = NULL,
+               },
+       }
+};
index 239b02ee7fef95086e80bb5d3fefbaf1827a2616..892c8dadd44dfa868eee989055ed0be7332f99a5 100644 (file)
 #include "lib/if.h"
 #include "lib/ipaddr.h"
 #include "lib/json.h"
+#include "lib/northbound_cli.h"
 #include "lib/prefix.h"
 #include "lib/termtable.h"
 #include "lib/vty.h"
+#include "lib/vrf.h"
 
 #include "vrrp.h"
 #include "vrrp_debug.h"
 #define VRRP_VRID_STR "Virtual Router ID\n"
 #define VRRP_PRIORITY_STR "Virtual Router Priority\n"
 #define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n"
-#define VRRP_IP_STR "Virtual Router IPv4 address\n"
+#define VRRP_IP_STR "Virtual Router IP address\n"
 #define VRRP_VERSION_STR "VRRP protocol version\n"
 
-#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr)                                \
-       do {                                                                   \
-               _vr = vrrp_lookup(_ifp, _vrid);                                \
-               if (!_vr) {                                                    \
-                       vty_out(_vty,                                          \
-                               "%% Please configure VRRP instance %u\n",      \
-                               (unsigned int)_vrid);                          \
-                       return CMD_WARNING_CONFIG_FAILED;                      \
-               }                                                              \
-       } while (0)
+#define VRRP_XPATH_ENTRY VRRP_XPATH "[virtual-router-id='%ld']"
 
 /* clang-format off */
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
+ */
 DEFPY(vrrp_vrid,
       vrrp_vrid_cmd,
       "[no] vrrp (1-255)$vrid [version (2-3)]",
@@ -65,27 +61,34 @@ DEFPY(vrrp_vrid,
       VRRP_VERSION_STR
       VRRP_VERSION_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       char valbuf[20];
 
-       struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
+       snprintf(valbuf, sizeof(valbuf), "%ld", version ? version : vd.version);
 
-       if (version == 0)
-               version = 3;
+       if (no)
+               nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+       else {
+               nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+               nb_cli_enqueue_change(vty, "./version", NB_OP_MODIFY, valbuf);
+       }
 
-       if (no && vr)
-               vrrp_vrouter_destroy(vr);
-       else if (no && !vr)
-               vty_out(vty, "%% VRRP instance %ld does not exist on %s\n",
-                       vrid, ifp->name);
-       else if (!vr)
-               vrrp_vrouter_create(ifp, vrid, version);
-       else if (vr)
-               vty_out(vty, "%% VRRP instance %ld already exists on %s\n",
-                       vrid, ifp->name);
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       return CMD_SUCCESS;
+void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "./virtual-router-id");
+       const char *ver = yang_dnode_get_string(dnode, "./version");
+
+       vty_out(vty, " vrrp %s", vrid);
+       if (show_defaults || !yang_dnode_is_default(dnode, "./version"))
+               vty_out(vty, " version %s", ver);
+       vty_out(vty, "\n");
 }
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
+ */
 DEFPY(vrrp_shutdown,
       vrrp_shutdown_cmd,
       "[no] vrrp (1-255)$vrid shutdown",
@@ -94,74 +97,114 @@ DEFPY(vrrp_shutdown,
       VRRP_VRID_STR
       "Force VRRP router into administrative shutdown\n")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       nb_cli_enqueue_change(vty, "./shutdown", NB_OP_MODIFY,
+                             no ? "false" : "true");
 
-       struct vrrp_vrouter *vr;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
-       if (!no) {
-               if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE)
-                       vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
-               if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE)
-                       vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
-               vr->shutdown = true;
-       } else {
-               vr->shutdown = false;
-               vrrp_check_start(vr);
-       }
+void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const bool shut = yang_dnode_get_bool(dnode, NULL);
 
-       return CMD_SUCCESS;
+       vty_out(vty, " %svrrp %s shutdown\n", shut ? "" : "no ", vrid);
 }
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
 DEFPY(vrrp_priority,
       vrrp_priority_cmd,
-      "[no] vrrp (1-255)$vrid priority (1-254)",
-      NO_STR
+      "vrrp (1-255)$vrid priority (1-254)",
       VRRP_STR
       VRRP_VRID_STR
       VRRP_PRIORITY_STR
       "Priority value")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str);
 
-       struct vrrp_vrouter *vr;
-       uint8_t newprio = no ? vd.priority : priority;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
+DEFPY(no_vrrp_priority,
+      no_vrrp_priority_cmd,
+      "no vrrp (1-255)$vrid priority [(1-254)]",
+      NO_STR
+      VRRP_STR
+      VRRP_VRID_STR
+      VRRP_PRIORITY_STR
+      "Priority value")
+{
+       nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL);
 
-       vrrp_set_priority(vr, newprio);
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       return CMD_SUCCESS;
+void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const char *prio = yang_dnode_get_string(dnode, NULL);
+
+       vty_out(vty, " vrrp %s priority %s\n", vrid, prio);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
 DEFPY(vrrp_advertisement_interval,
       vrrp_advertisement_interval_cmd,
-      "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)",
-      NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
+      "vrrp (1-255)$vrid advertisement-interval (10-40950)",
+      VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
       "Advertisement interval in milliseconds; must be multiple of 10")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       char val[20];
 
-       struct vrrp_vrouter *vr;
-       uint16_t newadvint =
-               no ? vd.advertisement_interval * CS2MS : advertisement_interval;
+       /* all internal computations are in centiseconds */
+       advertisement_interval /= CS2MS;
+       snprintf(val, sizeof(val), "%ld", advertisement_interval);
+       nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+                             val);
 
-       if (newadvint % CS2MS != 0) {
-               vty_out(vty, "%% Value must be a multiple of %u\n",
-                       (unsigned int)CS2MS);
-               return CMD_WARNING_CONFIG_FAILED;
-       }
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       /* all internal computations are in centiseconds */
-       newadvint /= CS2MS;
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
+DEFPY(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
+      "Advertisement interval in milliseconds; must be multiple of 10")
+{
+       nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+                             NULL);
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-       vrrp_set_advertisement_interval(vr, newadvint);
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       return CMD_SUCCESS;
+void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
+                                    bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       uint16_t advint = yang_dnode_get_uint16(dnode, NULL);
+
+       vty_out(vty, " vrrp %s advertisement-interval %u\n", vrid,
+               advint * CS2MS);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
+ */
 DEFPY(vrrp_ip,
       vrrp_ip_cmd,
       "[no] vrrp (1-255)$vrid ip A.B.C.D",
@@ -171,51 +214,25 @@ DEFPY(vrrp_ip,
       "Add IPv4 address\n"
       VRRP_IP_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+       nb_cli_enqueue_change(vty, "./v4/virtual-address", op, ip_str);
 
-       struct vrrp_vrouter *vr;
-       bool deactivated = false;
-       bool activated = false;
-       bool failed = false;
-       int ret = CMD_SUCCESS;
-       int oldstate;
-
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
-       bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE);
-
-       if (no) {
-               oldstate = vr->v4->fsm.state;
-               failed = vrrp_del_ipv4(vr, ip);
-               vrrp_check_start(vr);
-               deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE
-                              && oldstate != VRRP_STATE_INITIALIZE);
-       } else {
-               oldstate = vr->v4->fsm.state;
-               failed = vrrp_add_ipv4(vr, ip);
-               vrrp_check_start(vr);
-               activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE
-                            && oldstate == VRRP_STATE_INITIALIZE);
-       }
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       if (activated)
-               vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid);
-       if (deactivated)
-               vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid);
-       if (failed) {
-               vty_out(vty, "%% Failed to %s virtual IP\n",
-                       no ? "remove" : "add");
-               ret = CMD_WARNING_CONFIG_FAILED;
-               if (will_activate && !activated) {
-                       vty_out(vty,
-                               "%% Failed to activate IPv4 Virtual Router %ld\n",
-                               vrid);
-               }
-       }
+void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid =
+               yang_dnode_get_string(dnode, "../../virtual-router-id");
+       const char *ipv4 = yang_dnode_get_string(dnode, NULL);
 
-       return ret;
+       vty_out(vty, " vrrp %s ip %s\n", vrid, ipv4);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
+ */
 DEFPY(vrrp_ip6,
       vrrp_ip6_cmd,
       "[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
@@ -225,57 +242,24 @@ DEFPY(vrrp_ip6,
       "Add IPv6 address\n"
       VRRP_IP_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
-
-       struct vrrp_vrouter *vr;
-       bool deactivated = false;
-       bool activated = false;
-       bool failed = false;
-       int ret = CMD_SUCCESS;
-       int oldstate;
-
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
-       if (vr->version != 3) {
-               vty_out(vty,
-                       "%% Cannot add IPv6 address to VRRPv2 virtual router\n");
-               return CMD_WARNING_CONFIG_FAILED;
-       }
+       int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+       nb_cli_enqueue_change(vty, "./v6/virtual-address", op, ipv6_str);
 
-       bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE);
-
-       if (no) {
-               oldstate = vr->v6->fsm.state;
-               failed = vrrp_del_ipv6(vr, ipv6);
-               vrrp_check_start(vr);
-               deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE
-                              && oldstate != VRRP_STATE_INITIALIZE);
-       } else {
-               oldstate = vr->v6->fsm.state;
-               failed = vrrp_add_ipv6(vr, ipv6);
-               vrrp_check_start(vr);
-               activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE
-                            && oldstate == VRRP_STATE_INITIALIZE);
-       }
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       if (activated)
-               vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid);
-       if (deactivated)
-               vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid);
-       if (failed) {
-               vty_out(vty, "%% Failed to %s virtual IP\n",
-                       no ? "remove" : "add");
-               ret = CMD_WARNING_CONFIG_FAILED;
-               if (will_activate && !activated) {
-                       vty_out(vty,
-                               "%% Failed to activate IPv6 Virtual Router %ld\n",
-                               vrid);
-               }
-       }
+void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid =
+               yang_dnode_get_string(dnode, "../../virtual-router-id");
+       const char *ipv6 = yang_dnode_get_string(dnode, NULL);
 
-       return ret;
+       vty_out(vty, " vrrp %s ipv6 %s\n", vrid, ipv6);
 }
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
+ */
 DEFPY(vrrp_preempt,
       vrrp_preempt_cmd,
       "[no] vrrp (1-255)$vrid preempt",
@@ -284,17 +268,22 @@ DEFPY(vrrp_preempt,
       VRRP_VRID_STR
       "Preempt mode\n")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
-
-       struct vrrp_vrouter *vr;
+       nb_cli_enqueue_change(vty, "./preempt", NB_OP_MODIFY,
+                             no ? "false" : "true");
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       vr->preempt_mode = !no;
+void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
+                     bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const bool pre = yang_dnode_get_bool(dnode, NULL);
 
-       return CMD_SUCCESS;
+       vty_out(vty, " %svrrp %s preempt\n", pre ? "" : "no ", vrid);
 }
 
+/* XXX: yang conversion */
 DEFPY(vrrp_autoconfigure,
       vrrp_autoconfigure_cmd,
       "[no] vrrp autoconfigure [version (2-3)]",
@@ -314,6 +303,7 @@ DEFPY(vrrp_autoconfigure,
        return CMD_SUCCESS;
 }
 
+/* XXX: yang conversion */
 DEFPY(vrrp_default,
       vrrp_default_cmd,
       "[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
@@ -725,6 +715,35 @@ DEFUN_NOSH (show_debugging_vrrp,
 
 /* clang-format on */
 
+/*
+ * Write per interface VRRP config.
+ */
+static int vrrp_config_write_interface(struct vty *vty)
+{
+       struct vrf *vrf;
+       int write = 0;
+
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+               struct interface *ifp;
+
+               FOR_ALL_INTERFACES (vrf, ifp) {
+                       struct lyd_node *dnode;
+
+                       dnode = yang_dnode_get(
+                               running_config->dnode,
+                               "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+                               ifp->name, vrf->name);
+                       if (dnode == NULL)
+                               continue;
+
+                       write = 1;
+                       nb_cli_show_dnode_cmds(vty, dnode, false);
+               }
+       }
+
+       return write;
+}
+
 static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1};
 static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
 static struct cmd_node vrrp_node = {VRRP_NODE, "", 1};
@@ -746,7 +765,9 @@ void vrrp_vty_init(void)
        install_element(INTERFACE_NODE, &vrrp_vrid_cmd);
        install_element(INTERFACE_NODE, &vrrp_shutdown_cmd);
        install_element(INTERFACE_NODE, &vrrp_priority_cmd);
+       install_element(INTERFACE_NODE, &no_vrrp_priority_cmd);
        install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
+       install_element(INTERFACE_NODE, &no_vrrp_advertisement_interval_cmd);
        install_element(INTERFACE_NODE, &vrrp_ip_cmd);
        install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
        install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
index 377321ec4a383156165bb4a476b5c5d0d54acd09..6c6eef03274a80795c39c60a6bead3704424f504 100644 (file)
 #ifndef __VRRP_VTY_H__
 #define __VRRP_VTY_H__
 
+#include "lib/northbound.h"
+
 void vrrp_vty_init(void);
 
+/* Northbound callbacks */
+void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults);
+void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults);
+void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
+                                    bool show_defaults);
+void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
+                     bool show_defaults);
+
 #endif /* __VRRP_VTY_H__ */
index a6c575f8da4426102dc1195af3fad85661f35c4f..000672a0803f8002b1b2b2962d21b32e4e38c17b 100644 (file)
@@ -175,7 +175,7 @@ void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable)
               "Requesting Zebra to turn router advertisements %s for %s",
               r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name);
 
-       zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp,
+       zclient_send_interface_radv_req(zclient, r->mvl_ifp->vrf_id, r->mvl_ifp,
                                        enable, VRRP_RADV_INT);
 }
 
@@ -185,7 +185,7 @@ int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down)
               VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name,
               down ? "on" : "off");
 
-       return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp,
+       return zclient_send_interface_protodown(zclient, ifp->vrf_id, ifp,
                                                down);
 }
 
index d0b0c701a778c2ed488a0160fc96046e496c01fb..13413888bf5e6be7b8c2b154895c06b3ee14cee5 100755 (executable)
@@ -96,14 +96,14 @@ sub scan_file {
         elsif ($file =~ /lib\/if\.c$/) {
             $protocol = "VTYSH_INTERFACE";
         }
-        elsif ($file =~ /lib\/filter\.c$/) {
+        elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) {
             $protocol = "VTYSH_ALL";
         }
        elsif ($file =~ /lib\/agentx\.c$/) {
            $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
        }
        elsif ($file =~ /lib\/nexthop_group\.c$/) {
-           $protocol = "VTYSH_PBRD | VTYSH_SHARPD";
+           $protocol = "VTYSH_NH_GROUP";
        }
         elsif ($file =~ /lib\/plist\.c$/) {
             if ($defun_array[1] =~ m/ipv6/) {
index 5c4e8a313b04de5cd840a24d40d60ce586172daf..b7d35caa3975889b9f912ffab5df3d6ce063ef52 100644 (file)
@@ -2163,7 +2163,8 @@ DEFUNSH(VTYSH_ZEBRA, vtysh_pseudowire, vtysh_pseudowire_cmd,
        return CMD_SUCCESS;
 }
 
-DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd,
+DEFUNSH(VTYSH_NH_GROUP,
+       vtysh_nexthop_group, vtysh_nexthop_group_cmd,
        "nexthop-group NHGNAME",
        "Nexthop Group configuration\n"
        "Name of the Nexthop Group\n")
@@ -2172,7 +2173,7 @@ DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd,
        return CMD_SUCCESS;
 }
 
-DEFSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_no_nexthop_group_cmd,
+DEFSH(VTYSH_NH_GROUP, vtysh_no_nexthop_group_cmd,
       "no nexthop-group NHGNAME",
       NO_STR
       "Nexthop Group Configuration\n"
@@ -2209,13 +2210,15 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit",
        return vtysh_exit_vrf(self, vty, argc, argv);
 }
 
-DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
+DEFUNSH(VTYSH_NH_GROUP,
+       vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
        "exit", "Exit current mode and down to previous mode\n")
 {
        return vtysh_exit(vty);
 }
 
-DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd,
+DEFUNSH(VTYSH_NH_GROUP,
+       vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd,
        "quit", "Exit current mode and down to previous mode\n")
 {
        return vtysh_exit_nexthop_group(self, vty, argc, argv);
index b16761b41a226767ff4d7b23d17b6d6afd4d078c..d0edbb2710a6fa5b3ad9af12314cc38747178b5e 100644 (file)
@@ -56,6 +56,8 @@ DECLARE_MGROUP(MVTYSH)
 #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
 #define VTYSH_KEYS        VTYSH_RIPD|VTYSH_EIGRPD
+/* Daemons who can process nexthop-group configs */
+#define VTYSH_NH_GROUP    VTYSH_PBRD|VTYSH_SHARPD
 
 enum vtysh_write_integrated {
        WRITE_INTEGRATED_UNSPECIFIED,
index e56c6fbf4ef2d5350caf83beb92d6a914de86d27..27f4b0834d00ab4c73e79ab1cc99e0980efd5f33 100644 (file)
@@ -262,6 +262,8 @@ void vtysh_config_parse_line(void *arg, const char *line)
                                   || !strncmp(line, " no vrrp",
                                               strlen(" no vrrp"))) {
                                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 (config->index == RMAP_NODE
                                   || config->index == INTERFACE_NODE
                                   || config->index == VTY_NODE
index c7e1d252c7271a2345888c2024303dc4cfedd21d..0ba1b9d9c8c70b23461ee5c4a36ffd52ea84a0e1 100644 (file)
@@ -45,7 +45,6 @@
 #include "command.h"
 #include "memory.h"
 #include "linklist.h"
-#include "memory_vty.h"
 #include "libfrr.h"
 #include "ferr.h"
 #include "lib_errors.h"
index 30f606c202dfd94db51c43a6a2c16bae7c271d15..36af57cf8221034c1e5c6bab864b020bdd5c4801 100644 (file)
@@ -5,7 +5,7 @@
 if WATCHFRR
 sbin_PROGRAMS += watchfrr/watchfrr
 vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c
-man8 += $(MANBUILD)/watchfrr.8
+man8 += $(MANBUILD)/frr-watchfrr.8
 endif
 
 noinst_HEADERS += \
index 4e8502107a576188e8f96c01610f06f816e377ac..ed9616963dad19d96851a1170e0aac29826d6e03 100644 (file)
@@ -25,7 +25,6 @@
 #include <sigevent.h>
 #include <lib/version.h>
 #include "command.h"
-#include "memory_vty.h"
 #include "libfrr.h"
 #include "lib_errors.h"
 
index faa880eff4479648f74757a820dce46735f32466..faab1e55b25120352728f3c4742ac322c965545c 100644 (file)
@@ -27,6 +27,10 @@ module frr-isisd {
   description
     "This module defines a model for managing FRR isisd daemon.";
 
+  revision 2019-12-17 {
+    description
+      "Changed default area is-type to level-1-2";
+  }
   revision 2019-09-09 {
     description
       "Changed interface references to use
@@ -748,7 +752,7 @@ module frr-isisd {
 
       leaf is-type {
         type level;
-        default "level-1";
+        default "level-1-2";
         description
           "Level of the IS-IS routing instance (OSI only).";
       }
diff --git a/yang/frr-vrrpd.yang b/yang/frr-vrrpd.yang
new file mode 100644 (file)
index 0000000..3d3a413
--- /dev/null
@@ -0,0 +1,260 @@
+module frr-vrrpd {
+  yang-version 1.1;
+  namespace "http://frrouting.org/yang/vrrpd";
+  prefix frr-vrrpd;
+
+  import ietf-inet-types {
+    prefix inet;
+  }
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  import frr-interface {
+    prefix frr-interface;
+  }
+
+  organization
+    "Free Range Routing";
+  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 vrrpd daemon.";
+
+  revision 2019-09-09 {
+    description
+      "Initial revision.";
+  }
+
+  grouping ip-vrrp-config {
+    description
+      "Configuration data for VRRP on IP interfaces";
+    leaf virtual-router-id {
+      type uint8 {
+        range "1..255";
+      }
+      description
+        "Set the virtual router id for use by the VRRP group. This
+         usually also determines the virtual MAC address that is
+         generated for the VRRP group";
+    }
+
+    leaf version {
+      type enumeration {
+        enum "2" {
+          value 2;
+          description
+            "VRRP version 2.";
+        }
+        enum "3" {
+          value 3;
+          description
+            "VRRP version 3.";
+        }
+      }
+      default "3";
+    }
+
+    leaf priority {
+      type uint8 {
+        range "1..254";
+      }
+      default "100";
+      description
+        "Specifies the sending VRRP interface's priority
+         for the virtual router. Higher values equal higher
+         priority";
+    }
+
+    leaf preempt {
+      type boolean;
+      default "true";
+      description
+        "When set to true, enables preemption by a higher
+         priority backup router of a lower priority master router";
+    }
+
+    leaf accept-mode {
+      type boolean;
+      default "true";
+      description
+        "Configure whether packets destined for
+         virtual addresses are accepted even when the virtual
+         address is not owned by the router interface";
+    }
+
+    leaf advertisement-interval {
+      type uint16 {
+        range "1..4095";
+      }
+      units "centiseconds";
+      default "100";
+      description
+        "Sets the interval between successive VRRP
+         advertisements -- RFC 5798 defines this as a 12-bit
+         value expressed as 0.1 seconds, with default 100, i.e.,
+         1 second. Several implementation express this in units of
+         seconds";
+    }
+
+    leaf shutdown {
+      type boolean;
+      default "false";
+      description
+        "Administrative shutdown for this VRRP group.";
+    }
+  }
+
+  grouping ip-vrrp-state {
+    description
+      "Grouping for operational state data for a virtual router";
+    leaf current-priority {
+      type uint8;
+      config false;
+      description
+        "Operational value of the priority for the
+         interface in the VRRP group.";
+    }
+
+    leaf vrrp-interface {
+      type frr-interface:interface-ref;
+      config false;
+      description
+        "The interface used to transmit VRRP traffic.";
+    }
+
+    leaf source-address {
+      type inet:ip-address;
+      config false;
+      description
+        "The source IP address used for VRRP advertisements.";
+    }
+
+    leaf state {
+      type enumeration {
+        enum "Initialize" {
+          description
+            "State when virtual router is waiting for a Startup event.";
+        }
+        enum "Master" {
+          description
+            "State when virtual router is functioning as the forwarding router
+             for the virtual addresses.";
+        }
+        enum "Backup" {
+          description
+            "State when virtual router is monitoring the availability and state
+             of the Master router.";
+        }
+      }
+      config false;
+    }
+
+    leaf master-advertisement-interval {
+      type uint16 {
+        range "0..4095";
+      }
+      units "centiseconds";
+      config false;
+      description
+        "Advertisement interval contained in advertisements received from the Master.";
+    }
+
+    leaf skew-time {
+      type uint16;
+      units "centiseconds";
+      config false;
+      description
+        "Time to skew Master_Down_Interval.";
+    }
+
+    container counter {
+      config false;
+      leaf state-transition {
+        type yang:zero-based-counter32;
+        description
+          "Number of state transitions the virtual router has experienced.";
+      }
+
+      container tx {
+        leaf advertisement {
+          type yang:zero-based-counter32;
+          description
+            "Number of sent VRRP advertisements.";
+        }
+      }
+
+      container rx {
+        leaf advertisement {
+          type yang:zero-based-counter32;
+          description
+            "Number of received VRRP advertisements.";
+        }
+      }
+    }
+  }
+
+  grouping ip-vrrp-top {
+    description
+      "Top-level grouping for Virtual Router Redundancy Protocol";
+    container vrrp {
+      description
+        "Enclosing container for VRRP groups handled by this
+         IP interface";
+      reference
+        "RFC 5798 - Virtual Router Redundancy Protocol
+         (VRRP) Version 3 for IPv4 and IPv6";
+      list vrrp-group {
+        key "virtual-router-id";
+        description
+          "List of VRRP groups, keyed by virtual router id";
+        uses ip-vrrp-config;
+
+        container v4 {
+          leaf-list virtual-address {
+            type inet:ipv4-address;
+            description
+              "Configure one or more IPv4 virtual addresses for the
+               VRRP group";
+          }
+
+          uses ip-vrrp-state {
+            augment "./counter/tx" {
+              leaf gratuitous-arp {
+                type yang:zero-based-counter32;
+                description
+                  "Number of sent gratuitous ARP requests.";
+              }
+            }
+          }
+        }
+
+        container v6 {
+          when "../version = 3";
+          leaf-list virtual-address {
+            type inet:ipv6-address;
+            description
+              "Configure one or more IPv6 virtual addresses for the
+               VRRP group";
+          }
+
+          uses ip-vrrp-state {
+            augment "./counter/tx" {
+              leaf neighbor-advertisement {
+                type yang:zero-based-counter32;
+                description
+                  "Number of sent unsolicited Neighbor Advertisements.";
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  augment "/frr-interface:lib/frr-interface:interface" {
+    uses ip-vrrp-top;
+  }
+}
index b69239560ffe5bf1b123a6d752330233741b2fcc..cfaf1a6401799c5a017bca510cdafb40698845fe 100644 (file)
@@ -44,3 +44,7 @@ endif
 if ISISD
 dist_yangmodels_DATA += yang/frr-isisd.yang
 endif
+
+if VRRPD
+dist_yangmodels_DATA += yang/frr-vrrpd.yang
+endif
index 8e5fb0ea103b8e4474a242753204b8665b6e2406..681dfb87537236643d7f6d2df0df0072b608d9d1 100644 (file)
@@ -39,6 +39,7 @@ unsigned long zebra_debug_vxlan;
 unsigned long zebra_debug_pw;
 unsigned long zebra_debug_dplane;
 unsigned long zebra_debug_mlag;
+unsigned long zebra_debug_nexthop;
 
 DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty));
 
@@ -103,6 +104,10 @@ DEFUN_NOSH (show_debugging_zebra,
                vty_out(vty, "  Zebra dataplane debugging is on\n");
        if (IS_ZEBRA_DEBUG_MLAG)
                vty_out(vty, "  Zebra mlag debugging is on\n");
+       if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+               vty_out(vty, "Zebra detailed nexthop debugging is on\n");
+       else if (IS_ZEBRA_DEBUG_NHG)
+               vty_out(vty, "Zebra nexthop debugging is on\n");
 
        hook_call(zebra_debug_show_debugging, vty);
        return CMD_SUCCESS;
@@ -443,6 +448,28 @@ DEFUN (no_debug_zebra_dplane,
        return CMD_SUCCESS;
 }
 
+DEFPY (debug_zebra_nexthop,
+       debug_zebra_nexthop_cmd,
+       "[no$no] debug zebra nexthop [detail$detail]",
+       NO_STR
+       DEBUG_STR
+       "Zebra configuration\n"
+       "Debug zebra nexthop events\n"
+       "Detailed information\n")
+{
+       if (no)
+               zebra_debug_nexthop = 0;
+       else {
+               SET_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG);
+
+               if (detail)
+                       SET_FLAG(zebra_debug_nexthop,
+                                ZEBRA_DEBUG_NHG_DETAILED);
+       }
+
+       return CMD_SUCCESS;
+}
+
 /* Debug node. */
 struct cmd_node debug_node = {DEBUG_NODE, "", /* Debug node has no interface. */
                              1};
@@ -546,6 +573,7 @@ void zebra_debug_init(void)
        zebra_debug_dplane = 0;
        zebra_debug_mlag = 0;
        zebra_debug_nht = 0;
+       zebra_debug_nexthop = 0;
 
        install_node(&debug_node, config_write_debug);
 
@@ -563,6 +591,7 @@ void zebra_debug_init(void)
        install_element(ENABLE_NODE, &debug_zebra_fpm_cmd);
        install_element(ENABLE_NODE, &debug_zebra_dplane_cmd);
        install_element(ENABLE_NODE, &debug_zebra_mlag_cmd);
+       install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd);
        install_element(ENABLE_NODE, &no_debug_zebra_events_cmd);
        install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd);
        install_element(ENABLE_NODE, &no_debug_zebra_mpls_cmd);
@@ -585,6 +614,7 @@ void zebra_debug_init(void)
        install_element(CONFIG_NODE, &debug_zebra_rib_cmd);
        install_element(CONFIG_NODE, &debug_zebra_fpm_cmd);
        install_element(CONFIG_NODE, &debug_zebra_dplane_cmd);
+       install_element(CONFIG_NODE, &debug_zebra_nexthop_cmd);
        install_element(CONFIG_NODE, &no_debug_zebra_events_cmd);
        install_element(CONFIG_NODE, &no_debug_zebra_nht_cmd);
        install_element(CONFIG_NODE, &no_debug_zebra_mpls_cmd);
index 176226f7ae402b5ff0d10645ddc1a6d5b1dd8688..e513f8865d4faf72462770bf70f132a96da112c7 100644 (file)
@@ -59,6 +59,9 @@ extern "C" {
 
 #define ZEBRA_DEBUG_MLAG    0x01
 
+#define ZEBRA_DEBUG_NHG             0x01
+#define ZEBRA_DEBUG_NHG_DETAILED    0x02
+
 /* Debug related macro. */
 #define IS_ZEBRA_DEBUG_EVENT  (zebra_debug_event & ZEBRA_DEBUG_EVENT)
 
@@ -92,6 +95,11 @@ extern "C" {
 
 #define IS_ZEBRA_DEBUG_MLAG (zebra_debug_mlag & ZEBRA_DEBUG_MLAG)
 
+#define IS_ZEBRA_DEBUG_NHG (zebra_debug_nexthop & ZEBRA_DEBUG_NHG)
+
+#define IS_ZEBRA_DEBUG_NHG_DETAIL \
+       (zebra_debug_nexthop & ZEBRA_DEBUG_NHG_DETAILED)
+
 extern unsigned long zebra_debug_event;
 extern unsigned long zebra_debug_packet;
 extern unsigned long zebra_debug_kernel;
@@ -103,6 +111,7 @@ extern unsigned long zebra_debug_vxlan;
 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 void zebra_debug_init(void);
 
index c09007bcb1d26bf5fdf4fdc355bc002b61276997..4731d1ed154969bfd8fc9992b756cc4c0aceb4d2 100644 (file)
@@ -366,7 +366,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb,
        }
 }
 
-static int get_iflink_speed(struct interface *interface, int *error)
+static uint32_t get_iflink_speed(struct interface *interface, int *error)
 {
        struct ifreq ifdata;
        struct ethtool_cmd ecmd;
@@ -419,7 +419,7 @@ static int get_iflink_speed(struct interface *interface, int *error)
 
        close(sd);
 
-       return (ecmd.speed_hi << 16) | ecmd.speed;
+       return ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed;
 }
 
 uint32_t kernel_get_speed(struct interface *ifp, int *error)
@@ -1467,7 +1467,7 @@ int netlink_protodown(struct interface *ifp, bool down)
 
        req.ifa.ifi_index = ifp->ifindex;
 
-       addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4);
+       addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down));
        addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4);
 
        return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
index 20b05dfb32b9ace69e79d02b31f3021fd5483f70..bcb833b8d8f019b1ed7532533345b9a5bb2c4e42 100644 (file)
@@ -1977,6 +1977,8 @@ DEFUN (shutdown_if,
        struct zebra_if *if_data;
 
        if (ifp->ifindex != IFINDEX_INTERNAL) {
+               /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+               rtadv_stop_ra(ifp);
                ret = if_unset_flags(ifp, IFF_UP);
                if (ret < 0) {
                        vty_out(vty, "Can't shutdown interface\n");
index 23f1a3bf86ca61a5cbca8a26735dade8f6f7a1ff..90d3aeb482d9c4fe8b912380acf911b916c7ed10 100644 (file)
@@ -290,6 +290,18 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
                return netlink_neigh_change(h, ns_id);
        case RTM_DELNEIGH:
                return netlink_neigh_change(h, ns_id);
+       case RTM_GETNEIGH:
+               /*
+                * Kernel in some situations when it expects
+                * user space to resolve arp entries, we will
+                * receive this notification.  As we don't
+                * need this notification and as that
+                * we don't want to spam the log file with
+                * below messages, just ignore.
+                */
+               if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("Received RTM_GETNEIGH, ignoring");
+               break;
        case RTM_NEWRULE:
                return netlink_rule_change(h, ns_id, startup);
        case RTM_DELRULE:
@@ -580,6 +592,7 @@ struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
        struct rtattr *nest = NLMSG_TAIL(n);
 
        addattr_l(n, maxlen, type, NULL, 0);
+       nest->rta_type |= NLA_F_NESTED;
        return nest;
 }
 
@@ -594,6 +607,7 @@ struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type)
        struct rtattr *nest = RTA_TAIL(rta);
 
        rta_addattr_l(rta, maxlen, type, NULL, 0);
+       nest->rta_type |= NLA_F_NESTED;
        return nest;
 }
 
@@ -1086,7 +1100,7 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n)
    netlink_socket (). */
 void kernel_init(struct zebra_ns *zns)
 {
-       unsigned long groups;
+       uint32_t groups;
 #if defined SOL_NETLINK
        int one, ret;
 #endif
@@ -1107,9 +1121,9 @@ void kernel_init(struct zebra_ns *zns)
                RTMGRP_IPV6_IFADDR             |
                RTMGRP_IPV4_MROUTE             |
                RTMGRP_NEIGH                   |
-               (1 << (RTNLGRP_IPV4_RULE - 1)) |
-               (1 << (RTNLGRP_IPV6_RULE - 1)) |
-               (1 << (RTNLGRP_NEXTHOP - 1));
+               ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) |
+               ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) |
+               ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1));
 
        snprintf(zns->netlink.name, sizeof(zns->netlink.name),
                 "netlink-listen (NS %u)", zns->ns_id);
index 6e58f4b9253b05d352ba8d9a710bba8d08101ebb..caebdc0f08644a83f212f57f9aa2c6b84d108533 100644 (file)
@@ -262,8 +262,12 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
                 * included in the previous one */
                for (node = first_node; node && (node != last_node);
                     node = next) {
+                       struct label_manager_chunk *death;
+
                        next = listnextnode(node);
+                       death = listgetdata(node);
                        list_delete_node(lbl_mgr.lc_list, node);
+                       delete_label_chunk(death);
                }
 
                lmc = create_label_chunk(proto, instance, keep, base, end);
index 731c4e161400e268fa7bf00cf0d0c04e4737d098..75f825e5077e7179f22daee23e84b66e30f162a3 100644 (file)
@@ -27,7 +27,6 @@
 #include "filter.h"
 #include "memory.h"
 #include "zebra_memory.h"
-#include "memory_vty.h"
 #include "prefix.h"
 #include "log.h"
 #include "plist.h"
@@ -145,6 +144,9 @@ static void sigint(void)
        atomic_store_explicit(&zrouter.in_shutdown, true,
                              memory_order_relaxed);
 
+       /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+       rtadv_stop_ra_all();
+
        frr_early_fini();
 
        zebra_dplane_pre_finish();
index 98603c9693d1885c63c96457893b4a33ba529ec8..6aa52bcb61d8f4d8e86804975c790401a2bc3076 100644 (file)
@@ -637,13 +637,15 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
        struct route_entry *newre;
        struct route_entry *same;
        struct prefix p;
+       struct nexthop_group *ng;
        route_map_result_t ret = RMAP_PERMITMATCH;
        afi_t afi;
 
        afi = family2afi(rn->p.family);
        if (rmap_name)
                ret = zebra_import_table_route_map_check(
-                       afi, re->type, re->instance, &rn->p, re->ng->nexthop,
+                       afi, re->type, re->instance, &rn->p,
+                       re->nhe->nhg->nexthop,
                        zvrf->vrf->vrf_id, re->tag, rmap_name);
 
        if (ret != RMAP_PERMITMATCH) {
@@ -678,10 +680,11 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
        newre->table = zvrf->table_id;
        newre->uptime = monotime(NULL);
        newre->instance = re->table;
-       newre->ng = nexthop_group_new();
-       route_entry_copy_nexthops(newre, re->ng->nexthop);
 
-       rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre);
+       ng = nexthop_group_new();
+       copy_nexthops(&ng->nexthop, re->nhe->nhg->nexthop, NULL);
+
+       rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng);
 
        return 0;
 }
@@ -696,8 +699,9 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn,
        prefix_copy(&p, &rn->p);
 
        rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE,
-                  re->table, re->flags, &p, NULL, re->ng->nexthop, re->nhe_id,
-                  zvrf->table_id, re->metric, re->distance, false);
+                  re->table, re->flags, &p, NULL, re->nhe->nhg->nexthop,
+                  re->nhe_id, zvrf->table_id, re->metric, re->distance,
+                  false);
 
        return 0;
 }
index 35aa011c0d5020b19ad6c71b1fab6fcdbaf28f00..927675e3d983ccd50bb01adb6e6ffbdf54d22445 100644 (file)
@@ -35,6 +35,7 @@
 #include "if.h"
 #include "mpls.h"
 #include "srcdest_table.h"
+#include "zebra/zebra_nhg.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -87,10 +88,14 @@ struct route_entry {
        /* Link list. */
        struct re_list_item next;
 
-       /* Nexthop structure (from RIB) */
-       struct nexthop_group *ng;
+       /* Nexthop group, shared/refcounted, based on the nexthop(s)
+        * provided by the owner of the route
+        */
+       struct nhg_hash_entry *nhe;
 
-       /* Nexthop group from FIB (optional) */
+       /* Nexthop group from FIB (optional), reflecting what is actually
+        * installed in the FIB if that differs.
+        */
        struct nexthop_group fib_ng;
 
        /* Nexthop group hash entry ID */
@@ -307,33 +312,9 @@ typedef enum {
        RIB_UPDATE_MAX
 } rib_update_event_t;
 
-extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
-                                                      ifindex_t ifindex,
-                                                      vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_blackhole_add(struct route_entry *re,
-                                 enum blackhole_type bh_type);
-extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re,
-                                                   struct in_addr *ipv4,
-                                                   struct in_addr *src,
-                                                   vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
-                                    struct in_addr *ipv4, struct in_addr *src,
-                                    ifindex_t ifindex, vrf_id_t nh_vrf_id);
-extern void route_entry_nexthop_delete(struct route_entry *re,
-                                      struct nexthop *nexthop);
-extern struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re,
-                                                   struct in6_addr *ipv6,
-                                                   vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
-                                    struct in6_addr *ipv6, ifindex_t ifindex,
-                                    vrf_id_t nh_vrf_id);
-extern void route_entry_nexthop_add(struct route_entry *re,
-                                   struct nexthop *nexthop);
 extern void route_entry_copy_nexthops(struct route_entry *re,
                                      struct nexthop *nh);
+int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new);
 
 #define route_entry_dump(prefix, src, re) _route_entry_dump(__func__, prefix, src, re)
 extern void _route_entry_dump(const char *func, union prefixconstptr pp,
@@ -368,7 +349,8 @@ extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                   uint32_t mtu, uint8_t distance, route_tag_t tag);
 
 extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
-                            struct prefix_ipv6 *src_p, struct route_entry *re);
+                            struct prefix_ipv6 *src_p, struct route_entry *re,
+                            struct nexthop_group *ng);
 
 extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                       unsigned short instance, int flags, struct prefix *p,
@@ -533,7 +515,7 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re)
        if (re->fib_ng.nexthop)
                return &(re->fib_ng);
        else
-               return re->ng;
+               return re->nhe->nhg;
 }
 
 extern void zebra_vty_init(void);
index 569ffbab411f80fd0bcf0ace9680a09e63993d87..b37d4aea70277766a4b2a141136ecf7a2c19b715 100644 (file)
@@ -253,6 +253,36 @@ DEFUN (no_router_id,
        return CMD_SUCCESS;
 }
 
+DEFUN (show_router_id,
+       show_router_id_cmd,
+       "show router-id [vrf NAME]",
+       SHOW_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;
+
+        if (argc > 2)
+                VRF_GET_ID(vrf_id, argv[idx_name]->arg, false);
+
+        zvrf = vrf_info_get(vrf_id);
+
+        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);
+        }
+
+        return CMD_SUCCESS;
+}
 
 static int router_id_cmp(void *a, void *b)
 {
@@ -267,6 +297,7 @@ void router_id_cmd_init(void)
 {
        install_element(CONFIG_NODE, &router_id_cmd);
        install_element(CONFIG_NODE, &no_router_id_cmd);
+       install_element(VIEW_NODE, &show_router_id_cmd);
 }
 
 void router_id_init(struct zebra_vrf *zvrf)
index 5e328b827fc88d7ba613202736297c4f22d0097b..dd6e62ee6c8569a215a1f6904f84b42bd5d6baeb 100644 (file)
@@ -390,7 +390,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb,
 }
 
 static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
-                                               struct route_entry *re,
+                                               struct nexthop_group *ng,
                                                struct rtmsg *rtm,
                                                struct rtnexthop *rtnh,
                                                struct rtattr **tb,
@@ -407,8 +407,6 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
        int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
        vrf_id_t nh_vrf_id = vrf_id;
 
-       re->ng = nexthop_group_new();
-
        for (;;) {
                struct nexthop *nh = NULL;
 
@@ -454,29 +452,32 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
 
                if (gate && rtm->rtm_family == AF_INET) {
                        if (index)
-                               nh = route_entry_nexthop_ipv4_ifindex_add(
-                                       re, gate, prefsrc, index, nh_vrf_id);
+                               nh = nexthop_from_ipv4_ifindex(
+                                       gate, prefsrc, index, nh_vrf_id);
                        else
-                               nh = route_entry_nexthop_ipv4_add(
-                                       re, gate, prefsrc, nh_vrf_id);
+                               nh = nexthop_from_ipv4(gate, prefsrc,
+                                                      nh_vrf_id);
                } else if (gate && rtm->rtm_family == AF_INET6) {
                        if (index)
-                               nh = route_entry_nexthop_ipv6_ifindex_add(
-                                       re, gate, index, nh_vrf_id);
+                               nh = nexthop_from_ipv6_ifindex(
+                                       gate, index, nh_vrf_id);
                        else
-                               nh = route_entry_nexthop_ipv6_add(re, gate,
-                                                                 nh_vrf_id);
+                               nh = nexthop_from_ipv6(gate, nh_vrf_id);
                } else
-                       nh = route_entry_nexthop_ifindex_add(re, index,
-                                                            nh_vrf_id);
+                       nh = nexthop_from_ifindex(index, nh_vrf_id);
 
                if (nh) {
+                       nh->weight = rtnh->rtnh_hops + 1;
+
                        if (num_labels)
                                nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
                                                   num_labels, labels);
 
                        if (rtnh->rtnh_flags & RTNH_F_ONLINK)
                                SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK);
+
+                       /* Add to temporary list */
+                       nexthop_group_add_sorted(ng, nh);
                }
 
                if (rtnh->rtnh_len == 0)
@@ -486,10 +487,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
                rtnh = RTNH_NEXT(rtnh);
        }
 
-       uint8_t nhop_num = nexthop_group_nexthop_num(re->ng);
-
-       if (!nhop_num)
-               nexthop_group_delete(&re->ng);
+       uint8_t nhop_num = nexthop_group_nexthop_num(ng);
 
        return nhop_num;
 }
@@ -737,6 +735,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                } else {
                        /* This is a multipath route */
                        struct route_entry *re;
+                       struct nexthop_group *ng = NULL;
                        struct rtnexthop *rtnh =
                                (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
 
@@ -753,19 +752,30 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                        re->nhe_id = nhe_id;
 
                        if (!nhe_id) {
-                               uint8_t nhop_num =
+                               uint8_t nhop_num;
+
+                               /* Use temporary list of nexthops; parse
+                                * message payload's nexthops.
+                                */
+                               ng = nexthop_group_new();
+                               nhop_num =
                                        parse_multipath_nexthops_unicast(
-                                               ns_id, re, rtm, rtnh, tb,
+                                               ns_id, ng, rtm, rtnh, tb,
                                                prefsrc, vrf_id);
 
                                zserv_nexthop_num_warn(
                                        __func__, (const struct prefix *)&p,
                                        nhop_num);
+
+                               if (nhop_num == 0) {
+                                       nexthop_group_delete(&ng);
+                                       ng = NULL;
+                               }
                        }
 
-                       if (nhe_id || re->ng)
+                       if (nhe_id || ng)
                                rib_add_multipath(afi, SAFI_UNICAST, &p,
-                                                 &src_p, re);
+                                                 &src_p, re, ng);
                        else
                                XFREE(MTYPE_RE, re);
                }
@@ -777,34 +787,10 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                } else {
                        if (!tb[RTA_MULTIPATH]) {
                                struct nexthop nh;
-                               size_t sz = (afi == AFI_IP) ? 4 : 16;
-
-                               memset(&nh, 0, sizeof(nh));
-                               if (bh_type == BLACKHOLE_UNSPEC) {
-                                       if (index && !gate)
-                                               nh.type = NEXTHOP_TYPE_IFINDEX;
-                                       else if (index && gate)
-                                               nh.type =
-                                                       (afi == AFI_IP)
-                                                               ? NEXTHOP_TYPE_IPV4_IFINDEX
-                                                               : NEXTHOP_TYPE_IPV6_IFINDEX;
-                                       else if (!index && gate)
-                                               nh.type =
-                                                       (afi == AFI_IP)
-                                                               ? NEXTHOP_TYPE_IPV4
-                                                               : NEXTHOP_TYPE_IPV6;
-                                       else {
-                                               nh.type =
-                                                       NEXTHOP_TYPE_BLACKHOLE;
-                                               nh.bh_type = BLACKHOLE_UNSPEC;
-                                       }
-                               } else {
-                                       nh.type = NEXTHOP_TYPE_BLACKHOLE;
-                                       nh.bh_type = bh_type;
-                               }
-                               nh.ifindex = index;
-                               if (gate)
-                                       memcpy(&nh.gate, gate, sz);
+
+                               nh = parse_nexthop_unicast(
+                                       ns_id, rtm, tb, bh_type, index, prefsrc,
+                                       gate, afi, vrf_id);
                                rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0,
                                           flags, &p, &src_p, &nh, 0, table,
                                           metric, distance, true);
@@ -1411,6 +1397,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
                                "nexthop via if %u",
                                routedesc, nexthop->ifindex);
        }
+
+       if (nexthop->weight)
+               rtnh->rtnh_hops = nexthop->weight - 1;
 }
 
 static inline void _netlink_mpls_build_singlepath(const char *routedesc,
@@ -1913,7 +1902,7 @@ static void _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size,
        if (count) {
                for (int i = 0; i < count; i++) {
                        grp[i].id = z_grp[i].id;
-                       grp[i].weight = z_grp[i].weight;
+                       grp[i].weight = z_grp[i].weight - 1;
 
                        if (IS_ZEBRA_DEBUG_KERNEL) {
                                if (i == 0)
@@ -2031,6 +2020,9 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx)
 
                        addattr32(&req.n, req_size, NHA_OIF, nh->ifindex);
 
+                       if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK))
+                               req.nhm.nh_flags |= RTNH_F_ONLINK;
+
                        num_labels =
                                build_label_stack(nh->nh_label, out_lse,
                                                  label_buf, sizeof(label_buf));
@@ -2339,7 +2331,7 @@ static int netlink_nexthop_process_group(struct rtattr **tb,
 
        for (int i = 0; ((i < count) && (i < z_grp_size)); i++) {
                z_grp[i].id = n_grp[i].id;
-               z_grp[i].weight = n_grp[i].weight;
+               z_grp[i].weight = n_grp[i].weight + 1;
        }
        return count;
 }
@@ -2361,7 +2353,7 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
        unsigned char family;
        int type;
        afi_t afi = AFI_UNSPEC;
-       vrf_id_t vrf_id = 0;
+       vrf_id_t vrf_id = VRF_DEFAULT;
        struct interface *ifp = NULL;
        struct nhmsg *nhm = NULL;
        struct nexthop nh = {};
index 0adf654aaf72718098c64ba21c5550e3378c5914..e9a97d4b152cd704726bcf695588854c4466486f 100644 (file)
@@ -166,7 +166,8 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf,
 #define RTADV_MSG_SIZE 4096
 
 /* Send router advertisement packet. */
-static void rtadv_send_packet(int sock, struct interface *ifp)
+static void rtadv_send_packet(int sock, struct interface *ifp,
+                             ipv6_nd_suppress_ra_status stop)
 {
        struct msghdr msg;
        struct iovec iov;
@@ -252,7 +253,10 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
                zif->rtadv.AdvDefaultLifetime != -1
                        ? zif->rtadv.AdvDefaultLifetime
                        : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval);
-       rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime);
+
+       /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+       rtadv->nd_ra_router_lifetime =
+               (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime);
        rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime);
        rtadv->nd_ra_retransmit = htonl(0);
 
@@ -512,7 +516,7 @@ static int rtadv_timer(struct thread *thread)
                                                        ifp->name);
 
                                        rtadv_send_packet(rtadv_get_socket(zvrf),
-                                                         ifp);
+                                                         ifp, RA_ENABLE);
                                } else {
                                        zif->rtadv.AdvIntervalTimer -= period;
                                        if (zif->rtadv.AdvIntervalTimer <= 0) {
@@ -526,7 +530,7 @@ static int rtadv_timer(struct thread *thread)
                                                                .MaxRtrAdvInterval;
                                                rtadv_send_packet(
                                                          rtadv_get_socket(zvrf),
-                                                         ifp);
+                                                         ifp, RA_ENABLE);
                                        }
                                }
                        }
@@ -556,7 +560,7 @@ static void rtadv_process_solicit(struct interface *ifp)
        if ((zif->rtadv.UseFastRexmit)
            || (zif->rtadv.AdvIntervalTimer <=
                (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) {
-               rtadv_send_packet(rtadv_get_socket(zvrf), ifp);
+               rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE);
                zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
        } else
                zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS;
@@ -911,6 +915,8 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp,
        if (status == RA_SUPPRESS) {
                /* RA is currently enabled */
                if (zif->rtadv.AdvSendAdvertisements) {
+                       rtadv_send_packet(rtadv_get_socket(zvrf), ifp,
+                                         RA_SUPPRESS);
                        zif->rtadv.AdvSendAdvertisements = 0;
                        zif->rtadv.AdvIntervalTimer = 0;
                        zvrf->rtadv.adv_if_count--;
@@ -959,16 +965,25 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable)
        ifindex_t ifindex;
        struct interface *ifp;
        struct zebra_if *zif;
-       int ra_interval;
+       int ra_interval_rxd;
 
        s = msg;
 
        /* Get interface index and RA interval. */
        STREAM_GETL(s, ifindex);
-       STREAM_GETL(s, ra_interval);
+       STREAM_GETL(s, ra_interval_rxd);
+
+       if (ra_interval_rxd < 0) {
+               zlog_warn(
+                       "Requested RA interval %d is garbage; ignoring request",
+                       ra_interval_rxd);
+               return;
+       }
+
+       unsigned int ra_interval = ra_interval_rxd;
 
        if (IS_ZEBRA_DEBUG_EVENT)
-               zlog_debug("%u: IF %u RA %s from client %s, interval %ds",
+               zlog_debug("%u: IF %u RA %s from client %s, interval %ums",
                           zvrf_id(zvrf), ifindex,
                           enable ? "enable" : "disable",
                           zebra_route_string(client->proto), ra_interval);
@@ -995,7 +1010,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable)
                SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
                ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
                if (ra_interval
-                   && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval
+                   && (ra_interval * 1000) < (unsigned int) zif->rtadv.MaxRtrAdvInterval
                    && !CHECK_FLAG(zif->rtadv.ra_configured,
                                   VTY_RA_INTERVAL_CONFIGURED))
                        zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000;
@@ -1012,6 +1027,38 @@ stream_failure:
        return;
 }
 
+/*
+ * send router lifetime value of zero in RAs on this interface since we're
+ * ceasing to advertise and want to let our neighbors know.
+ * RFC 4861 secion 6.2.5
+ */
+void rtadv_stop_ra(struct interface *ifp)
+{
+       struct zebra_if *zif;
+       struct zebra_vrf *zvrf;
+
+       zif = ifp->info;
+       zvrf = vrf_info_lookup(ifp->vrf_id);
+
+       if (zif->rtadv.AdvSendAdvertisements)
+               rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS);
+}
+
+/*
+ * send router lifetime value of zero in RAs on all interfaces since we're
+ * ceasing to advertise globally and want to let all of our neighbors know
+ * RFC 4861 secion 6.2.5
+ */
+void rtadv_stop_ra_all(void)
+{
+       struct vrf *vrf;
+       struct interface *ifp;
+
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+               FOR_ALL_INTERFACES (vrf, ifp)
+                       rtadv_stop_ra(ifp);
+}
+
 void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS)
 {
        zebra_interface_radv_set(client, hdr, msg, zvrf, 0);
index 409959d08d8f47abf308932ab2f2ba2be0430c37..63cec944341d90ffc9d3ebf7a49eb9831b15f326 100644 (file)
@@ -140,6 +140,8 @@ typedef enum {
 
 extern void rtadv_init(struct zebra_vrf *zvrf);
 extern void rtadv_terminate(struct zebra_vrf *zvrf);
+extern void rtadv_stop_ra(struct interface *ifp);
+extern void rtadv_stop_ra_all(void);
 extern void rtadv_cmd_init(void);
 extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS);
 extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS);
index d0f32d6a14137c65985b9fe242bdfea07d278dba..77ed5a6caa6f7a56c72a923dcaa829469ef1849b 100644 (file)
@@ -10,7 +10,7 @@ vtysh_scan += \
        $(top_srcdir)/zebra/interface.c \
        $(top_srcdir)/zebra/router-id.c \
        $(top_srcdir)/zebra/rtadv.c \
-       $(top_srcdir)/zebra/zebra_mlag.c \
+       $(top_srcdir)/zebra/zebra_mlag_vty.c \
        $(top_srcdir)/zebra/zebra_mpls_vty.c \
        $(top_srcdir)/zebra/zebra_ptm.c \
        $(top_srcdir)/zebra/zebra_pw.c \
@@ -32,13 +32,16 @@ endif
 if FPM
 module_LTLIBRARIES += zebra/zebra_fpm.la
 endif
+if LINUX
+module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la
+endif
 
-man8 += $(MANBUILD)/zebra.8
+man8 += $(MANBUILD)/frr-zebra.8
 ## endif ZEBRA
 endif
 
 zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP)
-if HAVE_PROTOBUF
+if HAVE_PROTOBUF3
 zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS)
 endif
 zebra_zebra_SOURCES = \
@@ -69,7 +72,7 @@ zebra_zebra_SOURCES = \
        zebra/rule_netlink.c \
        zebra/rule_socket.c \
        zebra/zebra_mlag.c \
-       zebra/zebra_mlag_private.c \
+       zebra/zebra_mlag_vty.c \
        zebra/zebra_l2.c \
        zebra/zebra_memory.c \
        zebra/zebra_dplane.c \
@@ -103,8 +106,8 @@ zebra_zebra_SOURCES = \
 zebra/debug_clippy.c: $(CLIPPY_DEPS)
 zebra/debug.$(OBJEXT): zebra/debug_clippy.c
 
-zebra/zebra_mlag_clippy.c: $(CLIPPY_DEPS)
-zebra/zebra_mlag.$(OBJEXT): zebra/zebra_mlag_clippy.c
+zebra/zebra_mlag_vty_clippy.c: $(CLIPPY_DEPS)
+zebra/zebra_mlag_vty.$(OBJEXT): zebra/zebra_mlag_vty_clippy.c
 
 zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS)
 zebra/interface_clippy.c: $(CLIPPY_DEPS)
@@ -134,7 +137,7 @@ noinst_HEADERS += \
        zebra/rtadv.h \
        zebra/rule_netlink.h \
        zebra/zebra_mlag.h \
-       zebra/zebra_mlag_private.h \
+       zebra/zebra_mlag_vty.h \
        zebra/zebra_fpm_private.h \
        zebra/zebra_l2.h \
        zebra/zebra_dplane.h \
@@ -185,3 +188,6 @@ if DEV_BUILD
 zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_dt.c
 endif
 endif
+
+zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c
+zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
index e6a06ff95d679b7b980469b6979597ba266e8ba2..db54e6f25bda278d7014dfb8ea81857bf6a8cdfc 100644 (file)
@@ -41,6 +41,7 @@
 #include "lib/vrf.h"
 #include "lib/libfrr.h"
 #include "lib/sockopt.h"
+#include "lib/lib_errors.h"
 
 #include "zebra/zebra_router.h"
 #include "zebra/rib.h"
@@ -147,6 +148,25 @@ static int zserv_encode_nexthop(struct stream *s, struct nexthop *nexthop)
        return 1;
 }
 
+/*
+ * Zebra error addition adds error type.
+ *
+ *
+ *  0                   1
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |      enum zebra_error_types   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+static void zserv_encode_error(struct stream *s, enum zebra_error_types error)
+{
+       stream_put(s, &error, sizeof(error));
+
+       /* Write packet size. */
+       stream_putw_at(s, 0, stream_get_endp(s));
+}
+
 /* Send handlers ----------------------------------------------------------- */
 
 /* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */
@@ -559,13 +579,15 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
                memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix));
        }
 
-       for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
+       for (nexthop = re->nhe->nhg->nexthop;
+            nexthop; nexthop = nexthop->next) {
                if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                        continue;
 
                api_nh = &api.nexthops[count];
                api_nh->vrf_id = nexthop->vrf_id;
                api_nh->type = nexthop->type;
+               api_nh->weight = nexthop->weight;
                switch (nexthop->type) {
                case NEXTHOP_TYPE_BLACKHOLE:
                        api_nh->bh_type = nexthop->bh_type;
@@ -666,7 +688,7 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
                 * nexthop we are looking up. Therefore, we will just iterate
                 * over the top chain of nexthops.
                 */
-               for (nexthop = re->ng->nexthop; nexthop;
+               for (nexthop = re->nhe->nhg->nexthop; nexthop;
                     nexthop = nexthop->next)
                        if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                                num += zserv_encode_nexthop(s, nexthop);
@@ -1393,8 +1415,9 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
        struct prefix_ipv6 *src_p = NULL;
        struct route_entry *re;
        struct nexthop *nexthop = NULL;
+       struct nexthop_group *ng = NULL;
        int i, ret;
-       vrf_id_t vrf_id = 0;
+       vrf_id_t vrf_id;
        struct ipaddr vtep_ip;
 
        s = msg;
@@ -1409,11 +1432,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                char buf_prefix[PREFIX_STRLEN];
 
                prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix));
-               zlog_debug("%s: p=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x",
-                          __func__, buf_prefix,
-                          (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? ""
-                                                                       : "un"),
-                          api.flags);
+               zlog_debug("%s: p=%s, flags=0x%x",
+                          __func__, buf_prefix, api.flags);
        }
 
        /* Allocate new route. */
@@ -1424,7 +1444,6 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
        re->flags = api.flags;
        re->uptime = monotime(NULL);
        re->vrf_id = vrf_id;
-       re->ng = nexthop_group_new();
 
        if (api.tableid)
                re->table = api.tableid;
@@ -1438,11 +1457,13 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                          __func__, &api.prefix,
                          zebra_route_string(client->proto));
 
-               nexthop_group_delete(&re->ng);
                XFREE(MTYPE_RE, re);
                return;
        }
 
+       /* Use temporary list of nexthops */
+       ng = nexthop_group_new();
+
        /*
         * TBD should _all_ of the nexthop add operations use
         * api_nh->vrf_id instead of re->vrf_id ? I only changed
@@ -1457,8 +1478,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
 
                switch (api_nh->type) {
                case NEXTHOP_TYPE_IFINDEX:
-                       nexthop = route_entry_nexthop_ifindex_add(
-                               re, api_nh->ifindex, api_nh->vrf_id);
+                       nexthop = nexthop_from_ifindex(api_nh->ifindex,
+                                                      api_nh->vrf_id);
                        break;
                case NEXTHOP_TYPE_IPV4:
                        if (IS_ZEBRA_DEBUG_RECV) {
@@ -1469,8 +1490,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                                zlog_debug("%s: nh=%s, vrf_id=%d", __func__,
                                           nhbuf, api_nh->vrf_id);
                        }
-                       nexthop = route_entry_nexthop_ipv4_add(
-                               re, &api_nh->gate.ipv4, NULL, api_nh->vrf_id);
+                       nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4,
+                                                   NULL, api_nh->vrf_id);
                        break;
                case NEXTHOP_TYPE_IPV4_IFINDEX:
 
@@ -1486,8 +1507,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                                        __func__, nhbuf, api_nh->vrf_id,
                                        re->vrf_id, ifindex);
                        }
-                       nexthop = route_entry_nexthop_ipv4_ifindex_add(
-                               re, &api_nh->gate.ipv4, NULL, ifindex,
+                       nexthop = nexthop_from_ipv4_ifindex(
+                               &api_nh->gate.ipv4, NULL, ifindex,
                                api_nh->vrf_id);
 
                        /* Special handling for IPv4 routes sourced from EVPN:
@@ -1504,15 +1525,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                        }
                        break;
                case NEXTHOP_TYPE_IPV6:
-                       nexthop = route_entry_nexthop_ipv6_add(
-                               re, &api_nh->gate.ipv6, api_nh->vrf_id);
+                       nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6,
+                                                   api_nh->vrf_id);
                        break;
                case NEXTHOP_TYPE_IPV6_IFINDEX:
                        memset(&vtep_ip, 0, sizeof(struct ipaddr));
                        ifindex = api_nh->ifindex;
-                       nexthop = route_entry_nexthop_ipv6_ifindex_add(
-                               re, &api_nh->gate.ipv6, ifindex,
-                               api_nh->vrf_id);
+                       nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6,
+                                                           ifindex,
+                                                           api_nh->vrf_id);
 
                        /* Special handling for IPv6 routes sourced from EVPN:
                         * the nexthop and associated MAC need to be installed.
@@ -1527,8 +1548,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                        }
                        break;
                case NEXTHOP_TYPE_BLACKHOLE:
-                       nexthop = route_entry_nexthop_blackhole_add(
-                               re, api_nh->bh_type);
+                       nexthop = nexthop_from_blackhole(api_nh->bh_type);
                        break;
                }
 
@@ -1537,15 +1557,19 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                                EC_ZEBRA_NEXTHOP_CREATION_FAILED,
                                "%s: Nexthops Specified: %d but we failed to properly create one",
                                __PRETTY_FUNCTION__, api.nexthop_num);
-                       nexthop_group_delete(&re->ng);
+                       nexthop_group_delete(&ng);
                        XFREE(MTYPE_RE, re);
                        return;
                }
-               if (api_nh->onlink)
+
+               if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK))
                        SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
 
+               if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT))
+                       nexthop->weight = api_nh->weight;
+
                /* MPLS labels for BGP-LU or Segment Routing */
-               if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL)
+               if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
                    && api_nh->type != NEXTHOP_TYPE_IFINDEX
                    && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) {
                        enum lsp_types_t label_type;
@@ -1563,6 +1587,9 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                                           api_nh->label_num,
                                           &api_nh->labels[0]);
                }
+
+               /* Add new nexthop to temporary list */
+               nexthop_group_add_sorted(ng, nexthop);
        }
 
        if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE))
@@ -1579,14 +1606,22 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
                flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI,
                          "%s: Received SRC Prefix but afi is not v6",
                          __PRETTY_FUNCTION__);
-               nexthop_group_delete(&re->ng);
+               nexthop_group_delete(&ng);
                XFREE(MTYPE_RE, re);
                return;
        }
        if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
                src_p = &api.src_prefix;
 
-       ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re);
+       if (api.safi != SAFI_UNICAST && api.safi != SAFI_MULTICAST) {
+               flog_warn(EC_LIB_ZAPI_MISSMATCH,
+                         "%s: Received safi: %d but we can only accept UNICAST or MULTICAST",
+                         __func__, api.safi);
+               nexthop_group_delete(&ng);
+               XFREE(MTYPE_RE, re);
+               return;
+       }
+       ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re, ng);
 
        /* Stats */
        switch (api.prefix.family) {
@@ -2129,6 +2164,7 @@ static void zread_pseudowire(ZAPI_HANDLER_ARGS)
 
        /* Get data. */
        STREAM_GET(ifname, s, IF_NAMESIZE);
+       ifname[IF_NAMESIZE - 1] = '\0';
        STREAM_GETL(s, ifindex);
        STREAM_GETL(s, type);
        STREAM_GETL(s, af);
@@ -2353,6 +2389,25 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
                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
+                               ")\n",
+                               family2str(zpr.rule.filter.src_ip.family),
+                               zpr.rule.filter.src_ip.family);
+                       return;
+               }
+               if (!(zpr.rule.filter.dst_ip.family == AF_INET
+                     || zpr.rule.filter.dst_ip.family == AF_INET6)) {
+                       zlog_warn("Unsupported PBR IP family: %s (%" PRIu8
+                                 ")\n",
+                                 family2str(zpr.rule.filter.dst_ip.family),
+                                 zpr.rule.filter.dst_ip.family);
+                       return;
+               }
+
+
                zpr.vrf_id = zvrf->vrf->vrf_id;
                if (hdr->command == ZEBRA_RULE_ADD)
                        zebra_pbr_add_rule(&zpr);
@@ -2409,6 +2464,7 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS)
                zpi.sock = client->sock;
                STREAM_GETL(s, zpi.unique);
                STREAM_GET(&ipset.ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
+               ipset.ipset_name[ZEBRA_IPSET_NAME_SIZE - 1] = '\0';
                STREAM_GETC(s, zpi.src.family);
                STREAM_GETC(s, zpi.src.prefixlen);
                STREAM_GET(&zpi.src.u.prefix, s, prefix_blen(&zpi.src));
@@ -2440,6 +2496,13 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS)
                /* calculate backpointer */
                zpi.backpointer =
                        zebra_pbr_lookup_ipset_pername(ipset.ipset_name);
+
+               if (!zpi.backpointer) {
+                       zlog_warn("ipset name specified: %s does not exist",
+                                 ipset.ipset_name);
+                       goto stream_failure;
+               }
+
                if (hdr->command == ZEBRA_IPSET_ENTRY_ADD)
                        zebra_pbr_add_ipset_entry(&zpi);
                else
@@ -2452,40 +2515,72 @@ stream_failure:
 
 static inline void zread_iptable(ZAPI_HANDLER_ARGS)
 {
-       struct zebra_pbr_iptable zpi;
+       struct zebra_pbr_iptable *zpi =
+               XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
        struct stream *s;
 
        s = msg;
 
-       memset(&zpi, 0, sizeof(zpi));
-
-       zpi.interface_name_list = list_new();
-       zpi.sock = client->sock;
-       zpi.vrf_id = zvrf->vrf->vrf_id;
-       STREAM_GETL(s, zpi.unique);
-       STREAM_GETL(s, zpi.type);
-       STREAM_GETL(s, zpi.filter_bm);
-       STREAM_GETL(s, zpi.action);
-       STREAM_GETL(s, zpi.fwmark);
-       STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
-       STREAM_GETW(s, zpi.pkt_len_min);
-       STREAM_GETW(s, zpi.pkt_len_max);
-       STREAM_GETW(s, zpi.tcp_flags);
-       STREAM_GETW(s, zpi.tcp_mask_flags);
-       STREAM_GETC(s, zpi.dscp_value);
-       STREAM_GETC(s, zpi.fragment);
-       STREAM_GETC(s, zpi.protocol);
-       STREAM_GETL(s, zpi.nb_interface);
-       zebra_pbr_iptable_update_interfacelist(s, &zpi);
+       zpi->interface_name_list = list_new();
+       zpi->sock = client->sock;
+       zpi->vrf_id = zvrf->vrf->vrf_id;
+       STREAM_GETL(s, zpi->unique);
+       STREAM_GETL(s, zpi->type);
+       STREAM_GETL(s, zpi->filter_bm);
+       STREAM_GETL(s, zpi->action);
+       STREAM_GETL(s, zpi->fwmark);
+       STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE);
+       STREAM_GETW(s, zpi->pkt_len_min);
+       STREAM_GETW(s, zpi->pkt_len_max);
+       STREAM_GETW(s, zpi->tcp_flags);
+       STREAM_GETW(s, zpi->tcp_mask_flags);
+       STREAM_GETC(s, zpi->dscp_value);
+       STREAM_GETC(s, zpi->fragment);
+       STREAM_GETC(s, zpi->protocol);
+       STREAM_GETL(s, zpi->nb_interface);
+       zebra_pbr_iptable_update_interfacelist(s, zpi);
 
        if (hdr->command == ZEBRA_IPTABLE_ADD)
-               zebra_pbr_add_iptable(&zpi);
+               zebra_pbr_add_iptable(zpi);
        else
-               zebra_pbr_del_iptable(&zpi);
+               zebra_pbr_del_iptable(zpi);
+
 stream_failure:
+       zebra_pbr_iptable_free(zpi);
+       zpi = NULL;
        return;
 }
 
+static void zsend_error_msg(struct zserv *client, enum zebra_error_types error,
+                           struct zmsghdr *bad_hdr)
+{
+
+       struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+       zclient_create_header(s, ZEBRA_ERROR, bad_hdr->vrf_id);
+
+       zserv_encode_error(s, error);
+
+       client->error_cnt++;
+       zserv_send_message(client, s);
+}
+
+static void zserv_error_no_vrf(ZAPI_HANDLER_ARGS)
+{
+       if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+               zlog_debug("ZAPI message specifies unknown VRF: %d",
+                          hdr->vrf_id);
+
+       return zsend_error_msg(client, ZEBRA_NO_VRF, hdr);
+}
+
+static void zserv_error_invalid_msg_type(ZAPI_HANDLER_ARGS)
+{
+       zlog_info("Zebra received unknown command %d", hdr->command);
+
+       return zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr);
+}
+
 void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
        [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,
        [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
@@ -2590,6 +2685,14 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg)
        struct zmsghdr hdr;
        struct zebra_vrf *zvrf;
 
+       if (STREAM_READABLE(msg) > ZEBRA_MAX_PACKET_SIZ) {
+               if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+                       zlog_debug(
+                               "ZAPI message is %zu bytes long but the maximum packet size is %u; dropping",
+                               STREAM_READABLE(msg), ZEBRA_MAX_PACKET_SIZ);
+               return;
+       }
+
        zapi_parse_header(msg, &hdr);
 
        if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
@@ -2603,16 +2706,12 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg)
 
        /* lookup vrf */
        zvrf = zebra_vrf_lookup_by_id(hdr.vrf_id);
-       if (!zvrf) {
-               if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
-                       zlog_debug("ZAPI message specifies unknown VRF: %d",
-                                  hdr.vrf_id);
-               return;
-       }
+       if (!zvrf)
+               return zserv_error_no_vrf(client, &hdr, msg, zvrf);
 
        if (hdr.command >= array_size(zserv_handlers)
            || zserv_handlers[hdr.command] == NULL)
-               zlog_info("Zebra received unknown command %d", hdr.command);
-       else
-               zserv_handlers[hdr.command](client, &hdr, msg, zvrf);
+               return zserv_error_invalid_msg_type(client, &hdr, msg, zvrf);
+
+       zserv_handlers[hdr.command](client, &hdr, msg, zvrf);
 }
index aecadfadf8ff5a67b2d4eea3fcc2fdf80c09ed23..bf1ba522a37fb6ead27900b5e5a34ec2f3bc37b7 100644 (file)
@@ -1023,6 +1023,11 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
        return ctx->u.rinfo.zd_old_distance;
 }
 
+/*
+ * Set the nexthops associated with a context: note that processing code
+ * may well expect that nexthops are in canonical (sorted) order, so we
+ * will enforce that here.
+ */
 void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
 {
        DPLANE_CTX_VALID(ctx);
@@ -1031,7 +1036,7 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
                nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
                ctx->u.rinfo.zd_ng.nexthop = NULL;
        }
-       copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL);
+       nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh);
 }
 
 const struct nexthop_group *dplane_ctx_get_ng(
@@ -1508,7 +1513,8 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
        ctx->u.rinfo.zd_safi = info->safi;
 
        /* Copy nexthops; recursive info is included too */
-       copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng->nexthop, NULL);
+       copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop),
+                     re->nhe->nhg->nexthop, NULL);
 
        /* Ensure that the dplane's nexthops flags are clear. */
        for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
@@ -1748,7 +1754,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
 
                        if (re)
                                copy_nexthops(&(ctx->u.pw.nhg.nexthop),
-                                             re->ng->nexthop, NULL);
+                                             re->nhe->nhg->nexthop, NULL);
 
                        route_unlock_node(rn);
                }
@@ -1844,7 +1850,7 @@ dplane_route_update_internal(struct route_node *rn,
                         * We'll need these to do per-nexthop deletes.
                         */
                        copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
-                                     old_re->ng->nexthop, NULL);
+                                     old_re->nhe->nhg->nexthop, NULL);
 #endif /* !HAVE_NETLINK */
                }
 
@@ -2507,7 +2513,7 @@ enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp,
        enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
 
        result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL,
-                                      ifp, mac, ip, flags, 0);
+                                      ifp, mac, ip, flags, DPLANE_NUD_NOARP);
 
        return result;
 }
@@ -2813,6 +2819,7 @@ int dplane_provider_register(const char *name,
        TAILQ_INIT(&(p->dp_ctx_in_q));
        TAILQ_INIT(&(p->dp_ctx_out_q));
 
+       p->dp_flags = flags;
        p->dp_priority = prio;
        p->dp_fp = fp;
        p->dp_start = start_fp;
index 5a0905d59115090ce19fef4031dd7cffdf389a6b..ef792d14c285f6cdc028b9d6d801f36d46182f6a 100644 (file)
@@ -316,6 +316,12 @@ static struct log_ref ferr_zebra_err[] = {
                .description = "Zebra attempted to look up a interface for a particular vrf_id and interface index, but didn't find anything.",
                .suggestion = "If you entered a command to trigger this error, make sure you entered the arguments correctly. Check your config file for any potential errors. If these look correct, seek help.",
        },
+       {
+               .code = EC_ZEBRA_NS_NO_DEFAULT,
+               .title = "Zebra NameSpace failed to find Default",
+               .description = "Zebra NameSpace subsystem failed to find a Default namespace during initialization.",
+               .suggestion = "Open an Issue with all relevant log files and restart FRR",
+       },
        /* Warnings */
        {
                .code = EC_ZEBRAING_LM_PROTO_MISMATCH,
index f9ccc2db282df6f43f5e7176725724bd3bdc2d3d..4625a03ae6445ca7371c6db81ac71f7bb4a0f244 100644 (file)
@@ -76,6 +76,7 @@ enum zebra_log_refs {
        EC_ZEBRA_NHG_SYNC,
        EC_ZEBRA_NHG_FIB_UPDATE,
        EC_ZEBRA_IF_LOOKUP_FAILED,
+       EC_ZEBRA_NS_NO_DEFAULT,
        /* warnings */
        EC_ZEBRA_NS_NOTIFY_READ,
        EC_ZEBRAING_LM_PROTO_MISMATCH,
index debcf60ee5fb766589b38e86443b8ddacd82f51f..389781d4f74ceaa3eced608b3dab5c1f1d6972f0 100644 (file)
@@ -90,7 +90,7 @@ static int zfpm_dt_find_route(rib_dest_t **dest_p, struct route_entry **re_p)
                if (!re)
                        continue;
 
-               if (nexthop_group_active_nexthop_num(re->ng) == 0)
+               if (nexthop_group_active_nexthop_num(re->nhe->nhg) == 0)
                        continue;
 
                *dest_p = dest;
index b54d8fbc12256f97b8690f6c1a67e8a7fc82dd32..7786dc246c34eac08cef50020ec9b87f2a497d19 100644 (file)
@@ -314,7 +314,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
        ri->rtm_type = RTN_UNICAST;
        ri->metric = &re->metric;
 
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                if (ri->num_nhs >= zrouter.multipath_num)
                        break;
 
index a11517ab8b8c66a3c7d22702d6eab92185950a30..c09fa1c65d8a755fc620f97f95011de56c27e212 100644 (file)
@@ -173,7 +173,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator,
         * Figure out the set of nexthops to be added to the message.
         */
        num_nhs = 0;
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                if (num_nhs >= zrouter.multipath_num)
                        break;
 
index 1a911e429f66855c940d322e85483742d93af716..f6bd5f458647be6a8ba05beff7efc61d0206499d 100644 (file)
 #include "mlag.h"
 
 #include "zebra/zebra_mlag.h"
-#include "zebra/zebra_mlag_private.h"
+#include "zebra/zebra_mlag_vty.h"
 #include "zebra/zebra_router.h"
 #include "zebra/zebra_memory.h"
 #include "zebra/zapi_msg.h"
 #include "zebra/debug.h"
 
-#ifndef VTYSH_EXTRACT_PL
-#include "zebra/zebra_mlag_clippy.c"
-#endif
+DEFINE_HOOK(zebra_mlag_private_write_data,
+           (uint8_t *data, uint32_t len), (data, len))
+DEFINE_HOOK(zebra_mlag_private_monitor_state, (), ())
+DEFINE_HOOK(zebra_mlag_private_open_channel, (), ())
+DEFINE_HOOK(zebra_mlag_private_close_channel, (), ())
+DEFINE_HOOK(zebra_mlag_private_cleanup_data, (), ())
 
 #define ZEBRA_MLAG_METADATA_LEN 4
 #define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF
@@ -175,7 +178,8 @@ static int zebra_mlag_client_msg_handler(struct thread *event)
                 * write to MCLAGD
                 */
                if (len > 0) {
-                       zebra_mlag_private_write_data(mlag_wr_buffer, len);
+                       hook_call(zebra_mlag_private_write_data,
+                                 mlag_wr_buffer, len);
 
                        /*
                         * If message type is De-register, send a signal to main
@@ -220,7 +224,7 @@ void zebra_mlag_handle_process_state(enum zebra_mlag_state state)
        } else if (state == MLAG_DOWN) {
                zrouter.mlag_info.connected = false;
                zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN);
-               zebra_mlag_private_monitor_state();
+               hook_call(zebra_mlag_private_monitor_state);
        }
 }
 
@@ -412,7 +416,7 @@ static int zebra_mlag_terminate_pthread(struct thread *event)
        /*
         * Send Notification to clean private data
         */
-       zebra_mlag_private_cleanup_data();
+       hook_call(zebra_mlag_private_cleanup_data);
        return 0;
 }
 
@@ -470,7 +474,7 @@ void zebra_mlag_client_register(ZAPI_HANDLER_ARGS)
                                "First client, opening the channel with MLAG");
 
                zebra_mlag_spawn_pthread();
-               rc = zebra_mlag_private_open_channel();
+               rc = hook_call(zebra_mlag_private_open_channel);
                if (rc < 0) {
                        /*
                         * For some reason, zebra not able to open the
@@ -530,7 +534,7 @@ void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS)
                 * signal back to main thread to do the thread cleanup
                 * this was mainly to make sure De-register is posted to MCLAGD.
                 */
-               zebra_mlag_private_close_channel();
+               hook_call(zebra_mlag_private_close_channel);
        }
 
        if (IS_ZEBRA_DEBUG_MLAG)
@@ -579,29 +583,8 @@ enum mlag_role zebra_mlag_get_role(void)
        return zrouter.mlag_info.role;
 }
 
-DEFUN_HIDDEN (show_mlag,
-             show_mlag_cmd,
-             "show zebra mlag",
-             SHOW_STR
-             ZEBRA_STR
-             "The mlag role on this machine\n")
-{
-       char buf[MLAG_ROLE_STRSIZE];
-
-       vty_out(vty, "MLag is configured to: %s\n",
-               mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf)));
-
-       return CMD_SUCCESS;
-}
-
-DEFPY_HIDDEN(test_mlag, test_mlag_cmd,
-            "test zebra mlag <none$none|primary$primary|secondary$secondary>",
-            "Test code\n"
-            ZEBRA_STR
-            "Modify the Mlag state\n"
-            "Mlag is not setup on the machine\n"
-            "Mlag is setup to be primary\n"
-            "Mlag is setup to be the secondary\n")
+int32_t zebra_mlag_test_mlag_internal(const char *none, const char *primary,
+                                     const char *secondary)
 {
        enum mlag_role orig = zrouter.mlag_info.role;
        char buf1[MLAG_ROLE_STRSIZE], buf2[MLAG_ROLE_STRSIZE];
@@ -627,13 +610,13 @@ DEFPY_HIDDEN(test_mlag, test_mlag_cmd,
                                        zebra_mlag_spawn_pthread();
                                zrouter.mlag_info.clients_interested_cnt++;
                                test_mlag_in_progress = true;
-                               zebra_mlag_private_open_channel();
+                               hook_call(zebra_mlag_private_open_channel);
                        }
                } else {
                        if (test_mlag_in_progress == true) {
                                test_mlag_in_progress = false;
                                zrouter.mlag_info.clients_interested_cnt--;
-                               zebra_mlag_private_close_channel();
+                               hook_call(zebra_mlag_private_close_channel);
                        }
                }
        }
@@ -643,8 +626,7 @@ DEFPY_HIDDEN(test_mlag, test_mlag_cmd,
 
 void zebra_mlag_init(void)
 {
-       install_element(VIEW_NODE, &show_mlag_cmd);
-       install_element(ENABLE_NODE, &test_mlag_cmd);
+       zebra_mlag_vty_init();
 
        /*
         * Intialiaze the MLAG Global variables
@@ -672,7 +654,7 @@ void zebra_mlag_terminate(void)
  *  ProtoBuf Encoding APIs
  */
 
-#ifdef HAVE_PROTOBUF
+#ifdef HAVE_PROTOBUF_VERSION_3
 
 DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF")
 
index 6f7ef8319f3ab00d8868d475978a156f6f07840b..c35fa15561c6745671e7e97c980684d7de766bc0 100644 (file)
 #include "zclient.h"
 #include "zebra/zserv.h"
 
-#ifdef HAVE_PROTOBUF
+#ifdef HAVE_PROTOBUF_VERSION_3
 #include "mlag/mlag.pb-c.h"
 #endif
 
 #define ZEBRA_MLAG_BUF_LIMIT 2048
 #define ZEBRA_MLAG_LEN_SIZE 4
 
+DECLARE_HOOK(zebra_mlag_private_write_data,
+            (uint8_t *data, uint32_t len), (data, len))
+DECLARE_HOOK(zebra_mlag_private_monitor_state, (), ())
+DECLARE_HOOK(zebra_mlag_private_open_channel, (), ())
+DECLARE_HOOK(zebra_mlag_private_close_channel, (), ())
+DECLARE_HOOK(zebra_mlag_private_cleanup_data, (), ())
+
 extern uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT];
 extern uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT];
 extern uint32_t mlag_rd_buf_offset;
@@ -57,6 +64,7 @@ void zebra_mlag_send_register(void);
 void zebra_mlag_send_deregister(void);
 void zebra_mlag_handle_process_state(enum zebra_mlag_state state);
 void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len);
+
 /*
  * ProtoBuffer Api's
  */
index 4df7b6dd114cc746c64149b6a371b77a6014dd81..3024407ada0b2ea23dbbc5f1e61153904d46cad1 100644 (file)
@@ -36,7 +36,6 @@
 #include "zebra/debug.h"
 #include "zebra/zebra_router.h"
 #include "zebra/zebra_mlag.h"
-#include "zebra/zebra_mlag_private.h"
 
 #include <sys/un.h>
 
@@ -46,8 +45,6 @@
  *
  */
 
-#ifdef HAVE_CUMULUS
-
 static struct thread_master *zmlag_master;
 static int mlag_socket;
 
@@ -57,7 +54,7 @@ static int zebra_mlag_read(struct thread *thread);
 /*
  * Write the data to MLAGD
  */
-int zebra_mlag_private_write_data(uint8_t *data, uint32_t len)
+static int zebra_mlag_private_write_data(uint8_t *data, uint32_t len)
 {
        int rc = 0;
 
@@ -103,7 +100,7 @@ static int zebra_mlag_read(struct thread *thread)
                        return -1;
                }
                mlag_rd_buf_offset += data_len;
-               if (data_len != (ssize_t)ZEBRA_MLAG_LEN_SIZE - curr_len) {
+               if (data_len != (ssize_t)(ZEBRA_MLAG_LEN_SIZE - curr_len)) {
                        /* Try again later */
                        zebra_mlag_sched_read();
                        return 0;
@@ -132,7 +129,7 @@ static int zebra_mlag_read(struct thread *thread)
                        return -1;
                }
                mlag_rd_buf_offset += data_len;
-               if (data_len != (ssize_t)tot_len - curr_len) {
+               if (data_len != (ssize_t)(tot_len - curr_len)) {
                        /* Try again later */
                        zebra_mlag_sched_read();
                        return 0;
@@ -207,13 +204,14 @@ static int zebra_mlag_connect(struct thread *thread)
 /*
  * Currently we are doing polling later we will look for better options
  */
-void zebra_mlag_private_monitor_state(void)
+static int zebra_mlag_private_monitor_state(void)
 {
        thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0,
                         &zrouter.mlag_info.t_read);
+       return 0;
 }
 
-int zebra_mlag_private_open_channel(void)
+static int zebra_mlag_private_open_channel(void)
 {
        zmlag_master = zrouter.mlag_info.th_master;
 
@@ -242,7 +240,7 @@ int zebra_mlag_private_open_channel(void)
        return 0;
 }
 
-int zebra_mlag_private_close_channel(void)
+static int zebra_mlag_private_close_channel(void)
 {
        if (zmlag_master == NULL)
                return -1;
@@ -263,37 +261,34 @@ int zebra_mlag_private_close_channel(void)
        return 0;
 }
 
-void zebra_mlag_private_cleanup_data(void)
+static int zebra_mlag_private_cleanup_data(void)
 {
        zmlag_master = NULL;
        zrouter.mlag_info.connected = false;
        zrouter.mlag_info.timer_running = false;
 
        close(mlag_socket);
-}
-
-#else  /*HAVE_CUMULUS */
-
-int zebra_mlag_private_write_data(uint8_t *data, uint32_t len)
-{
        return 0;
 }
 
-void zebra_mlag_private_monitor_state(void)
-{
-}
-
-int zebra_mlag_private_open_channel(void)
+static int zebra_mlag_module_init(void)
 {
+       hook_register(zebra_mlag_private_write_data,
+                     zebra_mlag_private_write_data);
+       hook_register(zebra_mlag_private_monitor_state,
+                     zebra_mlag_private_monitor_state);
+       hook_register(zebra_mlag_private_open_channel,
+                     zebra_mlag_private_open_channel);
+       hook_register(zebra_mlag_private_close_channel,
+                     zebra_mlag_private_close_channel);
+       hook_register(zebra_mlag_private_cleanup_data,
+                     zebra_mlag_private_cleanup_data);
        return 0;
 }
 
-int zebra_mlag_private_close_channel(void)
-{
-       return 0;
-}
-
-void zebra_mlag_private_cleanup_data(void)
-{
-}
-#endif /*HAVE_CUMULUS*/
+FRR_MODULE_SETUP(
+       .name = "zebra_cumulus_mlag",
+       .version = FRR_VERSION,
+       .description = "zebra Cumulus MLAG interface",
+       .init = zebra_mlag_module_init,
+)
diff --git a/zebra/zebra_mlag_private.h b/zebra/zebra_mlag_private.h
deleted file mode 100644 (file)
index f7b68e9..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * This is an implementation of MLAG Functionality
- *
- * Module name: Zebra MLAG
- *
- * Author: sathesh Kumar karra <sathk@cumulusnetworks.com>
- *
- * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com
- *
- * 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 __ZEBRA_MLAG_PRIVATE_H__
-#define __ZEBRA_MLAG_PRIVATE_H__
-
-
-/*
- * all the platform specific API's
- */
-
-int zebra_mlag_private_open_channel(void);
-int zebra_mlag_private_close_channel(void);
-void zebra_mlag_private_monitor_state(void);
-int zebra_mlag_private_write_data(uint8_t *data, uint32_t len);
-void zebra_mlag_private_cleanup_data(void);
-#endif
diff --git a/zebra/zebra_mlag_vty.c b/zebra/zebra_mlag_vty.c
new file mode 100644 (file)
index 0000000..ebaaf03
--- /dev/null
@@ -0,0 +1,67 @@
+/* Zebra Mlag vty Code.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ *                    Donald Sharp
+ *
+ * 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 "vty.h"
+#include "command.h"
+
+#include "zebra_router.h"
+#include "zebra_mlag_vty.h"
+#include "debug.h"
+#include "zapi_msg.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "zebra/zebra_mlag_vty_clippy.c"
+#endif
+
+DEFUN_HIDDEN (show_mlag,
+             show_mlag_cmd,
+             "show zebra mlag",
+             SHOW_STR
+             ZEBRA_STR
+             "The mlag role on this machine\n")
+{
+       char buf[MLAG_ROLE_STRSIZE];
+
+       vty_out(vty, "MLag is configured to: %s\n",
+               mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf)));
+
+       return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(test_mlag, test_mlag_cmd,
+            "test zebra mlag <none$none|primary$primary|secondary$secondary>",
+            "Test code\n"
+            ZEBRA_STR
+            "Modify the Mlag state\n"
+            "Mlag is not setup on the machine\n"
+            "Mlag is setup to be primary\n"
+            "Mlag is setup to be the secondary\n")
+{
+       return zebra_mlag_test_mlag_internal(none, primary, secondary);
+}
+
+void zebra_mlag_vty_init(void)
+{
+       install_element(VIEW_NODE, &show_mlag_cmd);
+       install_element(ENABLE_NODE, &test_mlag_cmd);
+}
diff --git a/zebra/zebra_mlag_vty.h b/zebra/zebra_mlag_vty.h
new file mode 100644 (file)
index 0000000..c3dfdf7
--- /dev/null
@@ -0,0 +1,31 @@
+/* Zebra Mlag vty Code.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ *                    Donald Sharp
+ *
+ * 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_MLAG_VTY_CODE__
+#define __ZEBRA_MLAG_VTY_CODE__
+
+extern int32_t zebra_mlag_test_mlag_internal(const char *none,
+                                            const char *primary,
+                                            const char *secondary);
+
+extern void zebra_mlag_vty_init(void);
+
+#endif
index 5146311c6aed1893b937e14d0d4fa671430935b0..d5d424732acbfccc9b9a7bbd6147336d5521770e 100644 (file)
@@ -186,7 +186,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
         * the label advertised by the recursive nexthop (plus we don't have the
         * logic yet to push multiple labels).
         */
-       for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
+       for (nexthop = re->nhe->nhg->nexthop;
+            nexthop; nexthop = nexthop->next) {
                /* Skip inactive and recursive entries. */
                if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                        continue;
@@ -637,7 +638,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe,
                    || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED))
                        continue;
 
-               for (match_nh = match->ng->nexthop; match_nh;
+               for (match_nh = match->nhe->nhg->nexthop; match_nh;
                     match_nh = match_nh->next) {
                        if (match->type == ZEBRA_ROUTE_CONNECT
                            || nexthop->ifindex == match_nh->ifindex) {
@@ -688,10 +689,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe,
                        break;
        }
 
-       if (!match || !match->ng->nexthop)
+       if (!match || !match->nhe->nhg->nexthop)
                return 0;
 
-       nexthop->ifindex = match->ng->nexthop->ifindex;
+       nexthop->ifindex = match->nhe->nhg->nexthop->ifindex;
        return 1;
 }
 
@@ -2577,7 +2578,7 @@ static void mpls_zebra_nhg_update(struct route_entry *re, afi_t afi,
 
        nhe = zebra_nhg_rib_find(0, new_grp, afi);
 
-       zebra_nhg_re_update_ref(re, nhe);
+       route_entry_update_nhe(re, nhe);
 }
 
 static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop,
@@ -2631,7 +2632,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type,
         * We can't just change the values here since we are hashing
         * on labels. We need to create a whole new group
         */
-       nexthop_group_copy(&new_grp, re->ng);
+       nexthop_group_copy(&new_grp, re->nhe->nhg);
 
        found = false;
        for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
@@ -2712,7 +2713,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,
        if (re == NULL)
                return -1;
 
-       nexthop_group_copy(&new_grp, re->ng);
+       nexthop_group_copy(&new_grp, re->nhe->nhg);
 
        for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next)
                nexthop_del_labels(nexthop);
@@ -2949,7 +2950,7 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf,
                RNODE_FOREACH_RE (rn, re) {
                        struct nexthop_group new_grp = {};
 
-                       nexthop_group_copy(&new_grp, re->ng);
+                       nexthop_group_copy(&new_grp, re->nhe->nhg);
 
                        for (nexthop = new_grp.nexthop; nexthop;
                             nexthop = nexthop->next) {
index f06ff44f20eef091d1fccc5fe8c8ba924a0dc9ae..74303073206b6ac213ddd1dc60156b6bcf00cd75 100644 (file)
@@ -49,7 +49,8 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
 /* id counter to keep in sync with kernel */
 uint32_t id_counter;
 
-static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi);
+static struct nhg_hash_entry *depends_find(const struct nexthop *nh,
+                                          afi_t afi);
 static void depends_add(struct nhg_connected_tree_head *head,
                        struct nhg_hash_entry *depend);
 static struct nhg_hash_entry *
@@ -299,13 +300,22 @@ zebra_nhg_connect_depends(struct nhg_hash_entry *nhe,
        }
 }
 
-static struct nhg_hash_entry *zebra_nhg_copy(struct nhg_hash_entry *copy,
-                                            uint32_t id)
+struct nhg_hash_entry *zebra_nhg_alloc(void)
 {
        struct nhg_hash_entry *nhe;
 
        nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
 
+       return nhe;
+}
+
+static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy,
+                                            uint32_t id)
+{
+       struct nhg_hash_entry *nhe;
+
+       nhe = zebra_nhg_alloc();
+
        nhe->id = id;
 
        nhe->nhg = nexthop_group_new();
@@ -468,7 +478,7 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends,
        struct nhg_hash_entry *depend = NULL;
        struct nexthop_group resolved_ng = {};
 
-       _nexthop_group_add_sorted(&resolved_ng, nh);
+       resolved_ng.nexthop = nh;
 
        depend = zebra_nhg_rib_find(0, &resolved_ng, afi);
        depends_add(nhg_depends, depend);
@@ -497,7 +507,7 @@ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id,
        if (lookup.nhg->nexthop->next) {
                /* Groups can have all vrfs and AF's in them */
                lookup.afi = AFI_UNSPEC;
-               lookup.vrf_id = 0;
+               lookup.vrf_id = VRF_DEFAULT;
        } else {
                switch (lookup.nhg->nexthop->type) {
                case (NEXTHOP_TYPE_IFINDEX):
@@ -582,9 +592,9 @@ zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type)
        struct nhg_hash_entry *nhe = NULL;
        struct nexthop_group nhg = {};
 
-       _nexthop_group_add_sorted(&nhg, nh);
+       nexthop_group_add_sorted(&nhg, nh);
 
-       zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, 0);
+       zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, type);
 
        return nhe;
 }
@@ -1038,26 +1048,55 @@ int zebra_nhg_kernel_del(uint32_t id)
 }
 
 /* Some dependency helper functions */
-static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi)
+static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh,
+                                                    afi_t afi)
 {
+       struct nhg_hash_entry *nhe;
        struct nexthop *lookup = NULL;
-       struct nhg_hash_entry *nhe = NULL;
-
-       if (!nh)
-               goto done;
-
-       copy_nexthops(&lookup, nh, NULL);
 
-       /* Clear it, in case its a group */
-       nexthops_free(lookup->next);
-       nexthops_free(lookup->prev);
-       lookup->next = NULL;
-       lookup->prev = NULL;
+       lookup = nexthop_dup(nh, NULL);
 
        nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0);
 
        nexthops_free(lookup);
 
+       return nhe;
+}
+
+static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh,
+                                                    afi_t afi)
+{
+       struct nhg_hash_entry *nhe;
+       struct nexthop lookup = {};
+
+       /* Capture a snapshot of this single nh; it might be part of a list,
+        * so we need to make a standalone copy.
+        */
+       nexthop_copy_no_recurse(&lookup, nh, NULL);
+
+       nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0);
+
+       /* The copy may have allocated labels; free them if necessary. */
+       nexthop_del_labels(&lookup);
+
+       return nhe;
+}
+
+static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi)
+{
+       struct nhg_hash_entry *nhe = NULL;
+
+       if (!nh)
+               goto done;
+
+       /* We are separating these functions out to increase handling speed
+        * in the non-recursive case (by not alloc/freeing)
+        */
+       if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
+               nhe = depends_find_recursive(nh, afi);
+       else
+               nhe = depends_find_singleton(nh, afi);
+
 done:
        return nhe;
 }
@@ -1128,12 +1167,8 @@ static void zebra_nhg_free_members(struct nhg_hash_entry *nhe)
        nhg_connected_tree_free(&nhe->nhg_dependents);
 }
 
-void zebra_nhg_free(void *arg)
+void zebra_nhg_free(struct nhg_hash_entry *nhe)
 {
-       struct nhg_hash_entry *nhe = NULL;
-
-       nhe = (struct nhg_hash_entry *)arg;
-
        if (nhe->refcnt)
                zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt);
 
@@ -1142,6 +1177,11 @@ void zebra_nhg_free(void *arg)
        XFREE(MTYPE_NHG, nhe);
 }
 
+void zebra_nhg_hash_free(void *p)
+{
+       zebra_nhg_free((struct nhg_hash_entry *)p);
+}
+
 void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
 {
        nhe->refcnt--;
@@ -1447,7 +1487,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
 
                if (match->type == ZEBRA_ROUTE_CONNECT) {
                        /* Directly point connected route. */
-                       newhop = match->ng->nexthop;
+                       newhop = match->nhe->nhg->nexthop;
                        if (newhop) {
                                if (nexthop->type == NEXTHOP_TYPE_IPV4
                                    || nexthop->type == NEXTHOP_TYPE_IPV6)
@@ -1456,7 +1496,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
                        return 1;
                } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
                        resolved = 0;
-                       for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+                       for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
                                if (!CHECK_FLAG(match->status,
                                                ROUTE_ENTRY_INSTALLED))
                                        continue;
@@ -1477,7 +1517,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
                        return resolved;
                } else if (re->type == ZEBRA_ROUTE_STATIC) {
                        resolved = 0;
-                       for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+                       for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
                                if (!CHECK_FLAG(match->status,
                                                ROUTE_ENTRY_INSTALLED))
                                        continue;
@@ -1668,7 +1708,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
        UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
 
        /* Copy over the nexthops in current state */
-       nexthop_group_copy(&new_grp, re->ng);
+       nexthop_group_copy(&new_grp, re->nhe->nhg);
 
        for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
 
@@ -1718,7 +1758,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
 
                new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi);
 
-               zebra_nhg_re_update_ref(re, new_nhe);
+               route_entry_update_nhe(re, new_nhe);
        }
 
        if (curr_active) {
@@ -1744,40 +1784,6 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
        return curr_active;
 }
 
-static void zebra_nhg_re_attach_ref(struct route_entry *re,
-                                   struct nhg_hash_entry *new)
-{
-       re->ng = new->nhg;
-       re->nhe_id = new->id;
-
-       zebra_nhg_increment_ref(new);
-}
-
-int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new)
-{
-       struct nhg_hash_entry *old = NULL;
-       int ret = 0;
-
-       if (new == NULL) {
-               re->ng = NULL;
-               goto done;
-       }
-
-       if (re->nhe_id != new->id) {
-               old = zebra_nhg_lookup_id(re->nhe_id);
-
-               zebra_nhg_re_attach_ref(re, new);
-
-               if (old)
-                       zebra_nhg_decrement_ref(old);
-       } else if (!re->ng)
-               /* This is the first time it's being attached */
-               zebra_nhg_re_attach_ref(re, new);
-
-done:
-       return ret;
-}
-
 /* Convert a nhe into a group array */
 uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
                          int max_num)
@@ -1814,7 +1820,7 @@ uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
                if (!duplicate) {
                        grp[i].id = depend->id;
                        /* We aren't using weights for anything right now */
-                       grp[i].weight = 0;
+                       grp[i].weight = depend->nhg->nexthop->weight;
                        i++;
                }
 
index 1f695433c9689dc2733261511ba724ac6ab1df1d..522ec1e9ddfb6615cc5b98431a2dfcf559fe3f09 100644 (file)
 #ifndef __ZEBRA_NHG_H__
 #define __ZEBRA_NHG_H__
 
-#include "zebra/rib.h"
+#include "lib/nexthop.h"
 #include "lib/nexthop_group.h"
 
-#include "zebra/zebra_dplane.h"
-
 /* This struct is used exclusively for dataplane
  * interaction via a dataplane context.
  *
@@ -160,6 +158,11 @@ struct nhg_ctx {
  * NHE abstracted tree functions.
  * Use these where possible instead of the direct ones access ones.
  */
+struct nhg_hash_entry *zebra_nhg_alloc(void);
+void zebra_nhg_free(struct nhg_hash_entry *nhe);
+/* In order to clear a generic hash, we need a generic api, sigh. */
+void zebra_nhg_hash_free(void *p);
+
 extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe);
 
 extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe);
@@ -201,8 +204,6 @@ zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi);
 /* Reference counter functions */
 extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe);
 extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe);
-extern int zebra_nhg_re_update_ref(struct route_entry *re,
-                                  struct nhg_hash_entry *nhe);
 
 /* Check validity of nhe, if invalid will update dependents as well */
 extern void zebra_nhg_check_valid(struct nhg_hash_entry *nhe);
@@ -224,5 +225,6 @@ extern void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx);
 extern void zebra_nhg_sweep_table(struct hash *hash);
 
 /* Nexthop resolution processing */
+struct route_entry; /* Forward ref to avoid circular includes */
 extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
 #endif
index 170e2357e3a3791f6ef3f559605cefa5cf6dfa44..79107b047c95c1485d81f596d1d847f0e5e72614 100644 (file)
@@ -57,6 +57,4 @@ extern void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
 extern void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
                                       struct nhg_hash_entry *nhe);
 
-extern void zebra_nhg_free(void *arg);
-
 #endif /* __ZEBRA_NHG_PRIVATE_H__ */
index 37f53bf91187fddf220624d9480b3cd0c64de4a4..3287176ef5c0a1e7411d63055b7b6d4d1e2b83d5 100644 (file)
@@ -37,6 +37,7 @@
 #include "zebra_pbr.h"
 #include "rib.h"
 #include "table_manager.h"
+#include "zebra_errors.h"
 
 extern struct zebra_privs_t zserv_privs;
 
@@ -64,6 +65,9 @@ static int zebra_ns_new(struct ns *ns)
 {
        struct zebra_ns *zns;
 
+       if (!ns)
+               return -1;
+
        if (IS_ZEBRA_DEBUG_EVENT)
                zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id);
 
@@ -86,7 +90,7 @@ static int zebra_ns_delete(struct ns *ns)
                zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id);
        if (!zns)
                return 0;
-       XFREE(MTYPE_ZEBRA_NS, zns);
+       XFREE(MTYPE_ZEBRA_NS, ns->info);
        return 0;
 }
 
@@ -175,19 +179,26 @@ int zebra_ns_final_shutdown(struct ns *ns)
 
 int zebra_ns_init(const char *optional_default_name)
 {
+       struct ns *default_ns;
        ns_id_t ns_id;
        ns_id_t ns_id_external;
 
-       dzns = zebra_ns_alloc();
-
        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);
 
+       default_ns = ns_lookup(ns_get_default_id());
+       if (!default_ns) {
+               flog_err(EC_ZEBRA_NS_NO_DEFAULT,
+                        "%s: failed to find default ns", __func__);
+               exit(EXIT_FAILURE); /* This is non-recoverable */
+       }
+
        /* Do any needed per-NS data structure allocation. */
-       dzns->if_table = route_table_init();
+       zebra_ns_new(default_ns);
+       dzns = default_ns->info;
 
        /* Register zebra VRF callbacks, create and activate default VRF. */
        zebra_vrf_init();
index e24d2e2b422914f776ff6bd677d21a36a1e7adf3..fe7a93a50c4de617850f0e54f38f0bae80079500 100644 (file)
@@ -345,11 +345,13 @@ void zebra_pbr_iptable_free(void *arg)
        iptable = (struct zebra_pbr_iptable *)arg;
        hook_call(zebra_pbr_iptable_update, 0, iptable);
 
-       for (ALL_LIST_ELEMENTS(iptable->interface_name_list,
-                                       node, nnode, name)) {
-               XFREE(MTYPE_PBR_IPTABLE_IFNAME, name);
-               list_delete_node(iptable->interface_name_list,
-                                node);
+       if (iptable->interface_name_list) {
+               for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node,
+                                      nnode, name)) {
+                       XFREE(MTYPE_PBR_IPTABLE_IFNAME, name);
+                       list_delete_node(iptable->interface_name_list, node);
+               }
+               list_delete(&iptable->interface_name_list);
        }
        XFREE(MTYPE_TMP, iptable);
 }
@@ -650,12 +652,22 @@ static void *pbr_iptable_alloc_intern(void *arg)
 {
        struct zebra_pbr_iptable *zpi;
        struct zebra_pbr_iptable *new;
+       struct listnode *ln;
+       char *ifname;
 
        zpi = (struct zebra_pbr_iptable *)arg;
 
        new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
 
+       /* Deep structure copy */
        memcpy(new, zpi, sizeof(*zpi));
+       new->interface_name_list = list_new();
+
+       if (zpi->interface_name_list) {
+               for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname))
+                       listnode_add(new->interface_name_list,
+                                    XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname));
+       }
 
        return new;
 }
@@ -688,6 +700,7 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
                        list_delete_node(iptable->interface_name_list,
                                         node);
                }
+               list_delete(&iptable->interface_name_list);
                XFREE(MTYPE_TMP, lookup);
        } else
                zlog_debug("%s: IPTable being deleted we know nothing about",
index 46f138552059f6f9edfc03287cc3bc991c2ec22f..d7bbe779bdb15462b198f0f51a0a8b02b086794a 100644 (file)
@@ -1402,39 +1402,28 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf,
                               struct stream *msg, uint32_t command)
 {
        struct stream *msgc;
-       size_t zmsglen, zhdrlen;
+       char buf[ZEBRA_MAX_PACKET_SIZ];
        pid_t ppid;
 
-       /*
-        * Don't modify message in the zebra API. In order to do that we
-        * need to allocate a new message stream and copy the message
-        * provided by zebra.
-        */
+       /* Create BFD header */
        msgc = stream_new(ZEBRA_MAX_PACKET_SIZ);
-       if (msgc == NULL) {
-               zlog_debug("%s: not enough memory", __func__);
-               return;
-       }
-
-       /* Calculate our header size plus the message contents. */
-       zhdrlen = ZEBRA_HEADER_SIZE + sizeof(uint32_t);
-       zmsglen = msg->endp - msg->getp;
-       memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen);
-
-       /*
-        * The message type will be BFD_DEST_REPLY so we can use only
-        * one callback at the `bfdd` side, however the real command
-        * number will be included right after the zebra header.
-        */
        zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id);
        stream_putl(msgc, command);
 
-       /* Update the data pointers. */
-       msgc->getp = 0;
-       msgc->endp = zhdrlen + zmsglen;
-       stream_putw_at(msgc, 0, stream_get_endp(msgc));
+       if (STREAM_READABLE(msg) > STREAM_WRITEABLE(msgc)) {
+               zlog_warn("Cannot fit extended BFD header plus original message contents into ZAPI packet; dropping message");
+               goto stream_failure;
+       }
+
+       /* Copy original message, excluding header, into new message */
+       stream_get_from(buf, msg, stream_get_getp(msg), STREAM_READABLE(msg));
+       stream_put(msgc, buf, STREAM_READABLE(msg));
+
+       /* Update length field */
+       stream_putw_at(msgc, 0, STREAM_READABLE(msgc));
 
        zebra_ptm_send_bfdd(msgc);
+       msgc = NULL;
 
        /* Registrate process PID for shutdown hook. */
        STREAM_GETL(msg, ppid);
@@ -1443,6 +1432,8 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf,
        return;
 
 stream_failure:
+       if (msgc)
+               stream_free(msgc);
        zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__);
 }
 
index 3f1567a95b724bf0aba5882afeacef40613f5471..618a232408ba92cb2dec0b88d0a36c54fd594a37 100644 (file)
@@ -259,7 +259,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw)
         * Need to ensure that there's a label binding for all nexthops.
         * Otherwise, ECMP for this route could render the pseudowire unusable.
         */
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                if (!nexthop->nh_label) {
                        if (IS_ZEBRA_DEBUG_PW)
                                zlog_debug("%s: unlabeled route for %s",
index 605311769dc0e41375c119aef31cf2224b61cfb7..051d7f523135888ddb331a6254c6a8d82e45d5d8 100644 (file)
@@ -193,144 +193,47 @@ int zebra_check_addr(const struct prefix *p)
        return 1;
 }
 
-/* Add nexthop to the end of a rib node's nexthop list */
-void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop)
-{
-       _nexthop_group_add_sorted(re->ng, nexthop);
-}
-
-
 /**
  * copy_nexthop - copy a nexthop to the rib structure.
  */
 void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh)
 {
-       assert(!re->ng->nexthop);
-       copy_nexthops(&re->ng->nexthop, nh, NULL);
-}
-
-/* Delete specified nexthop from the list. */
-void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop)
-{
-       if (nexthop->next)
-               nexthop->next->prev = nexthop->prev;
-       if (nexthop->prev)
-               nexthop->prev->next = nexthop->next;
-       else
-               re->ng->nexthop = nexthop->next;
+       assert(!re->nhe->nhg->nexthop);
+       copy_nexthops(&re->nhe->nhg->nexthop, nh, NULL);
 }
 
-
-struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
-                                               ifindex_t ifindex,
-                                               vrf_id_t nh_vrf_id)
+static void route_entry_attach_ref(struct route_entry *re,
+                                  struct nhg_hash_entry *new)
 {
-       struct nexthop *nexthop;
-
-       nexthop = nexthop_new();
-       nexthop->type = NEXTHOP_TYPE_IFINDEX;
-       nexthop->ifindex = ifindex;
-       nexthop->vrf_id = nh_vrf_id;
-
-       route_entry_nexthop_add(re, nexthop);
-
-       return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re,
-                                            struct in_addr *ipv4,
-                                            struct in_addr *src,
-                                            vrf_id_t nh_vrf_id)
-{
-       struct nexthop *nexthop;
-
-       nexthop = nexthop_new();
-       nexthop->type = NEXTHOP_TYPE_IPV4;
-       nexthop->vrf_id = nh_vrf_id;
-       nexthop->gate.ipv4 = *ipv4;
-       if (src)
-               nexthop->src.ipv4 = *src;
-
-       route_entry_nexthop_add(re, nexthop);
+       re->nhe = new;
+       re->nhe_id = new->id;
 
-       return nexthop;
+       zebra_nhg_increment_ref(new);
 }
 
-struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
-                                                    struct in_addr *ipv4,
-                                                    struct in_addr *src,
-                                                    ifindex_t ifindex,
-                                                    vrf_id_t nh_vrf_id)
+int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new)
 {
-       struct nexthop *nexthop;
-       struct interface *ifp;
-
-       nexthop = nexthop_new();
-       nexthop->vrf_id = nh_vrf_id;
-       nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
-       nexthop->gate.ipv4 = *ipv4;
-       if (src)
-               nexthop->src.ipv4 = *src;
-       nexthop->ifindex = ifindex;
-       ifp = if_lookup_by_index(nexthop->ifindex, nh_vrf_id);
-       /*Pending: need to think if null ifp here is ok during bootup?
-         There was a crash because ifp here was coming to be NULL */
-       if (ifp)
-               if (connected_is_unnumbered(ifp))
-                       SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
-
-       route_entry_nexthop_add(re, nexthop);
-
-       return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re,
-                                            struct in6_addr *ipv6,
-                                            vrf_id_t nh_vrf_id)
-{
-       struct nexthop *nexthop;
-
-       nexthop = nexthop_new();
-       nexthop->vrf_id = nh_vrf_id;
-       nexthop->type = NEXTHOP_TYPE_IPV6;
-       nexthop->gate.ipv6 = *ipv6;
-
-       route_entry_nexthop_add(re, nexthop);
-
-       return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
-                                                    struct in6_addr *ipv6,
-                                                    ifindex_t ifindex,
-                                                    vrf_id_t nh_vrf_id)
-{
-       struct nexthop *nexthop;
-
-       nexthop = nexthop_new();
-       nexthop->vrf_id = nh_vrf_id;
-       nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
-       nexthop->gate.ipv6 = *ipv6;
-       nexthop->ifindex = ifindex;
-
-       route_entry_nexthop_add(re, nexthop);
+       struct nhg_hash_entry *old = NULL;
+       int ret = 0;
 
-       return nexthop;
-}
+       if (new == NULL) {
+               re->nhe->nhg = NULL;
+               goto done;
+       }
 
-struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re,
-                                                 enum blackhole_type bh_type)
-{
-       struct nexthop *nexthop;
+       if (re->nhe_id != new->id) {
+               old = zebra_nhg_lookup_id(re->nhe_id);
 
-       nexthop = nexthop_new();
-       nexthop->vrf_id = VRF_DEFAULT;
-       nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
-       nexthop->bh_type = bh_type;
+               route_entry_attach_ref(re, new);
 
-       route_entry_nexthop_add(re, nexthop);
+               if (old)
+                       zebra_nhg_decrement_ref(old);
+       } else if (!re->nhe)
+               /* This is the first time it's being attached */
+               route_entry_attach_ref(re, new);
 
-       return nexthop;
+done:
+       return ret;
 }
 
 struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
@@ -501,7 +404,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re)
        if (re->type != ZEBRA_ROUTE_BGP)
                return 0;
 
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
                if (!nexthop->nh_label || !nexthop->nh_label->num_labels)
                        return 0;
 
@@ -525,7 +428,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
        srcdest_rnode_prefixes(rn, &p, &src_p);
 
        if (info->safi != SAFI_UNICAST) {
-               for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
                        SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
                return;
        }
@@ -603,7 +506,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
 
        if (info->safi != SAFI_UNICAST) {
                UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
-               for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
                return;
        }
@@ -663,7 +566,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
                        re->fib_ng.nexthop = NULL;
                }
 
-               for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
        }
 
@@ -839,7 +742,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
 
        /* Update real nexthop. This may actually determine if nexthop is active
         * or not. */
-       if (!nexthop_group_active_nexthop_num(new->ng)) {
+       if (!nexthop_group_active_nexthop_num(new->nhe->nhg)) {
                UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
                return;
        }
@@ -908,7 +811,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
 
                /* Update the nexthop; we could determine here that nexthop is
                 * inactive. */
-               if (nexthop_group_active_nexthop_num(new->ng))
+               if (nexthop_group_active_nexthop_num(new->nhe->nhg))
                        nh_active = 1;
 
                /* If nexthop is active, install the selected route, if
@@ -1026,7 +929,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
                /* both are connected.  are either loop or vrf? */
                struct nexthop *nexthop = NULL;
 
-               for (ALL_NEXTHOPS_PTR(alternate->ng, nexthop)) {
+               for (ALL_NEXTHOPS_PTR(alternate->nhe->nhg, nexthop)) {
                        struct interface *ifp = if_lookup_by_index(
                                nexthop->ifindex, alternate->vrf_id);
 
@@ -1034,7 +937,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
                                return alternate;
                }
 
-               for (ALL_NEXTHOPS_PTR(current->ng, nexthop)) {
+               for (ALL_NEXTHOPS_PTR(current->nhe->nhg, nexthop)) {
                        struct interface *ifp = if_lookup_by_index(
                                nexthop->ifindex, current->vrf_id);
 
@@ -1365,7 +1268,7 @@ static void zebra_rib_fixup_system(struct route_node *rn)
                SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
                UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
 
-               for (ALL_NEXTHOPS_PTR(re->ng, nhop)) {
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nhop)) {
                        if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
                                continue;
 
@@ -1466,15 +1369,23 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
 
        ctx_nexthop = dplane_ctx_get_ng(ctx)->nexthop;
 
+       /* Nothing installed - we can skip some of the checking/comparison
+        * of nexthops.
+        */
+       if (ctx_nexthop == NULL) {
+               changed_p = true;
+               goto no_nexthops;
+       }
+
        /* Get the first `installed` one to check against.
         * If the dataplane doesn't set these to be what was actually installed,
-        * it will just be whatever was in re->ng?
+        * it will just be whatever was in re->nhe->nhg?
         */
        if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE)
            || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop);
 
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
 
                if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
                        continue;
@@ -1528,6 +1439,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
                goto done;
        }
 
+no_nexthops:
+
        /* FIB nexthop set differs from the RIB set:
         * create a fib-specific nexthop-group
         */
@@ -1885,18 +1798,40 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
        /* Ensure we clear the QUEUED flag */
        UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
 
-       /* Is this a notification that ... matters? We only really care about
-        * the route that is currently selected for installation.
+       /* Is this a notification that ... matters? We mostly care about
+        * the route that is currently selected for installation; we may also
+        * get an un-install notification, and handle that too.
         */
        if (re != dest->selected_fib) {
-               /* TODO -- don't skip processing entirely? We might like to
-                * at least report on the event.
+               /*
+                * If we need to, clean up after a delete that was part of
+                * an update operation.
                 */
-               if (debug_p)
-                       zlog_debug("%u:%s dplane notif, but type %s not selected_fib",
-                                  dplane_ctx_get_vrf(ctx), dest_str,
-                                  zebra_route_string(
-                                          dplane_ctx_get_type(ctx)));
+               end_count = 0;
+               for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
+                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+                               end_count++;
+               }
+
+               /* If no nexthops or none installed, ensure that this re
+                * gets its 'installed' flag cleared.
+                */
+               if (end_count == 0) {
+                       if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))
+                               UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+                       if (debug_p)
+                               zlog_debug("%u:%s dplane notif, uninstalled type %s route",
+                                          dplane_ctx_get_vrf(ctx), dest_str,
+                                          zebra_route_string(
+                                                  dplane_ctx_get_type(ctx)));
+               } else {
+                       /* At least report on the event. */
+                       if (debug_p)
+                               zlog_debug("%u:%s dplane notif, but type %s not selected_fib",
+                                          dplane_ctx_get_vrf(ctx), dest_str,
+                                          zebra_route_string(
+                                                  dplane_ctx_get_type(ctx)));
+               }
                goto done;
        }
 
@@ -1905,9 +1840,12 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
         * and then again if there's been a change.
         */
        start_count = 0;
-       for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
-               if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
-                       start_count++;
+
+       if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
+               for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
+                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+                               start_count++;
+               }
        }
 
        /* Update zebra's nexthop FIB flags based on the context struct's
@@ -1917,10 +1855,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
 
        if (!fib_changed) {
                if (debug_p)
-                       zlog_debug("%u:%s No change from dplane notification",
+                       zlog_debug("%u:%s dplane notification: rib_update returns FALSE",
                                   dplane_ctx_get_vrf(ctx), dest_str);
-
-               goto done;
        }
 
        /*
@@ -1962,11 +1898,6 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
                /* Redistribute, lsp, and nht update */
                redistribute_update(dest_pfx, src_pfx, re, NULL);
 
-               zebra_rib_evaluate_rn_nexthops(
-                       rn, zebra_router_get_next_sequence());
-
-               zebra_rib_evaluate_mpls(rn);
-
        } else if (start_count > 0 && end_count == 0) {
                if (debug_p)
                        zlog_debug("%u:%s un-installed transition from dplane notification",
@@ -1985,12 +1916,13 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
 
                /* Redistribute, lsp, and nht update */
                redistribute_delete(dest_pfx, src_pfx, re, NULL);
+       }
 
-               zebra_rib_evaluate_rn_nexthops(
-                       rn, zebra_router_get_next_sequence());
+       /* Make any changes visible for lsp and nexthop-tracking processing */
+       zebra_rib_evaluate_rn_nexthops(
+               rn, zebra_router_get_next_sequence());
 
-               zebra_rib_evaluate_mpls(rn);
-       }
+       zebra_rib_evaluate_mpls(rn);
 
 done:
        if (rn)
@@ -2421,8 +2353,8 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
                nhe = zebra_nhg_lookup_id(re->nhe_id);
                if (nhe)
                        zebra_nhg_decrement_ref(nhe);
-       } else if (re->ng)
-               nexthop_group_delete(&re->ng);
+       } else if (re->nhe->nhg)
+               nexthop_group_delete(&re->nhe->nhg);
 
        nexthops_free(re->fib_ng.nexthop);
 
@@ -2489,10 +2421,10 @@ void _route_entry_dump(const char *func, union prefixconstptr pp,
                "%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u",
                straddr, re->metric, re->mtu, re->distance, re->flags, re->status);
        zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr,
-                  nexthop_group_nexthop_num(re->ng),
-                  nexthop_group_active_nexthop_num(re->ng));
+                  nexthop_group_nexthop_num(re->nhe->nhg),
+                  nexthop_group_active_nexthop_num(re->nhe->nhg));
 
-       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                struct interface *ifp;
                struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
 
@@ -2641,7 +2573,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
 }
 
 int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
-                     struct prefix_ipv6 *src_p, struct route_entry *re)
+                     struct prefix_ipv6 *src_p, struct route_entry *re,
+                     struct nexthop_group *ng)
 {
        struct nhg_hash_entry *nhe = NULL;
        struct route_table *table;
@@ -2658,8 +2591,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
        table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id,
                                                  re->table);
        if (!table) {
-               if (re->ng)
-                       nexthop_group_delete(&re->ng);
+               if (ng)
+                       nexthop_group_delete(&ng);
                XFREE(MTYPE_RE, re);
                return 0;
        }
@@ -2676,13 +2609,13 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
                        return -1;
                }
        } else {
-               nhe = zebra_nhg_rib_find(0, re->ng, afi);
+               nhe = zebra_nhg_rib_find(0, ng, afi);
 
                /*
                 * The nexthops got copied over into an nhe,
                 * so free them now.
                 */
-               nexthop_group_delete(&re->ng);
+               nexthop_group_delete(&ng);
 
                if (!nhe) {
                        char buf[PREFIX_STRLEN] = "";
@@ -2708,7 +2641,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
         * level protocols, as the refcnt might be wrong, since it checks
         * if old_id != new_id.
         */
-       zebra_nhg_re_update_ref(re, nhe);
+       route_entry_update_nhe(re, nhe);
 
        /* Make it sure prefixlen is applied to the prefix. */
        apply_mask(p);
@@ -2851,7 +2784,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
 
                if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric)
                        continue;
-               if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng->nexthop)
+               if (re->type == ZEBRA_ROUTE_CONNECT &&
+                   (rtnh = re->nhe->nhg->nexthop)
                    && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) {
                        if (rtnh->ifindex != nh->ifindex)
                                continue;
@@ -2869,7 +2803,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                        same = re;
                        break;
                }
-               for (ALL_NEXTHOPS_PTR(re->ng, rtnh)) {
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, rtnh)) {
                        /*
                         * No guarantee all kernel send nh with labels
                         * on delete.
@@ -2911,7 +2845,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                        if (allow_delete) {
                                UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED);
                                /* Unset flags. */
-                               for (rtnh = fib->ng->nexthop; rtnh;
+                               for (rtnh = fib->nhe->nhg->nexthop; rtnh;
                                     rtnh = rtnh->next)
                                        UNSET_FLAG(rtnh->flags,
                                                   NEXTHOP_FLAG_FIB);
@@ -2967,7 +2901,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
                        struct nexthop *tmp_nh;
 
-                       for (ALL_NEXTHOPS_PTR(re->ng, tmp_nh)) {
+                       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, tmp_nh)) {
                                struct ipaddr vtep_ip;
 
                                memset(&vtep_ip, 0, sizeof(struct ipaddr));
@@ -3007,6 +2941,7 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
 {
        struct route_entry *re = NULL;
        struct nexthop *nexthop = NULL;
+       struct nexthop_group *ng = NULL;
 
        /* Allocate new route_entry structure. */
        re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
@@ -3022,16 +2957,19 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
        re->tag = tag;
        re->nhe_id = nhe_id;
 
+       /* If the owner of the route supplies a shared nexthop-group id,
+        * we'll use that. Otherwise, pass the nexthop along directly.
+        */
        if (!nhe_id) {
-               re->ng = nexthop_group_new();
+               ng = nexthop_group_new();
 
                /* Add nexthop. */
                nexthop = nexthop_new();
                *nexthop = *nh;
-               route_entry_nexthop_add(re, nexthop);
+               nexthop_group_add_sorted(ng, nexthop);
        }
 
-       return rib_add_multipath(afi, safi, p, src_p, re);
+       return rib_add_multipath(afi, safi, p, src_p, re, ng);
 }
 
 static const char *rib_update_event2str(rib_update_event_t event)
@@ -3289,7 +3227,7 @@ void rib_sweep_table(struct route_table *table)
                         * this decision needs to be revisited
                         */
                        SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
-                       for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+                       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
                                SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
 
                        rib_uninstall_kernel(rn, re);
index 60e23cc4d480ea2ac026b9121fd5e3bfbcebaebb..2d9c83becb7b6ee65926ca8f23d54fd321170cac 100644 (file)
@@ -384,7 +384,7 @@ static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re)
        struct nexthop *nexthop;
 
        if (re) {
-               for (nexthop = re->ng->nexthop; nexthop;
+               for (nexthop = re->nhe->nhg->nexthop; nexthop;
                     nexthop = nexthop->next) {
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED);
                }
@@ -403,7 +403,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,
        route_map_result_t ret;
 
        if (prn && re) {
-               for (nexthop = re->ng->nexthop; nexthop;
+               for (nexthop = re->nhe->nhg->nexthop; nexthop;
                     nexthop = nexthop->next) {
                        ret = zebra_nht_route_map_check(
                                afi, proto, &prn->p, zvrf, re, nexthop);
@@ -688,7 +688,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
                        /* Just being SELECTED isn't quite enough - must
                         * have an installed nexthop to be useful.
                         */
-                       for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+                       for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                                if (rnh_nexthop_valid(re, nexthop))
                                        break;
                        }
@@ -707,7 +707,8 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,
                                        break;
                                if (re->type == ZEBRA_ROUTE_NHRP) {
 
-                                       for (nexthop = re->ng->nexthop; nexthop;
+                                       for (nexthop = re->nhe->nhg->nexthop;
+                                            nexthop;
                                             nexthop = nexthop->next)
                                                if (nexthop->type
                                                    == NEXTHOP_TYPE_IFINDEX)
@@ -940,7 +941,7 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re,
                return;
 
        /* free RE and nexthops */
-       nexthop_group_delete(&re->ng);
+       zebra_nhg_free(re->nhe);
        XFREE(MTYPE_RE, re);
 }
 
@@ -963,9 +964,11 @@ static void copy_state(struct rnh *rnh, struct route_entry *re,
        state->metric = re->metric;
        state->vrf_id = re->vrf_id;
        state->status = re->status;
-       state->ng = nexthop_group_new();
 
-       route_entry_copy_nexthops(state, re->ng->nexthop);
+       state->nhe = zebra_nhg_alloc();
+       state->nhe->nhg = nexthop_group_new();
+
+       nexthop_group_copy(state->nhe->nhg, re->nhe->nhg);
        rnh->state = state;
 }
 
@@ -983,11 +986,12 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)
        if (r1->metric != r2->metric)
                return 1;
 
-       if (nexthop_group_nexthop_num(r1->ng)
-           != nexthop_group_nexthop_num(r2->ng))
+       if (nexthop_group_nexthop_num(r1->nhe->nhg)
+           != nexthop_group_nexthop_num(r2->nhe->nhg))
                return 1;
 
-       if (nexthop_group_hash(r1->ng) != nexthop_group_hash(r2->ng))
+       if (nexthop_group_hash(r1->nhe->nhg) !=
+           nexthop_group_hash(r2->nhe->nhg))
                return 1;
 
        return 0;
@@ -1030,6 +1034,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                break;
        }
        if (re) {
+               struct zapi_nexthop znh;
+
                stream_putc(s, re->type);
                stream_putw(s, re->instance);
                stream_putc(s, re->distance);
@@ -1037,39 +1043,10 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                num = 0;
                nump = stream_get_endp(s);
                stream_putc(s, 0);
-               for (ALL_NEXTHOPS_PTR(re->ng, nh))
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nh))
                        if (rnh_nexthop_valid(re, nh)) {
-                               stream_putl(s, nh->vrf_id);
-                               stream_putc(s, nh->type);
-                               switch (nh->type) {
-                               case NEXTHOP_TYPE_IPV4:
-                               case NEXTHOP_TYPE_IPV4_IFINDEX:
-                                       stream_put_in_addr(s, &nh->gate.ipv4);
-                                       stream_putl(s, nh->ifindex);
-                                       break;
-                               case NEXTHOP_TYPE_IFINDEX:
-                                       stream_putl(s, nh->ifindex);
-                                       break;
-                               case NEXTHOP_TYPE_IPV6:
-                               case NEXTHOP_TYPE_IPV6_IFINDEX:
-                                       stream_put(s, &nh->gate.ipv6, 16);
-                                       stream_putl(s, nh->ifindex);
-                                       break;
-                               default:
-                                       /* do nothing */
-                                       break;
-                               }
-                               if (nh->nh_label) {
-                                       stream_putc(s,
-                                                   nh->nh_label->num_labels);
-                                       if (nh->nh_label->num_labels)
-                                               stream_put(
-                                                       s,
-                                                       &nh->nh_label->label[0],
-                                                       nh->nh_label->num_labels
-                                                               * sizeof(mpls_label_t));
-                               } else
-                                       stream_putc(s, 0);
+                               zapi_nexthop_from_nexthop(&znh, nh);
+                               zapi_nexthop_encode(s, &znh, 0 /* flags */);
                                num++;
                        }
                stream_putc_at(s, nump, num);
@@ -1137,7 +1114,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty)
        if (rnh->state) {
                vty_out(vty, " resolved via %s\n",
                        zebra_route_string(rnh->state->type));
-               for (nexthop = rnh->state->ng->nexthop; nexthop;
+               for (nexthop = rnh->state->nhe->nhg->nexthop; nexthop;
                     nexthop = nexthop->next)
                        print_nh(nexthop, vty);
        } else
index e573093b70d4c1b35d98d4a7c56907d105965e97..a891ffb76a2453c226039c7cb9e1f3a3ea3653c2 100644 (file)
@@ -29,7 +29,7 @@
 #include "zebra_pbr.h"
 #include "zebra_vxlan.h"
 #include "zebra_mlag.h"
-#include "zebra_nhg_private.h"
+#include "zebra_nhg.h"
 #include "debug.h"
 
 DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info")
@@ -223,7 +223,7 @@ void zebra_router_terminate(void)
        zebra_vxlan_disable();
        zebra_mlag_terminate();
 
-       hash_clean(zrouter.nhgs, zebra_nhg_free);
+       hash_clean(zrouter.nhgs, zebra_nhg_hash_free);
        hash_free(zrouter.nhgs);
        hash_clean(zrouter.nhgs_id, NULL);
        hash_free(zrouter.nhgs_id);
index 56c76643283f69cb2ddb8a4450ffa06fe675db5f..4c52651981b2ec74b533d2323f7879ae8efd9042 100644 (file)
@@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2,
                return;
        }
 
-       if (in_addr_cmp((uint8_t *)&(*re)->ng->nexthop->gate.ipv4,
-                       (uint8_t *)&re2->ng->nexthop->gate.ipv4)
+       if (in_addr_cmp((uint8_t *)&(*re)->nhe->nhg->nexthop->gate.ipv4,
+                       (uint8_t *)&re2->nhe->nhg->nexthop->gate.ipv4)
            <= 0)
                return;
 
@@ -371,9 +371,9 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
                        if (!in_addr_cmp(&(*np)->p.u.prefix,
                                         (uint8_t *)&dest)) {
                                RNODE_FOREACH_RE (*np, *re) {
-                                       if (!in_addr_cmp((uint8_t *)&(*re)
-                                                                ->ng->nexthop
-                                                                ->gate.ipv4,
+                                       if (!in_addr_cmp((uint8_t *)&(*re)->nhe
+                                                        ->nhg->nexthop
+                                                        ->gate.ipv4,
                                                         (uint8_t *)&nexthop))
                                                if (proto
                                                    == proto_trans((*re)->type))
@@ -406,8 +406,8 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
                                    || ((policy == policy2) && (proto < proto2))
                                    || ((policy == policy2) && (proto == proto2)
                                        && (in_addr_cmp(
-                                                   (uint8_t *)&re2->ng->nexthop
-                                                           ->gate.ipv4,
+                                                   (uint8_t *)&re2->nhe
+                                                   ->nhg->nexthop->gate.ipv4,
                                                    (uint8_t *)&nexthop)
                                            >= 0)))
                                        check_replace(np2, re2, np, re);
@@ -432,7 +432,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[],
        {
                struct nexthop *nexthop;
 
-               nexthop = (*re)->ng->nexthop;
+               nexthop = (*re)->nhe->nhg->nexthop;
                if (nexthop) {
                        pnt = (uint8_t *)&nexthop->gate.ipv4;
                        for (i = 0; i < 4; i++)
@@ -462,7 +462,7 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len,
        if (!np)
                return NULL;
 
-       nexthop = re->ng->nexthop;
+       nexthop = re->nhe->nhg->nexthop;
        if (!nexthop)
                return NULL;
 
index 8a7c7e359fe9fe9b9deebe0b98a101370cc93191..c8b96011dc3b5ed85219b968ea3b9c98202f61b4 100644 (file)
@@ -66,9 +66,10 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
 static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                                     int mcast, bool use_fib, bool show_ng);
 static void vty_show_ip_route_summary(struct vty *vty,
-                                     struct route_table *table);
+                                     struct route_table *table, bool use_json);
 static void vty_show_ip_route_summary_prefix(struct vty *vty,
-                                            struct route_table *table);
+                                            struct route_table *table,
+                                            bool use_json);
 
 DEFUN (ip_multicast_mode,
        ip_multicast_mode_cmd,
@@ -263,7 +264,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                if (show_ng)
                        vty_out(vty, "  Nexthop Group ID: %u\n", re->nhe_id);
 
-               for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+               for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
                        char addrstr[32];
 
                        vty_out(vty, "  %c%s",
@@ -380,6 +381,9 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                                                sizeof buf, 1));
                        }
 
+                       if (nexthop->weight)
+                               vty_out(vty, ", weight %u", nexthop->weight);
+
                        vty_out(vty, "\n");
                }
                vty_out(vty, "\n");
@@ -413,7 +417,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
        if (is_fib)
                nhg = rib_active_nhg(re);
        else
-               nhg = re->ng;
+               nhg = re->nhe->nhg;
 
        if (json) {
                json_route = json_object_new_object();
@@ -466,9 +470,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                json_object_int_add(json_route, "internalFlags",
                                    re->flags);
                json_object_int_add(json_route, "internalNextHopNum",
-                                   nexthop_group_nexthop_num(re->ng));
+                                   nexthop_group_nexthop_num(re->nhe->nhg));
                json_object_int_add(json_route, "internalNextHopActiveNum",
-                                   nexthop_group_active_nexthop_num(re->ng));
+                                   nexthop_group_active_nexthop_num(
+                                           re->nhe->nhg));
                if (uptime < ONE_DAY_SECOND)
                        sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
                                tm->tm_sec);
@@ -1239,6 +1244,9 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
                                               sizeof(buf), 1));
                }
 
+               if (nexthop->weight)
+                       vty_out(vty, ", weight %u", nexthop->weight);
+
                vty_out(vty, "\n");
        }
 
@@ -1343,9 +1351,10 @@ DEFPY (show_interface_nexthop_group,
 
 DEFPY (show_nexthop_group,
        show_nexthop_group_cmd,
-       "show nexthop-group <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>",
+       "show nexthop-group rib <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>",
        SHOW_STR
        "Show Nexthop Groups\n"
+       "RIB information\n"
        "Nexthop Group ID\n"
        IP_STR
        IP6_STR
@@ -1683,7 +1692,7 @@ DEFPY (show_route_detail,
 DEFPY (show_route_summary,
        show_route_summary_cmd,
        "show <ip$ipv4|ipv6$ipv6> route [vrf <NAME$vrf_name|all$vrf_all>] \
-            summary [table (1-4294967295)$table_id] [prefix$prefix]",
+            summary [table (1-4294967295)$table_id] [prefix$prefix] [json]",
        SHOW_STR
        IP_STR
        IP6_STR
@@ -1692,10 +1701,12 @@ DEFPY (show_route_summary,
        "Summary of all routes\n"
        "Table to display summary for\n"
        "The table number\n"
-       "Prefix routes\n")
+       "Prefix routes\n"
+       JSON_STR)
 {
        afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
        struct route_table *table;
+       bool uj = use_json(argc, argv);
 
        if (table_id == 0)
                table_id = RT_TABLE_MAIN;
@@ -1715,9 +1726,10 @@ DEFPY (show_route_summary,
                                continue;
 
                        if (prefix)
-                               vty_show_ip_route_summary_prefix(vty, table);
+                               vty_show_ip_route_summary_prefix(vty, table,
+                                                                uj);
                        else
-                               vty_show_ip_route_summary(vty, table);
+                               vty_show_ip_route_summary(vty, table, uj);
                }
        } else {
                vrf_id_t vrf_id = VRF_DEFAULT;
@@ -1731,16 +1743,16 @@ DEFPY (show_route_summary,
                        return CMD_SUCCESS;
 
                if (prefix)
-                       vty_show_ip_route_summary_prefix(vty, table);
+                       vty_show_ip_route_summary_prefix(vty, table, uj);
                else
-                       vty_show_ip_route_summary(vty, table);
+                       vty_show_ip_route_summary(vty, table, uj);
        }
 
        return CMD_SUCCESS;
 }
 
 static void vty_show_ip_route_summary(struct vty *vty,
-                                     struct route_table *table)
+                                     struct route_table *table, bool use_json)
 {
        struct route_node *rn;
        struct route_entry *re;
@@ -1750,9 +1762,19 @@ static void vty_show_ip_route_summary(struct vty *vty,
        uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
        uint32_t i;
        uint32_t is_ibgp;
+       json_object *json_route_summary = NULL;
+       json_object *json_route_routes = NULL;
 
        memset(&rib_cnt, 0, sizeof(rib_cnt));
        memset(&fib_cnt, 0, sizeof(fib_cnt));
+
+       if (use_json) {
+               json_route_summary = json_object_new_object();
+               json_route_routes = json_object_new_array();
+               json_object_object_add(json_route_summary, "routes",
+                                      json_route_routes);
+       }
+
        for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
                RNODE_FOREACH_RE (rn, re) {
                        is_ibgp = (re->type == ZEBRA_ROUTE_BGP
@@ -1774,30 +1796,93 @@ static void vty_show_ip_route_summary(struct vty *vty,
                        }
                }
 
-       vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source", "Routes",
-               "FIB", zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf));
+       if (!use_json)
+               vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source",
+                       "Routes", "FIB",
+                       zvrf_name(((rib_table_info_t *)route_table_get_info(
+                                          table))
+                                         ->zvrf));
 
        for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
                if ((rib_cnt[i] > 0) || (i == ZEBRA_ROUTE_BGP
                                         && rib_cnt[ZEBRA_ROUTE_IBGP] > 0)) {
                        if (i == ZEBRA_ROUTE_BGP) {
-                               vty_out(vty, "%-20s %-20d %-20d \n", "ebgp",
-                                       rib_cnt[ZEBRA_ROUTE_BGP],
-                                       fib_cnt[ZEBRA_ROUTE_BGP]);
-                               vty_out(vty, "%-20s %-20d %-20d \n", "ibgp",
-                                       rib_cnt[ZEBRA_ROUTE_IBGP],
-                                       fib_cnt[ZEBRA_ROUTE_IBGP]);
-                       } else
-                               vty_out(vty, "%-20s %-20d %-20d \n",
-                                       zebra_route_string(i), rib_cnt[i],
-                                       fib_cnt[i]);
+                               if (use_json) {
+                                       json_object *json_route_ebgp =
+                                               json_object_new_object();
+
+                                       json_object_int_add(
+                                               json_route_ebgp, "fib",
+                                               fib_cnt[ZEBRA_ROUTE_BGP]);
+                                       json_object_int_add(
+                                               json_route_ebgp, "rib",
+                                               rib_cnt[ZEBRA_ROUTE_BGP]);
+                                       json_object_string_add(json_route_ebgp,
+                                                              "type", "ebgp");
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_ebgp);
+
+                                       json_object *json_route_ibgp =
+                                               json_object_new_object();
+
+                                       json_object_int_add(
+                                               json_route_ibgp, "fib",
+                                               fib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_int_add(
+                                               json_route_ibgp, "rib",
+                                               rib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_string_add(json_route_ibgp,
+                                                              "type", "ibgp");
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_ibgp);
+                               } else {
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               "ebgp",
+                                               rib_cnt[ZEBRA_ROUTE_BGP],
+                                               fib_cnt[ZEBRA_ROUTE_BGP]);
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               "ibgp",
+                                               rib_cnt[ZEBRA_ROUTE_IBGP],
+                                               fib_cnt[ZEBRA_ROUTE_IBGP]);
+                               }
+                       } else {
+                               if (use_json) {
+                                       json_object *json_route_type =
+                                               json_object_new_object();
+
+                                       json_object_int_add(json_route_type,
+                                                           "fib", fib_cnt[i]);
+                                       json_object_int_add(json_route_type,
+                                                           "rib", rib_cnt[i]);
+                                       json_object_string_add(
+                                               json_route_type, "type",
+                                               zebra_route_string(i));
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_type);
+                               } else
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               zebra_route_string(i),
+                                               rib_cnt[i], fib_cnt[i]);
+                       }
                }
        }
 
-       vty_out(vty, "------\n");
-       vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
-               rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
-       vty_out(vty, "\n");
+       if (use_json) {
+               json_object_int_add(json_route_summary, "routesTotal",
+                                   rib_cnt[ZEBRA_ROUTE_TOTAL]);
+               json_object_int_add(json_route_summary, "routesTotalFib",
+                                   fib_cnt[ZEBRA_ROUTE_TOTAL]);
+
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               json_route_summary, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json_route_summary);
+       } else {
+               vty_out(vty, "------\n");
+               vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
+                       rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
+               vty_out(vty, "\n");
+       }
 }
 
 /*
@@ -1808,7 +1893,8 @@ 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)
+                                            struct route_table *table,
+                                            bool use_json)
 {
        struct route_node *rn;
        struct route_entry *re;
@@ -1819,9 +1905,19 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
        uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1];
        uint32_t i;
        int cnt;
+       json_object *json_route_summary = NULL;
+       json_object *json_route_routes = NULL;
 
        memset(&rib_cnt, 0, sizeof(rib_cnt));
        memset(&fib_cnt, 0, sizeof(fib_cnt));
+
+       if (use_json) {
+               json_route_summary = json_object_new_object();
+               json_route_routes = json_object_new_array();
+               json_object_object_add(json_route_summary, "prefixRoutes",
+                                      json_route_routes);
+       }
+
        for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
                RNODE_FOREACH_RE (rn, re) {
 
@@ -1833,7 +1929,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
                                fib_cnt[ZEBRA_ROUTE_TOTAL]++;
                                fib_cnt[re->type]++;
                        }
-                       for (nexthop = re->ng->nexthop; (!cnt && nexthop);
+                       for (nexthop = re->nhe->nhg->nexthop; (!cnt && nexthop);
                             nexthop = nexthop->next) {
                                cnt++;
                                rib_cnt[ZEBRA_ROUTE_TOTAL]++;
@@ -1848,32 +1944,96 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
                        }
                }
 
-       vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source",
-               "Prefix Routes", "FIB",
-               zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf));
+       if (!use_json)
+               vty_out(vty, "%-20s %-20s %s  (vrf %s)\n", "Route Source",
+                       "Prefix Routes", "FIB",
+                       zvrf_name(((rib_table_info_t *)route_table_get_info(
+                                          table))
+                                         ->zvrf));
 
        for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
                if (rib_cnt[i] > 0) {
                        if (i == ZEBRA_ROUTE_BGP) {
-                               vty_out(vty, "%-20s %-20d %-20d \n", "ebgp",
-                                       rib_cnt[ZEBRA_ROUTE_BGP]
-                                               - rib_cnt[ZEBRA_ROUTE_IBGP],
-                                       fib_cnt[ZEBRA_ROUTE_BGP]
-                                               - fib_cnt[ZEBRA_ROUTE_IBGP]);
-                               vty_out(vty, "%-20s %-20d %-20d \n", "ibgp",
-                                       rib_cnt[ZEBRA_ROUTE_IBGP],
-                                       fib_cnt[ZEBRA_ROUTE_IBGP]);
-                       } else
-                               vty_out(vty, "%-20s %-20d %-20d \n",
-                                       zebra_route_string(i), rib_cnt[i],
-                                       fib_cnt[i]);
+                               if (use_json) {
+                                       json_object *json_route_ebgp =
+                                               json_object_new_object();
+
+                                       json_object_int_add(
+                                               json_route_ebgp, "fib",
+                                               fib_cnt[ZEBRA_ROUTE_BGP]
+                                                       - fib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_int_add(
+                                               json_route_ebgp, "rib",
+                                               rib_cnt[ZEBRA_ROUTE_BGP]
+                                                       - rib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_string_add(json_route_ebgp,
+                                                              "type", "ebgp");
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_ebgp);
+
+                                       json_object *json_route_ibgp =
+                                               json_object_new_object();
+
+                                       json_object_int_add(
+                                               json_route_ibgp, "fib",
+                                               fib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_int_add(
+                                               json_route_ibgp, "rib",
+                                               rib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       json_object_string_add(json_route_ibgp,
+                                                              "type", "ibgp");
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_ibgp);
+                               } else {
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               "ebgp",
+                                               rib_cnt[ZEBRA_ROUTE_BGP]
+                                                       - rib_cnt[ZEBRA_ROUTE_IBGP],
+                                               fib_cnt[ZEBRA_ROUTE_BGP]
+                                                       - fib_cnt[ZEBRA_ROUTE_IBGP]);
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               "ibgp",
+                                               rib_cnt[ZEBRA_ROUTE_IBGP],
+                                               fib_cnt[ZEBRA_ROUTE_IBGP]);
+                               }
+                       } else {
+                               if (use_json) {
+                                       json_object *json_route_type =
+                                               json_object_new_object();
+
+                                       json_object_int_add(json_route_type,
+                                                           "fib", fib_cnt[i]);
+                                       json_object_int_add(json_route_type,
+                                                           "rib", rib_cnt[i]);
+                                       json_object_string_add(
+                                               json_route_type, "type",
+                                               zebra_route_string(i));
+                                       json_object_array_add(json_route_routes,
+                                                             json_route_type);
+                               } else
+                                       vty_out(vty, "%-20s %-20d %-20d \n",
+                                               zebra_route_string(i),
+                                               rib_cnt[i], fib_cnt[i]);
+                       }
                }
        }
 
-       vty_out(vty, "------\n");
-       vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
-               rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
-       vty_out(vty, "\n");
+       if (use_json) {
+               json_object_int_add(json_route_summary, "prefixRoutesTotal",
+                                   rib_cnt[ZEBRA_ROUTE_TOTAL]);
+               json_object_int_add(json_route_summary, "prefixRoutesTotalFib",
+                                   fib_cnt[ZEBRA_ROUTE_TOTAL]);
+
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               json_route_summary, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json_route_summary);
+       } else {
+               vty_out(vty, "------\n");
+               vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
+                       rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]);
+               vty_out(vty, "\n");
+       }
 }
 
 /*
index 086b13d67096e0d87ad761489fbed78e52d456e0..ffb2528a245716716984c08d78528142195c6ada 100644 (file)
@@ -7682,6 +7682,55 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
        return zvni_remote_neigh_update(zvni, 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)
+{
+       uint16_t l = 0;
+
+       /*
+        * Obtain each remote MACIP and process.
+        * Message contains VNI, followed by MAC followed by IP (if any)
+        * followed by remote VTEP IP.
+        */
+       memset(ip, 0, sizeof(*ip));
+       STREAM_GETL(s, *vni);
+       STREAM_GET(macaddr->octet, s, ETH_ALEN);
+       STREAM_GETL(s, *ipa_len);
+
+       if (*ipa_len) {
+               if (*ipa_len == IPV4_MAX_BYTELEN)
+                       ip->ipa_type = IPADDR_V4;
+               else if (*ipa_len == IPV6_MAX_BYTELEN)
+                       ip->ipa_type = IPADDR_V6;
+               else {
+                       if (IS_ZEBRA_DEBUG_VXLAN)
+                               zlog_debug(
+                                       "ipa_len *must* be %d or %d bytes in length not %d",
+                                       IPV4_MAX_BYTELEN, IPV6_MAX_BYTELEN,
+                                       *ipa_len);
+                       goto stream_failure;
+               }
+
+               STREAM_GET(&ip->ip.addr, s, *ipa_len);
+       }
+       l += 4 + ETH_ALEN + 4 + *ipa_len;
+       STREAM_GET(&vtep_ip->s_addr, s, IPV4_MAX_BYTELEN);
+       l += IPV4_MAX_BYTELEN;
+
+       if (add) {
+               STREAM_GETC(s, *flags);
+               STREAM_GETL(s, *seq);
+               l += 5;
+       }
+
+       return l;
+
+stream_failure:
+       return -1;
+}
 
 /*
  * Handle message from client to delete a remote MACIP for a VNI.
@@ -7704,23 +7753,14 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
        s = msg;
 
        while (l < hdr->length) {
-               /* Obtain each remote MACIP and process. */
-               /* Message contains VNI, followed by MAC followed by IP (if any)
-                * followed by remote VTEP IP.
-                */
-               memset(&ip, 0, sizeof(ip));
-               STREAM_GETL(s, vni);
-               STREAM_GET(&macaddr.octet, s, ETH_ALEN);
-               STREAM_GETL(s, ipa_len);
-               if (ipa_len) {
-                       ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4
-                                                                   : IPADDR_V6;
-                       STREAM_GET(&ip.ip.addr, s, ipa_len);
-               }
-               l += 4 + ETH_ALEN + 4 + ipa_len;
-               STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
-               l += IPV4_MAX_BYTELEN;
+               int res_length = zebra_vxlan_remote_macip_helper(
+                       false, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, NULL,
+                       NULL);
 
+               if (res_length == -1)
+                       goto stream_failure;
+
+               l += res_length;
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
                                "Recv MACIP DEL VNI %u MAC %s%s%s Remote VTEP %s from %s",
@@ -7769,29 +7809,14 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
        s = msg;
 
        while (l < hdr->length) {
-               /* Obtain each remote MACIP and process. */
-               /* Message contains VNI, followed by MAC followed by IP (if any)
-                * followed by remote VTEP IP.
-                */
-               memset(&ip, 0, sizeof(ip));
-               STREAM_GETL(s, vni);
-               STREAM_GET(&macaddr.octet, s, ETH_ALEN);
-               STREAM_GETL(s, ipa_len);
-               if (ipa_len) {
-                       ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4
-                                                                   : IPADDR_V6;
-                       STREAM_GET(&ip.ip.addr, s, ipa_len);
-               }
-               l += 4 + ETH_ALEN + 4 + ipa_len;
-               STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
-               l += IPV4_MAX_BYTELEN;
+               int res_length = zebra_vxlan_remote_macip_helper(
+                       true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip,
+                       &flags, &seq);
 
-               /* Get flags - sticky mac and/or gateway mac */
-               STREAM_GETC(s, flags);
-               l++;
-               STREAM_GETL(s, seq);
-               l += 4;
+               if (res_length == -1)
+                       goto stream_failure;
 
+               l += res_length;
                if (IS_ZEBRA_DEBUG_VXLAN)
                        zlog_debug(
                                "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s",
@@ -8599,9 +8624,8 @@ void zebra_vxlan_macvlan_down(struct interface *ifp)
                        struct interface *ifp;
 
                        ifp = if_lookup_by_index_all_vrf(zif->link_ifindex);
-                       zlog_debug("macvlan %s parent link is not found. Parent index %d ifp %s",
-                               ifp->name, zif->link_ifindex,
-                               ifp ? ifp->name : " ");
+                       zlog_debug("macvlan parent link is not found. Parent index %d ifp %s",
+                               zif->link_ifindex, ifp ? ifp->name : " ");
                }
                return;
        }
@@ -9462,7 +9486,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
 
        s = msg;
        STREAM_GETC(s, advertise);
-       vni = stream_get3(s);
+       STREAM_GET(&vni, s, 3);
 
        zvni = zvni_lookup(vni);
        if (!zvni)
index 989ea464e700685164abac0ef3677c5257d6ba21..100bb0e09318640d460107bcebf293479aa6ab82 100644 (file)
@@ -162,8 +162,7 @@ static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf,
        char *ptr;
 
        if (!buf)
-               ptr = (char *)XMALLOC(MTYPE_TMP,
-                                     ETHER_ADDR_STRLEN * sizeof(char));
+               ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char));
        else {
                assert(size >= ETHER_ADDR_STRLEN);
                ptr = buf;
@@ -200,8 +199,7 @@ static inline const char *zl3vni_sysmac2str(zebra_l3vni_t *zl3vni, char *buf,
        char *ptr;
 
        if (!buf)
-               ptr = (char *)XMALLOC(MTYPE_TMP,
-                                     ETHER_ADDR_STRLEN * sizeof(char));
+               ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char));
        else {
                assert(size >= ETHER_ADDR_STRLEN);
                ptr = buf;
index 419f30e6d34531ed2a04f35a16bace8752661104..cca926f3b060f4e0730856f75d4d1f1fc0779ad9 100644 (file)
@@ -591,8 +591,10 @@ static void zserv_client_free(struct zserv *client)
 
        /* Free bitmaps. */
        for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) {
-               for (int i = 0; i < ZEBRA_ROUTE_MAX; i++)
+               for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
                        vrf_bitmap_free(client->redist[afi][i]);
+                       redist_del_all_instances(&client->mi_redist[afi][i]);
+               }
 
                vrf_bitmap_free(client->redist_default[afi]);
        }
index ccc8d92aa2d6f5d311e2dcbdb0ec86d3d1fec119..d8d82a52ec47bab8765843ba94351734211fed23 100644 (file)
@@ -146,6 +146,7 @@ struct zserv {
        uint32_t v6_nh_watch_rem_cnt;
        uint32_t vxlan_sg_add_cnt;
        uint32_t vxlan_sg_del_cnt;
+       uint32_t error_cnt;
 
        time_t nh_reg_time;
        time_t nh_dereg_time;