summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--bfdd/bfd.h4
-rw-r--r--bfdd/bfd_packet.c6
-rw-r--r--bgpd/bgp_addpath.h2
-rw-r--r--bgpd/bgp_aspath.c2
-rw-r--r--bgpd/bgp_attr.c49
-rw-r--r--bgpd/bgp_attr.h6
-rw-r--r--bgpd/bgp_conditional_adv.c5
-rw-r--r--bgpd/bgp_damp.c6
-rw-r--r--bgpd/bgp_debug.c11
-rw-r--r--bgpd/bgp_debug.h3
-rw-r--r--bgpd/bgp_ecommunity.c5
-rw-r--r--bgpd/bgp_ecommunity.h8
-rw-r--r--bgpd/bgp_evpn.c330
-rw-r--r--bgpd/bgp_evpn.h8
-rw-r--r--bgpd/bgp_evpn_mh.c175
-rw-r--r--bgpd/bgp_evpn_mh.h10
-rw-r--r--bgpd/bgp_evpn_private.h3
-rw-r--r--bgpd/bgp_fsm.c18
-rw-r--r--bgpd/bgp_label.c2
-rw-r--r--bgpd/bgp_main.c2
-rw-r--r--bgpd/bgp_mplsvpn.c68
-rw-r--r--bgpd/bgp_mplsvpn.h10
-rw-r--r--bgpd/bgp_network.c4
-rw-r--r--bgpd/bgp_nexthop.c4
-rw-r--r--bgpd/bgp_nexthop.h1
-rw-r--r--bgpd/bgp_nht.c5
-rw-r--r--bgpd/bgp_open.c32
-rw-r--r--bgpd/bgp_packet.c24
-rw-r--r--bgpd/bgp_route.c691
-rw-r--r--bgpd/bgp_route.h20
-rw-r--r--bgpd/bgp_routemap.c7
-rw-r--r--bgpd/bgp_rpki.c24
-rw-r--r--bgpd/bgp_table.c4
-rw-r--r--bgpd/bgp_table.h9
-rw-r--r--bgpd/bgp_trace.c2
-rw-r--r--bgpd/bgp_updgrp.c33
-rw-r--r--bgpd/bgp_updgrp.h3
-rw-r--r--bgpd/bgp_updgrp_adv.c64
-rw-r--r--bgpd/bgp_vpn.c2
-rw-r--r--bgpd/bgp_vty.c408
-rw-r--r--bgpd/bgp_zebra.c300
-rw-r--r--bgpd/bgp_zebra.h13
-rw-r--r--bgpd/bgpd.c92
-rw-r--r--bgpd/bgpd.h13
-rw-r--r--bgpd/rfapi/rfapi.c6
-rw-r--r--configure.ac19
-rw-r--r--debian/changelog12
-rw-r--r--debian/control2
-rw-r--r--doc/developer/logging.rst3
-rw-r--r--doc/developer/northbound/advanced-topics.rst12
-rw-r--r--doc/developer/northbound/architecture.rst2
-rw-r--r--doc/developer/northbound/demos.rst20
-rw-r--r--doc/developer/northbound/links.rst8
-rw-r--r--doc/developer/northbound/operational-data-rpcs-and-notifications.rst50
-rw-r--r--doc/developer/topotests.rst54
-rw-r--r--doc/developer/zebra.rst15
-rw-r--r--doc/user/bgp.rst20
-rw-r--r--doc/user/installation.rst5
-rw-r--r--doc/user/isisd.rst16
-rw-r--r--doc/user/ospf6d.rst6
-rw-r--r--doc/user/ospf_fundamentals.rst10
-rw-r--r--doc/user/ospfd.rst32
-rw-r--r--doc/user/rpki.rst9
-rw-r--r--eigrpd/eigrp_main.c3
-rw-r--r--eigrpd/eigrp_pkt_tlv2.c0
-rw-r--r--eigrpd/eigrp_routemap.h2
-rw-r--r--isisd/isis_adjacency.c4
-rw-r--r--isisd/isis_lfa.c5
-rw-r--r--isisd/isis_pdu.c2
-rw-r--r--isisd/isis_spf.c2
-rw-r--r--isisd/isis_sr.c7
-rw-r--r--isisd/isis_sr.h2
-rw-r--r--isisd/isis_zebra.c2
-rw-r--r--lib/checksum.c2
-rw-r--r--lib/checksum.h2
-rw-r--r--lib/command.c2
-rw-r--r--lib/compiler.h6
-rw-r--r--lib/explicit_bzero.c2
-rw-r--r--lib/keychain_nb.c8
-rw-r--r--lib/lib_errors.c18
-rw-r--r--lib/lib_errors.h3
-rw-r--r--lib/libfrr.c18
-rw-r--r--lib/libfrr.h11
-rw-r--r--lib/libfrr_trace.c2
-rw-r--r--lib/libospf.h1
-rw-r--r--lib/mgmt_be_client.c4
-rw-r--r--lib/mgmt_fe_client.c54
-rw-r--r--lib/mgmt_fe_client.h45
-rw-r--r--lib/mgmt_msg_native.c2
-rw-r--r--lib/mgmt_msg_native.h63
-rw-r--r--lib/northbound.c219
-rw-r--r--lib/northbound.h44
-rw-r--r--lib/northbound_confd.c1494
-rw-r--r--lib/northbound_oper.c34
-rw-r--r--lib/plist.c30
-rw-r--r--lib/routing_nb.h2
-rw-r--r--lib/sigevent.c12
-rw-r--r--lib/srv6.c4
-rw-r--r--lib/stream.h2
-rw-r--r--lib/subdir.am12
-rw-r--r--lib/vrf.c51
-rw-r--r--lib/vrf.h6
-rw-r--r--lib/vty.c40
-rw-r--r--lib/vty.h4
-rw-r--r--lib/yang.c63
-rw-r--r--lib/yang.h16
-rw-r--r--lib/zclient.c1
-rw-r--r--mgmtd/mgmt_fe_adapter.c194
-rw-r--r--mgmtd/mgmt_fe_adapter.h20
-rw-r--r--mgmtd/mgmt_testc.c4
-rw-r--r--mgmtd/mgmt_txn.c96
-rw-r--r--mgmtd/mgmt_txn.h41
-rw-r--r--mgmtd/mgmt_vty.c59
-rw-r--r--nhrpd/debug.h2
-rw-r--r--nhrpd/os.h1
-rw-r--r--nhrpd/reqid.c2
-rw-r--r--nhrpd/vici.h1
-rw-r--r--ospf6d/ospf6_gr_helper.c9
-rw-r--r--ospf6d/ospf6_main.c3
-rw-r--r--ospfd/ospf_ase.c2
-rw-r--r--ospfd/ospf_flood.c5
-rw-r--r--ospfd/ospf_interface.c63
-rw-r--r--ospfd/ospf_interface.h11
-rw-r--r--ospfd/ospf_ism.c2
-rw-r--r--ospfd/ospf_neighbor.c4
-rw-r--r--ospfd/ospf_nsm.c4
-rw-r--r--ospfd/ospf_packet.c102
-rw-r--r--ospfd/ospf_sr.c4
-rw-r--r--ospfd/ospf_vty.c200
-rw-r--r--ospfd/ospfd.c7
-rw-r--r--pimd/pim_cmd.c15
-rw-r--r--pimd/pim_iface.c2
-rw-r--r--redhat/frr.spec.in18
-rw-r--r--snapcraft/snapcraft.yaml.in2
-rw-r--r--tests/lib/subdir.am2
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref6
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json45
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json44
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py6
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.py2
-rw-r--r--tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json1
-rw-r--r--tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json1
-rw-r--r--tests/topotests/bgp_dynamic_capability/r1/frr.conf1
-rw-r--r--tests/topotests/bgp_dynamic_capability/r2/frr.conf1
-rw-r--r--tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py160
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/__init__.py (renamed from eigrpd/eigrp_pkt_tlv1.c)0
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/c1/frr.conf4
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/c2/frr.conf4
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/r1/frr.conf30
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/r2/frr.conf25
-rw-r--r--tests/topotests/bgp_evpn_maximum_prefix/test_bgp_evpn_maximum_prefix.py92
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf1/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/leaf2/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/spine1/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/spine2/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/torm11/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/torm12/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/torm21/evpn.conf1
-rw-r--r--tests/topotests/bgp_evpn_mh/torm22/evpn.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py36
-rw-r--r--tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py34
-rw-r--r--tests/topotests/bgp_peer_group/r1/bgpd.conf4
-rw-r--r--tests/topotests/bgp_peer_group/r1/zebra.conf3
-rw-r--r--tests/topotests/bgp_peer_group/r2/bgpd.conf4
-rw-r--r--tests/topotests/bgp_peer_group/r2/zebra.conf3
-rw-r--r--tests/topotests/bgp_peer_group/test_bgp_peer-group.py49
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py77
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json1
-rw-r--r--tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json1
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json1
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json1
-rwxr-xr-xtests/topotests/conftest.py104
-rw-r--r--tests/topotests/evpn_pim_1/spine/bgp.summ.json1
-rw-r--r--tests/topotests/grpc_basic/test_basic_grpc.py2
-rw-r--r--tests/topotests/lib/common_config.py16
-rw-r--r--tests/topotests/lib/topotest.py36
-rw-r--r--tests/topotests/mgmt_tests/test_yang_mgmt.py239
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py2
-rw-r--r--tests/topotests/ospf_p2mp/r1/frr-p2mp-non-broadcast.conf26
-rw-r--r--tests/topotests/ospf_p2mp/r1/frr-p2mp.conf23
-rw-r--r--tests/topotests/ospf_p2mp/r2/frr-p2mp-non-broadcast.conf29
-rw-r--r--tests/topotests/ospf_p2mp/r2/frr-p2mp.conf26
-rw-r--r--tests/topotests/ospf_p2mp/r3/frr-p2mp-non-broadcast.conf28
-rw-r--r--tests/topotests/ospf_p2mp/r3/frr-p2mp.conf25
-rw-r--r--tests/topotests/ospf_p2mp/r4/frr-p2mp-non-broadcast.conf28
-rw-r--r--tests/topotests/ospf_p2mp/r4/frr-p2mp.conf25
-rw-r--r--tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py346
-rw-r--r--tests/topotests/ospf_p2mp/test_ospf_p2mp_non_broadcast.py467
-rw-r--r--tools/cocci.h2
-rwxr-xr-xtools/frr-reload.py21
-rwxr-xr-xtools/indent.py4
-rw-r--r--vtysh/vtysh.c167
-rw-r--r--vtysh/vtysh.h14
-rw-r--r--yang/confd/confd.frr-ripd.yang24
-rw-r--r--yang/confd/confd.frr-ripngd.yang24
-rw-r--r--yang/frr-zebra.yang6
-rw-r--r--yang/subdir.am27
-rw-r--r--zebra/fpm_listener.c95
-rw-r--r--zebra/interface.c7
-rw-r--r--zebra/main.c6
-rw-r--r--zebra/rt_netlink.c2
-rw-r--r--zebra/rtadv.c10
-rw-r--r--zebra/zebra_cli.c2
-rw-r--r--zebra/zebra_dplane.c15
-rw-r--r--zebra/zebra_dplane.h7
-rw-r--r--zebra/zebra_evpn.c6
-rw-r--r--zebra/zebra_evpn_mac.c2
-rw-r--r--zebra/zebra_evpn_mh.c6
-rw-r--r--zebra/zebra_fpm_netlink.c8
-rw-r--r--zebra/zebra_gr.c10
-rw-r--r--zebra/zebra_l2.c31
-rw-r--r--zebra/zebra_mpls.c28
-rw-r--r--zebra/zebra_mpls.h5
-rw-r--r--zebra/zebra_mpls_openbsd.c28
-rw-r--r--zebra/zebra_mpls_vty.c2
-rw-r--r--zebra/zebra_nb_config.c5
-rw-r--r--zebra/zebra_nb_state.c4
-rw-r--r--zebra/zebra_neigh.c12
-rw-r--r--zebra/zebra_netns_id.c4
-rw-r--r--zebra/zebra_netns_notify.c4
-rw-r--r--zebra/zebra_nhg.c4
-rw-r--r--zebra/zebra_routemap_nb_config.c2
-rw-r--r--zebra/zebra_trace.c2
-rw-r--r--zebra/zebra_vty.c150
-rw-r--r--zebra/zebra_vxlan.c9
-rw-r--r--zebra/zserv.c10
-rw-r--r--zebra/zserv.h1
234 files changed, 5529 insertions, 3311 deletions
diff --git a/.clang-format b/.clang-format
index d16263da2e..93ad18b98a 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# clang-format configuration file. Intended for clang-format >= 11.
+# If the version is changed also check that CI tool frrbot is updated.
#
# For more information, see:
#
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index 66bf706808..f4ff884e00 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -105,6 +105,8 @@ struct bfd_echo_pkt {
#define BFD_CBIT 0x08
#define BFD_ABIT 0x04
#define BFD_DEMANDBIT 0x02
+#define BFD_MBIT 0x01
+#define BFD_GETMBIT(flags) (flags & BFD_MBIT)
#define BFD_SETDEMANDBIT(flags, val) \
{ \
if ((val)) \
@@ -133,7 +135,7 @@ struct bfd_echo_pkt {
if ((val)) \
flags |= val; \
}
-#define BFD_GETCBIT(flags) (flags & BFD_FBIT)
+#define BFD_GETCBIT(flags) (flags & BFD_CBIT)
#define BFD_ECHO_VERSION 1
#define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt)
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index fec195c77e..8110f434c2 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -898,6 +898,12 @@ void bfd_recv_cb(struct event *t)
return;
}
+ if (BFD_GETMBIT(cp->flags)) {
+ cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
+ "detect non-zero Multipoint (M) flag");
+ return;
+ }
+
if (cp->discrs.my_discr == 0) {
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
"'my discriminator' is zero");
diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h
index b19e63c946..c267ebe43e 100644
--- a/bgpd/bgp_addpath.h
+++ b/bgpd/bgp_addpath.h
@@ -25,7 +25,7 @@ struct bgp_paths_limit_capability {
uint16_t afi;
uint8_t safi;
uint16_t paths_limit;
-};
+} __attribute__((packed));
#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
index bc7e8939b4..9dcd0ad1d6 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -2249,7 +2249,7 @@ void aspath_finish(void)
/* return and as path value */
const char *aspath_print(struct aspath *as)
{
- return (as ? as->str : NULL);
+ return as ? as->str : "(null)";
}
/* Printing functions */
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 0dec8ea4e4..e2321ad296 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -911,9 +911,16 @@ static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty)
vty_out(vty,
"\tflags: %" PRIu64
- " distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %pI6\n",
+ " distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %pI6 aigp_metric: %" PRIu64
+ "\n",
attr->flag, attr->distance, attr->med, attr->local_pref,
- attr->origin, attr->weight, attr->label, sid);
+ attr->origin, attr->weight, attr->label, sid, attr->aigp_metric);
+ vty_out(vty,
+ "\taspath: %s Community: %s Extended Community: %s Large Community: %s\n",
+ aspath_print(attr->aspath),
+ community_str(attr->community, false, false),
+ ecommunity_str(attr->ecommunity),
+ lcommunity_str(attr->lcommunity, false, false));
}
void attr_show_all(struct vty *vty)
@@ -1382,6 +1389,15 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
(args->startp - STREAM_DATA(BGP_INPUT(peer)))
+ args->total);
+ /* Partial optional attributes that are malformed should not cause
+ * the whole session to be reset. Instead treat it as a withdrawal
+ * of the routes, if possible.
+ */
+ if (CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS) &&
+ CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL) &&
+ CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL))
+ return BGP_ATTR_PARSE_WITHDRAW;
+
switch (args->type) {
/* where an attribute is relatively inconsequential, e.g. it does not
* affect route selection, and can be safely ignored, then any such
@@ -1391,6 +1407,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
case BGP_ATTR_AS4_AGGREGATOR:
case BGP_ATTR_AGGREGATOR:
case BGP_ATTR_ATOMIC_AGGREGATE:
+ case BGP_ATTR_PREFIX_SID:
return BGP_ATTR_PARSE_PROCEED;
/* Core attributes, particularly ones which may influence route
@@ -1418,19 +1435,21 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
BGP_NOTIFY_UPDATE_ERR, subcode,
notify_datap, length);
return BGP_ATTR_PARSE_ERROR;
+ default:
+ /* Unknown attributes, that are handled by this function
+ * should be treated as withdraw, to prevent one more CVE
+ * from being introduced.
+ * RFC 7606 says:
+ * The "treat-as-withdraw" approach is generally preferred
+ * and the "session reset" approach is discouraged.
+ */
+ flog_err(EC_BGP_ATTR_FLAG,
+ "%s(%u) attribute received, while it is not known how to handle it, treating as withdraw",
+ lookup_msg(attr_str, args->type, NULL), args->type);
+ break;
}
- /* Partial optional attributes that are malformed should not cause
- * the whole session to be reset. Instead treat it as a withdrawal
- * of the routes, if possible.
- */
- if (CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS)
- && CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)
- && CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL))
- return BGP_ATTR_PARSE_WITHDRAW;
-
- /* default to reset */
- return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ return BGP_ATTR_PARSE_WITHDRAW;
}
/* Find out what is wrong with the path attribute flag bits and log the error.
@@ -3156,8 +3175,6 @@ enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
struct attr *const attr = args->attr;
enum bgp_attr_parse_ret ret;
- attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
-
uint8_t type;
uint16_t length;
size_t headersz = sizeof(type) + sizeof(length);
@@ -3207,6 +3224,8 @@ enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
}
}
+ SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID));
+
return BGP_ATTR_PARSE_PROCEED;
}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 5386f24a0b..164aa5ae79 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -517,9 +517,11 @@ static inline void bgp_attr_set_ecommunity(struct attr *attr,
{
attr->ecommunity = ecomm;
- if (ecomm)
+ if (ecomm) {
SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
- else
+ if (ecommunity_select_color(ecomm))
+ SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR));
+ } else
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
}
diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c
index 6ed0dd797b..2d96b444b6 100644
--- a/bgpd/bgp_conditional_adv.c
+++ b/bgpd/bgp_conditional_adv.c
@@ -122,8 +122,9 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
if (update_type == UPDATE_TYPE_ADVERTISE &&
subgroup_announce_check(dest, pi, subgrp, dest_p,
&attr, &advmap_attr)) {
- bgp_adj_out_set_subgroup(dest, subgrp, &attr,
- pi);
+ if (!bgp_adj_out_set_subgroup(dest, subgrp,
+ &attr, pi))
+ bgp_attr_flush(&attr);
} else {
/* If default originate is enabled for
* the peer, do not send explicit
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index 80425ebe7a..6b6387b1b5 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -150,7 +150,7 @@ static void bgp_reuse_timer(struct event *t)
bgp_aggregate_increment(
bgp, bgp_dest_get_prefix(bdi->dest),
bdi->path, bdi->afi, bdi->safi);
- bgp_process(bgp, bdi->dest, bdi->afi,
+ bgp_process(bgp, bdi->dest, bdi->path, bdi->afi,
bdi->safi);
}
@@ -306,8 +306,10 @@ void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi,
bgp_path_info_unset_flag(bdi->dest, path,
BGP_PATH_HISTORY | BGP_PATH_DAMPED);
- if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+ if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw) {
bgp_path_info_delete(bdi->dest, path);
+ bgp_process(path->peer->bgp, bdi->dest, path, afi, safi);
+ }
XFREE(MTYPE_BGP_DAMP_INFO, bdi);
}
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index c1b06a0ae3..b4651ad0ba 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -50,7 +50,6 @@ unsigned long conf_bgp_debug_keepalive;
unsigned long conf_bgp_debug_update;
unsigned long conf_bgp_debug_bestpath;
unsigned long conf_bgp_debug_zebra;
-unsigned long conf_bgp_debug_allow_martians;
unsigned long conf_bgp_debug_nht;
unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
@@ -71,7 +70,6 @@ unsigned long term_bgp_debug_keepalive;
unsigned long term_bgp_debug_update;
unsigned long term_bgp_debug_bestpath;
unsigned long term_bgp_debug_zebra;
-unsigned long term_bgp_debug_allow_martians;
unsigned long term_bgp_debug_nht;
unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
@@ -2165,7 +2163,6 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(as4, AS4_SEGMENT);
TERM_DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
TERM_DEBUG_OFF(zebra, ZEBRA);
- TERM_DEBUG_OFF(allow_martians, ALLOW_MARTIANS);
TERM_DEBUG_OFF(nht, NHT);
TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
@@ -2241,9 +2238,6 @@ DEFUN_NOSH (show_debugging_bgp,
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
vty_out(vty, " BGP graceful-restart debugging is on\n");
- if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
- vty_out(vty, " BGP allow martian next hop debugging is on\n");
-
if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF))
vty_out(vty,
" BGP route leak from vrf to vpn debugging is on\n");
@@ -2356,11 +2350,6 @@ static int bgp_config_write_debug(struct vty *vty)
}
}
- if (CONF_BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) {
- vty_out(vty, "debug bgp allow-martians\n");
- write++;
- }
-
if (CONF_BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) {
vty_out(vty, "debug bgp vpn leak-from-vrf\n");
write++;
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index 5b09501852..ffe715a42f 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -61,7 +61,6 @@ extern unsigned long conf_bgp_debug_keepalive;
extern unsigned long conf_bgp_debug_update;
extern unsigned long conf_bgp_debug_bestpath;
extern unsigned long conf_bgp_debug_zebra;
-extern unsigned long conf_bgp_debug_allow_martians;
extern unsigned long conf_bgp_debug_nht;
extern unsigned long conf_bgp_debug_update_groups;
extern unsigned long conf_bgp_debug_vpn;
@@ -80,7 +79,6 @@ extern unsigned long term_bgp_debug_keepalive;
extern unsigned long term_bgp_debug_update;
extern unsigned long term_bgp_debug_bestpath;
extern unsigned long term_bgp_debug_zebra;
-extern unsigned long term_bgp_debug_allow_martians;
extern unsigned long term_bgp_debug_nht;
extern unsigned long term_bgp_debug_update_groups;
extern unsigned long term_bgp_debug_vpn;
@@ -120,7 +118,6 @@ struct bgp_debug_filter {
#define BGP_DEBUG_UPDATE_PREFIX 0x04
#define BGP_DEBUG_UPDATE_DETAIL 0x08
#define BGP_DEBUG_ZEBRA 0x01
-#define BGP_DEBUG_ALLOW_MARTIANS 0x01
#define BGP_DEBUG_NHT 0x01
#define BGP_DEBUG_UPDATE_GROUPS 0x01
#define BGP_DEBUG_VPN_LEAK_FROM_VRF 0x01
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 90c0123b93..e1b462ae56 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -263,8 +263,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
}
/* Return string representation of ecommunities attribute. */
-char *ecommunity_str(struct ecommunity *ecom)
+const char *ecommunity_str(struct ecommunity *ecom)
{
+ if (!ecom)
+ return "(null)";
+
if (!ecom->str)
ecom->str =
ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 62e0430c96..794c5a3975 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -52,10 +52,8 @@
* 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
*/
#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c
-/* from draft-ietf-idr-flow-spec-v6-09
- * 0x0b Flow-spec Redirect to IPv6
- */
-#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b
+/* RFC 8956 */
+#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0d
/* Low-order octet of the Extended Communities type field for EVPN types */
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
@@ -346,7 +344,7 @@ extern void ecommunity_strfree(char **s);
extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
extern bool ecommunity_match(const struct ecommunity *,
const struct ecommunity *);
-extern char *ecommunity_str(struct ecommunity *ecom);
+extern const char *ecommunity_str(struct ecommunity *ecom);
extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
uint8_t, uint8_t);
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index a846484f0e..1c3b4e05c6 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -892,11 +892,10 @@ struct bgp_dest *bgp_evpn_vni_node_lookup(const struct bgpevpn *vpn,
/*
* Add (update) or delete MACIP from zebra.
*/
-static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p,
- const struct ethaddr *mac,
- struct in_addr remote_vtep_ip, int add,
- uint8_t flags, uint32_t seq, esi_t *esi)
+static enum zclient_send_status bgp_zebra_send_remote_macip(
+ struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p,
+ const struct ethaddr *mac, struct in_addr remote_vtep_ip, int add,
+ uint8_t flags, uint32_t seq, esi_t *esi)
{
struct stream *s;
uint16_t ipa_len;
@@ -904,8 +903,12 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
bool esi_valid;
/* Check socket. */
- if (!zclient || zclient->sock < 0)
- return 0;
+ if (!zclient || zclient->sock < 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: No zclient or zclient->sock exists",
+ __func__);
+ return ZCLIENT_SEND_SUCCESS;
+ }
/* Don't try to register if Zebra doesn't know of this instance. */
if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
@@ -913,7 +916,7 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
zlog_debug(
"%s: No zebra instance to talk to, not installing remote macip",
__func__);
- return 0;
+ return ZCLIENT_SEND_SUCCESS;
}
if (!esi)
@@ -979,24 +982,26 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
frrtrace(5, frr_bgp, evpn_mac_ip_zsend, add, vpn, p, remote_vtep_ip,
esi);
- if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
- return -1;
-
- return 0;
+ return zclient_send_message(zclient);
}
/*
* Add (update) or delete remote VTEP from zebra.
*/
-static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p,
- int flood_control, int add)
+static enum zclient_send_status
+bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p, int flood_control,
+ int add)
{
struct stream *s;
/* Check socket. */
- if (!zclient || zclient->sock < 0)
- return 0;
+ if (!zclient || zclient->sock < 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: No zclient or zclient->sock exists",
+ __func__);
+ return ZCLIENT_SEND_SUCCESS;
+ }
/* Don't try to register if Zebra doesn't know of this instance. */
if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
@@ -1004,7 +1009,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
zlog_debug(
"%s: No zebra instance to talk to, not installing remote vtep",
__func__);
- return 0;
+ return ZCLIENT_SEND_SUCCESS;
}
s = zclient->obuf;
@@ -1021,7 +1026,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
EC_BGP_VTEP_INVALID,
"Bad remote IP when trying to %s remote VTEP for VNI %u",
add ? "ADD" : "DEL", (vpn ? vpn->vni : 0));
- return -1;
+ return ZCLIENT_SEND_FAILURE;
}
stream_putl(s, flood_control);
@@ -1034,10 +1039,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
frrtrace(3, frr_bgp, evpn_bum_vtep_zsend, add, vpn, p);
- if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE)
- return -1;
-
- return 0;
+ return zclient_send_message(zclient);
}
/*
@@ -1263,14 +1265,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
}
/* Install EVPN route into zebra. */
-static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p,
- struct bgp_path_info *pi)
+enum zclient_send_status evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *pi)
{
- int ret;
uint8_t flags;
int flood_control = VXLAN_FLOOD_DISABLED;
uint32_t seq;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
flags = 0;
@@ -1348,6 +1350,7 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
flood_control = VXLAN_FLOOD_DISABLED;
break;
}
+
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, flood_control, 1);
}
@@ -1355,11 +1358,13 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
}
/* Uninstall EVPN route from zebra. */
-static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p,
- struct bgp_path_info *pi, bool is_sync)
+enum zclient_send_status evpn_zebra_uninstall(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *pi,
+ bool is_sync)
{
- int ret;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
ret = bgp_zebra_send_remote_macip(
@@ -1374,7 +1379,7 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
ret = bgp_evpn_remote_es_evi_del(bgp, vpn, p);
else
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p,
- VXLAN_FLOOD_DISABLED, 0);
+ VXLAN_FLOOD_DISABLED, 0);
return ret;
}
@@ -1424,7 +1429,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
* this table.
*/
if (pi)
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -1438,14 +1443,29 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
* Note: vpn is NULL for local EAD-ES routes.
*/
int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
- struct bgp_dest *dest)
+ struct bgp_dest *dest, struct bgp_path_info *pi)
{
- struct bgp_path_info *old_select, *new_select;
+ struct bgp_path_info *old_select, *new_select, *first;
struct bgp_path_info_pair old_and_new;
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
int ret = 0;
+ first = bgp_dest_get_bgp_path_info(dest);
+ SET_FLAG(pi->flags, BGP_PATH_UNSORTED);
+ if (pi != first) {
+ if (pi->next)
+ pi->next->prev = pi->prev;
+ if (pi->prev)
+ pi->prev->next = pi->next;
+
+ if (first)
+ first->prev = pi;
+ pi->next = first;
+ pi->prev = NULL;
+ bgp_dest_set_bgp_path_info(dest, pi);
+ }
+
/* Compute the best path. */
bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new,
afi, safi);
@@ -1465,12 +1485,18 @@ int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
&& !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
&& !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
- if (bgp_zebra_has_route_changed(old_select))
- ret = evpn_zebra_install(
- bgp, vpn,
- (const struct prefix_evpn *)bgp_dest_get_prefix(
- dest),
- old_select);
+ if (bgp_zebra_has_route_changed(old_select)) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS))
+ evpn_zebra_install(bgp, vpn,
+ (const struct prefix_evpn *)
+ bgp_dest_get_prefix(
+ dest),
+ old_select);
+ else
+ bgp_zebra_route_install(dest, old_select, bgp,
+ true, vpn, false);
+ }
+
UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG);
bgp_zebra_clear_route_change_flags(dest);
@@ -1502,10 +1528,14 @@ int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
if (new_select && new_select->type == ZEBRA_ROUTE_BGP
&& (new_select->sub_type == BGP_ROUTE_IMPORTED ||
bgp_evpn_attr_is_sync(new_select->attr))) {
- ret = evpn_zebra_install(
- bgp, vpn,
- (struct prefix_evpn *)bgp_dest_get_prefix(dest),
- new_select);
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS))
+ evpn_zebra_install(bgp, vpn,
+ (const struct prefix_evpn *)
+ bgp_dest_get_prefix(dest),
+ new_select);
+ else
+ bgp_zebra_route_install(dest, new_select, bgp, true,
+ vpn, false);
/* If an old best existed and it was a "local" route, the only
* reason
@@ -1522,13 +1552,19 @@ int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
evpn_delete_old_local_route(bgp, vpn, dest,
old_select, new_select);
} else {
- if (old_select && old_select->type == ZEBRA_ROUTE_BGP
- && old_select->sub_type == BGP_ROUTE_IMPORTED)
- ret = evpn_zebra_uninstall(
- bgp, vpn,
- (const struct prefix_evpn *)bgp_dest_get_prefix(
- dest),
- old_select, false);
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP &&
+ old_select->sub_type == BGP_ROUTE_IMPORTED) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) ||
+ CHECK_FLAG(bgp->flags, BGP_FLAG_VNI_DOWN))
+ evpn_zebra_uninstall(bgp, vpn,
+ (const struct prefix_evpn *)
+ bgp_dest_get_prefix(
+ dest),
+ old_select, false);
+ else
+ bgp_zebra_route_install(dest, old_select, bgp,
+ false, vpn, false);
+ }
}
/* Clear any route change flags. */
@@ -1561,7 +1597,8 @@ static struct bgp_path_info *bgp_evpn_route_get_local_path(
static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
struct bgp *bgp_vrf, afi_t afi,
safi_t safi, struct bgp_dest *dest,
- struct attr *attr, int *route_changed)
+ struct attr *attr, int *route_changed,
+ struct bgp_path_info **entry)
{
struct attr *attr_new = NULL;
struct bgp_path_info *pi = NULL;
@@ -1599,8 +1636,8 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
/* add the route entry to route node*/
bgp_path_info_add(dest, pi);
+ *entry = pi;
} else {
-
tmp_pi = local_pi;
if (!attrhash_cmp(tmp_pi->attr, attr)) {
@@ -1622,6 +1659,7 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
tmp_pi->attr = attr_new;
tmp_pi->uptime = monotime(NULL);
}
+ *entry = local_pi;
}
return 0;
}
@@ -1637,6 +1675,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
struct bgp_dest *dest = NULL;
struct bgp *bgp_evpn = NULL;
int route_changed = 0;
+ struct bgp_path_info *pi = NULL;
bgp_evpn = bgp_get_evpn();
if (!bgp_evpn)
@@ -1718,11 +1757,11 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
/* create or update the route entry within the route node */
update_evpn_type5_route_entry(bgp_evpn, bgp_vrf, afi, safi, dest, &attr,
- &route_changed);
+ &route_changed, &pi);
/* schedule for processing and unlock node */
if (route_changed) {
- bgp_process(bgp_evpn, dest, afi, safi);
+ bgp_process(bgp_evpn, dest, pi, afi, safi);
bgp_dest_unlock_node(dest);
}
@@ -2062,9 +2101,19 @@ static void evpn_zebra_reinstall_best_route(struct bgp *bgp,
if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP
&& (curr_select->sub_type == BGP_ROUTE_IMPORTED ||
bgp_evpn_attr_is_sync(curr_select->attr)))
- evpn_zebra_install(bgp, vpn,
- (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
- curr_select);
+ if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP &&
+ (curr_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(curr_select->attr))) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS))
+ evpn_zebra_install(bgp, vpn,
+ (const struct prefix_evpn *)
+ bgp_dest_get_prefix(
+ dest),
+ curr_select);
+ else
+ bgp_zebra_route_install(dest, curr_select, bgp,
+ true, vpn, false);
+ }
}
/*
@@ -2222,7 +2271,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* route would win, and we should evict the defunct local route
* and (re)install the remote route into zebra.
*/
- evpn_route_select_install(bgp, vpn, dest);
+ evpn_route_select_install(bgp, vpn, dest, pi);
/*
* If the new local route was not selected evict it and tell zebra
* to re-add the best remote dest. BGP doesn't retain non-best local
@@ -2245,8 +2294,16 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* has been removed.
*/
new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
- if (!new_is_sync && old_is_sync)
- evpn_zebra_uninstall(bgp, vpn, p, pi, true);
+ if (!new_is_sync && old_is_sync) {
+ if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DELETE_IN_PROGRESS))
+ evpn_zebra_uninstall(bgp, vpn, p, pi,
+ true);
+ else
+ bgp_zebra_route_install(dest, pi, bgp,
+ false, vpn,
+ true);
+ }
}
}
bgp_path_info_unlock(pi);
@@ -2270,7 +2327,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
false /* setup_sync */, NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, global_pi, afi, safi);
bgp_dest_unlock_node(dest);
}
@@ -2330,7 +2387,7 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
delete_evpn_route_entry(bgp_evpn, afi, safi, dest, &pi);
if (pi)
- bgp_process(bgp_evpn, dest, afi, safi);
+ bgp_process(bgp_evpn, dest, pi, afi, safi);
bgp_dest_unlock_node(dest);
return 0;
}
@@ -2370,7 +2427,7 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* this table.
*/
if (pi)
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -2378,9 +2435,8 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
*/
delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
if (pi) {
- dest = bgp_path_info_reap(dest, pi);
- assert(dest);
- evpn_route_select_install(bgp, vpn, dest);
+ bgp_path_info_delete(dest, pi);
+ evpn_route_select_install(bgp, vpn, dest, pi);
}
/* dest should still exist due to locking make coverity happy */
@@ -2494,7 +2550,7 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* advertised to peers; otherwise, ensure it is evicted and
* (re)install the remote route into zebra.
*/
- evpn_route_select_install(bgp, vpn, dest);
+ evpn_route_select_install(bgp, vpn, dest, pi);
if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
route_change = 0;
@@ -2512,8 +2568,17 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* has been removed.
*/
new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
- if (!new_is_sync && old_is_sync)
- evpn_zebra_uninstall(bgp, vpn, &evp, pi, true);
+ if (!new_is_sync && old_is_sync) {
+ if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DELETE_IN_PROGRESS))
+ (void)evpn_zebra_uninstall(bgp, vpn,
+ &evp, pi,
+ true);
+ else
+ bgp_zebra_route_install(dest, pi, bgp,
+ false, vpn,
+ true);
+ }
}
}
@@ -2533,7 +2598,7 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, global_pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -2618,7 +2683,7 @@ static void delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
if (pi)
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
/* Unlock RD node. */
@@ -2795,7 +2860,22 @@ static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
delete_all_type2_routes(bgp, vpn);
build_evpn_type3_prefix(&p, vpn->originator_ip);
+
+ /*
+ * To handle the following scenario:
+ * - Say, the new zebra announce fifo list has few vni Evpn prefixes yet
+ * to be sent to zebra.
+ * - At this point if we have triggers like "no advertise-all-vni" or
+ * "networking restart", where a vni is going down.
+ *
+ * Perform the below
+ * 1) send withdraw routes to zebra immediately in case it is installed.
+ * 2) before we blow up the vni table, we need to walk the list and
+ * pop all the dest whose za_vpn points to this vni.
+ */
+ SET_FLAG(bgp->flags, BGP_FLAG_VNI_DOWN);
ret = delete_evpn_route(bgp, vpn, &p);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_VNI_DOWN);
if (ret)
return ret;
@@ -3072,7 +3152,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
safi);
/* Perform route selection and update zebra, if required. */
- bgp_process(bgp_vrf, dest, afi, safi);
+ bgp_process(bgp_vrf, dest, pi, afi, safi);
/* Process for route leaking. */
vpn_leak_from_vrf_update(bgp_get_default(), bgp_vrf, pi);
@@ -3184,7 +3264,7 @@ static int install_evpn_route_entry_in_vni_common(
bgp_evpn_remote_ip_hash_add(vpn, pi);
/* Perform route selection and update zebra, if required. */
- ret = evpn_route_select_install(bgp, vpn, dest);
+ ret = evpn_route_select_install(bgp, vpn, dest, pi);
/* if the best path is a local path with a non-zero ES
* sync info against the local path may need to be updated
@@ -3226,7 +3306,7 @@ static int uninstall_evpn_route_entry_in_vni_common(
bgp_path_info_delete(dest, pi);
/* Perform route selection and update zebra, if required. */
- ret = evpn_route_select_install(bgp, vpn, dest);
+ ret = evpn_route_select_install(bgp, vpn, dest, pi);
/* if the best path is a local path with a non-zero ES
* sync info against the local path may need to be updated
@@ -3434,7 +3514,7 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
bgp_evpn_path_nh_del(bgp_vrf, pi);
/* Perform route selection and update zebra, if required. */
- bgp_process(bgp_vrf, dest, afi, safi);
+ bgp_process(bgp_vrf, dest, pi, afi, safi);
/* Unlock route node. */
bgp_dest_unlock_node(dest);
@@ -3799,7 +3879,7 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
* Install or uninstall mac-ip routes are appropriate for this
* particular VRF.
*/
-static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
+static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, bool install)
{
afi_t afi;
safi_t safi;
@@ -3863,9 +3943,7 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
* particular VNI.
*/
static int install_uninstall_routes_for_vni(struct bgp *bgp,
- struct bgpevpn *vpn,
- bgp_evpn_route_type rtype,
- int install)
+ struct bgpevpn *vpn, bool install)
{
afi_t afi;
safi_t safi;
@@ -3896,7 +3974,9 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
(const struct prefix_evpn *)bgp_dest_get_prefix(
dest);
- if (evp->prefix.route_type != rtype)
+ if (evp->prefix.route_type != BGP_EVPN_IMET_ROUTE &&
+ evp->prefix.route_type != BGP_EVPN_AD_ROUTE &&
+ evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
@@ -3923,16 +4003,16 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
bgp, vpn, evp, pi);
if (ret) {
- flog_err(
- EC_BGP_EVPN_FAIL,
- "%u: Failed to %s EVPN %s route in VNI %u",
- bgp->vrf_id,
- install ? "install"
- : "uninstall",
- rtype == BGP_EVPN_MAC_IP_ROUTE
- ? "MACIP"
- : "IMET",
- vpn->vni);
+ flog_err(EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id,
+ install ? "install"
+ : "uninstall",
+ evp->prefix.route_type ==
+ BGP_EVPN_MAC_IP_ROUTE
+ ? "MACIP"
+ : "IMET",
+ vpn->vni);
bgp_dest_unlock_node(rd_dest);
bgp_dest_unlock_node(dest);
@@ -3950,7 +4030,7 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
*/
static int install_routes_for_vrf(struct bgp *bgp_vrf)
{
- install_uninstall_routes_for_vrf(bgp_vrf, 1);
+ install_uninstall_routes_for_vrf(bgp_vrf, true);
return 0;
}
@@ -3961,29 +4041,17 @@ static int install_routes_for_vrf(struct bgp *bgp_vrf)
*/
static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
{
- int ret;
-
- /* Install type-3 routes followed by type-2 routes - the ones applicable
+ /*
+ * Install type-3 routes followed by type-2 routes - the ones applicable
* for this VNI.
*/
- ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
- 1);
- if (ret)
- return ret;
-
- ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
- 1);
- if (ret)
- return ret;
-
- return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE,
- 1);
+ return install_uninstall_routes_for_vni(bgp, vpn, true);
}
/* uninstall routes from l3vni vrf. */
static int uninstall_routes_for_vrf(struct bgp *bgp_vrf)
{
- install_uninstall_routes_for_vrf(bgp_vrf, 0);
+ install_uninstall_routes_for_vrf(bgp_vrf, false);
return 0;
}
@@ -3993,25 +4061,11 @@ static int uninstall_routes_for_vrf(struct bgp *bgp_vrf)
*/
static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
{
- int ret;
-
- /* Uninstall type-2 routes followed by type-3 routes - the ones
- * applicable
- * for this VNI.
+ /*
+ * Uninstall type-2 routes followed by type-3 routes - the ones
+ * applicable for this VNI.
*/
- ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE,
- 0);
- if (ret)
- return ret;
-
- ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
- 0);
- if (ret)
- return ret;
-
-
- return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
- 0);
+ return install_uninstall_routes_for_vni(bgp, vpn, false);
}
/*
@@ -4431,7 +4485,7 @@ static void update_advertise_vni_route(struct bgp *bgp, struct bgpevpn *vpn,
}
/* Schedule for processing and unlock node. */
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, global_pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -4481,7 +4535,7 @@ static void update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
false /* setup_sync */, NULL /* old_is_sync */);
/* Schedule for processing and unlock node. */
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -4526,7 +4580,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
* this table.
*/
if (pi)
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -6262,6 +6316,17 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ struct bgp_dest *dest = NULL;
+
+ while (zebra_announce_count(&bm->zebra_announce_head)) {
+ dest = zebra_announce_pop(&bm->zebra_announce_head);
+ if (dest->za_vpn == vpn) {
+ bgp_path_info_unlock(dest->za_bgp_pi);
+ bgp_dest_unlock_node(dest);
+ } else
+ zebra_announce_add_tail(&bm->zebra_announce_head, dest);
+ }
+
bgp_evpn_remote_ip_hash_destroy(vpn);
bgp_evpn_vni_es_cleanup(vpn);
bgpevpn_unlink_from_l3vni(vpn);
@@ -6425,9 +6490,10 @@ void bgp_filter_evpn_routes_upon_martian_change(
for (dest = bgp_table_top(table); dest;
dest = bgp_route_next(dest)) {
+ struct bgp_path_info *next;
- for (pi = bgp_dest_get_bgp_path_info(dest); pi;
- pi = pi->next) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (next = pi->next, 1); pi = next) {
bool affected = false;
const struct prefix *p;
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index c641a64f62..11a6f45dd0 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -186,4 +186,12 @@ extern bool is_route_injectable_into_evpn_non_supp(struct bgp_path_info *pi);
extern void bgp_aggr_supp_withdraw_from_evpn(struct bgp *bgp, afi_t afi,
safi_t safi);
+extern enum zclient_send_status evpn_zebra_install(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *pi);
+extern enum zclient_send_status
+evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p, struct bgp_path_info *pi,
+ bool is_sync);
#endif /* _QUAGGA_BGP_EVPN_H */
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index d88c52d1f6..d63e011560 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -45,13 +45,14 @@ static void bgp_evpn_local_es_down(struct bgp *bgp,
struct bgp_evpn_es *es);
static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp,
struct bgp_evpn_es *es);
-static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
+static struct bgp_evpn_es_vtep *
+bgp_evpn_es_vtep_add(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct in_addr vtep_ip, bool esr, uint8_t df_alg,
+ uint16_t df_pref, int *zret);
+static enum zclient_send_status bgp_evpn_es_vtep_del(struct bgp *bgp,
struct bgp_evpn_es *es,
struct in_addr vtep_ip,
- bool esr, uint8_t df_alg,
- uint16_t df_pref);
-static void bgp_evpn_es_vtep_del(struct bgp *bgp,
- struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr);
+ bool esr);
static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es);
static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es);
static struct bgp_evpn_es_evi *
@@ -91,15 +92,19 @@ static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info);
*/
static int bgp_evpn_es_route_select_install(struct bgp *bgp,
struct bgp_evpn_es *es,
- struct bgp_dest *dest)
+ struct bgp_dest *dest,
+ struct bgp_path_info *pi)
{
int ret = 0;
+ int zret = 0;
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
struct bgp_path_info *old_select; /* old best */
struct bgp_path_info *new_select; /* new best */
struct bgp_path_info_pair old_and_new;
+ SET_FLAG(pi->flags, BGP_PATH_UNSORTED);
+
/* Compute the best path. */
bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new,
afi, safi);
@@ -120,7 +125,7 @@ static int bgp_evpn_es_route_select_install(struct bgp *bgp,
bgp_evpn_es_vtep_add(bgp, es, old_select->attr->nexthop,
true /*esr*/,
old_select->attr->df_alg,
- old_select->attr->df_pref);
+ old_select->attr->df_pref, &zret);
}
UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
bgp_zebra_clear_route_change_flags(dest);
@@ -149,7 +154,7 @@ static int bgp_evpn_es_route_select_install(struct bgp *bgp,
&& new_select->sub_type == BGP_ROUTE_IMPORTED) {
bgp_evpn_es_vtep_add(bgp, es, new_select->attr->nexthop,
true /*esr */, new_select->attr->df_alg,
- new_select->attr->df_pref);
+ new_select->attr->df_pref, &zret);
} else {
if (old_select && old_select->type == ZEBRA_ROUTE_BGP
&& old_select->sub_type == BGP_ROUTE_IMPORTED)
@@ -231,7 +236,7 @@ static int bgp_evpn_es_route_install(struct bgp *bgp,
}
/* Perform route selection and update zebra, if required. */
- ret = bgp_evpn_es_route_select_install(bgp, es, dest);
+ ret = bgp_evpn_es_route_select_install(bgp, es, dest, pi);
bgp_dest_unlock_node(dest);
@@ -272,7 +277,7 @@ static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
bgp_path_info_delete(dest, pi);
/* Perform route selection and update zebra, if required. */
- ret = bgp_evpn_es_route_select_install(bgp, es, dest);
+ ret = bgp_evpn_es_route_select_install(bgp, es, dest, pi);
/* Unlock route node. */
bgp_dest_unlock_node(dest);
@@ -447,7 +452,7 @@ int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
attr->mp_nexthop_global_in);
}
- /* Return back the route entry. */
+ /* Return back th*e route entry. */
*ri = tmp_pi;
return 0;
}
@@ -511,7 +516,7 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es,
* this table.
*/
if (pi)
- bgp_process(bgp, global_dest, afi, safi);
+ bgp_process(bgp, global_dest, pi, afi, safi);
bgp_dest_unlock_node(global_dest);
}
@@ -562,7 +567,7 @@ int delete_global_ead_evi_routes(struct bgp *bgp, struct bgpevpn *vpn)
delete_evpn_route_entry(bgp, afi, safi, bd, &pi);
if (pi)
- bgp_process(bgp, bd, afi, safi);
+ bgp_process(bgp, bd, pi, afi, safi);
}
}
@@ -668,7 +673,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp,
* this is just to set the flags correctly
* as local route in the ES always wins.
*/
- bgp_evpn_es_route_select_install(bgp, es, dest);
+ bgp_evpn_es_route_select_install(bgp, es, dest, pi);
bgp_dest_unlock_node(dest);
/* If this is a new route or some attribute has changed, export the
@@ -686,7 +691,7 @@ static int bgp_evpn_type4_route_update(struct bgp *bgp,
attr_new, &global_pi, &route_changed);
/* Schedule for processing and unlock node. */
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, global_pi, afi, safi);
bgp_dest_unlock_node(dest);
}
@@ -1008,7 +1013,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
* this is just to set the flags correctly as local route in
* the ES always wins.
*/
- evpn_route_select_install(bgp, vpn, dest);
+ evpn_route_select_install(bgp, vpn, dest, pi);
bgp_dest_unlock_node(dest);
/* If this is a new route or some attribute has changed, export the
@@ -1025,7 +1030,7 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
attr_new, &global_pi, &route_changed);
/* Schedule for processing and unlock node. */
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, global_pi, afi, safi);
bgp_dest_unlock_node(dest);
}
@@ -1371,23 +1376,28 @@ static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_find(struct bgp_evpn_es *es,
}
/* Send the remote ES to zebra for NHG programming */
-static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp,
- struct bgp_evpn_es_vtep *es_vtep, bool add)
+static enum zclient_send_status
+bgp_zebra_send_remote_es_vtep(struct bgp *bgp, struct bgp_evpn_es_vtep *es_vtep,
+ bool add)
{
struct bgp_evpn_es *es = es_vtep->es;
struct stream *s;
uint32_t flags = 0;
/* Check socket. */
- if (!zclient || zclient->sock < 0)
- return 0;
+ if (!zclient || zclient->sock < 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: No zclient or zclient->sock exists",
+ __func__);
+ return ZCLIENT_SEND_SUCCESS;
+ }
/* Don't try to register if Zebra doesn't know of this instance. */
if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("No zebra instance, not installing remote es %s",
es->esi_str);
- return 0;
+ return ZCLIENT_SEND_SUCCESS;
}
if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
@@ -1418,12 +1428,12 @@ static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp,
return zclient_send_message(zclient);
}
-static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
- struct bgp_evpn_es_vtep *es_vtep,
- bool param_change)
+static enum zclient_send_status bgp_evpn_es_vtep_re_eval_active(
+ struct bgp *bgp, struct bgp_evpn_es_vtep *es_vtep, bool param_change)
{
bool old_active;
bool new_active;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
old_active = CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
/* currently we need an active EVI reference to use the VTEP as
@@ -1445,7 +1455,7 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
es_vtep->df_alg, es_vtep->df_pref);
/* send remote ES to zebra */
- bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active);
+ ret = bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active);
/* The NHG is updated first for efficient failover handling.
* Note the NHG can be de-activated while there are bgp
@@ -1457,13 +1467,14 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
/* queue up the es for background consistency checks */
bgp_evpn_es_cons_checks_pend_add(es_vtep->es);
}
+
+ return ret;
}
-static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
- struct bgp_evpn_es *es,
- struct in_addr vtep_ip,
- bool esr, uint8_t df_alg,
- uint16_t df_pref)
+static struct bgp_evpn_es_vtep *
+bgp_evpn_es_vtep_add(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct in_addr vtep_ip, bool esr, uint8_t df_alg,
+ uint16_t df_pref, int *zret)
{
struct bgp_evpn_es_vtep *es_vtep;
bool param_change = false;
@@ -1490,15 +1501,17 @@ static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
++es_vtep->evi_cnt;
}
- bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
+ *zret = bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
return es_vtep;
}
-static void bgp_evpn_es_vtep_do_del(struct bgp *bgp,
- struct bgp_evpn_es_vtep *es_vtep, bool esr)
+static enum zclient_send_status
+bgp_evpn_es_vtep_do_del(struct bgp *bgp, struct bgp_evpn_es_vtep *es_vtep,
+ bool esr)
{
bool param_change = false;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug("es %s vtep %pI4 del %s", es_vtep->es->esi_str,
@@ -1515,18 +1528,25 @@ static void bgp_evpn_es_vtep_do_del(struct bgp *bgp,
--es_vtep->evi_cnt;
}
- bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
+ ret = bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
bgp_evpn_es_vtep_free(es_vtep);
+
+ return ret;
}
-static void bgp_evpn_es_vtep_del(struct bgp *bgp,
- struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr)
+static enum zclient_send_status bgp_evpn_es_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct in_addr vtep_ip,
+ bool esr)
{
struct bgp_evpn_es_vtep *es_vtep;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip);
if (es_vtep)
- bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
+ ret = bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
+
+ return ret;
}
/********************** ES MAC-IP paths *************************************
@@ -3399,12 +3419,14 @@ static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_find(
/* A VTEP can be added as "active" attach to an ES if EAD-per-ES and
* EAD-per-EVI routes are rxed from it.
*/
-static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
- struct bgp_evpn_es_evi_vtep *evi_vtep)
+static enum zclient_send_status
+bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
{
bool old_active;
bool new_active;
uint32_t ead_activity_flags;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
old_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
@@ -3425,7 +3447,7 @@ static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
new_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
if (old_active == new_active)
- return;
+ return ret;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug("es %s evi %u vtep %pI4 %s",
@@ -3434,24 +3456,27 @@ static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
new_active ? "active" : "inactive");
/* add VTEP to parent es */
- if (new_active)
- evi_vtep->es_vtep = bgp_evpn_es_vtep_add(
- bgp, evi_vtep->es_evi->es, evi_vtep->vtep_ip,
- false /*esr*/, 0, 0);
- else {
+ if (new_active) {
+ evi_vtep->es_vtep =
+ bgp_evpn_es_vtep_add(bgp, evi_vtep->es_evi->es,
+ evi_vtep->vtep_ip, false /*esr*/,
+ 0, 0, &ret);
+ } else {
if (evi_vtep->es_vtep) {
- bgp_evpn_es_vtep_do_del(bgp, evi_vtep->es_vtep,
- false /*esr*/);
+ ret = bgp_evpn_es_vtep_do_del(bgp, evi_vtep->es_vtep,
+ false /*esr*/);
evi_vtep->es_vtep = NULL;
}
}
/* queue up the parent es for background consistency checks */
bgp_evpn_es_cons_checks_pend_add(evi_vtep->es_evi->es);
+
+ return ret;
}
-static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp,
- struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
- bool ead_es)
+static enum zclient_send_status
+bgp_evpn_es_evi_vtep_add(struct bgp *bgp, struct bgp_evpn_es_evi *es_evi,
+ struct in_addr vtep_ip, bool ead_es)
{
struct bgp_evpn_es_evi_vtep *evi_vtep;
@@ -3475,18 +3500,19 @@ static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp,
else
SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
- bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ return bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
}
-static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp,
- struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
- bool ead_es)
+static enum zclient_send_status
+bgp_evpn_es_evi_vtep_del(struct bgp *bgp, struct bgp_evpn_es_evi *es_evi,
+ struct in_addr vtep_ip, bool ead_es)
{
struct bgp_evpn_es_evi_vtep *evi_vtep;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip);
if (!evi_vtep)
- return;
+ return ret;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug("del es %s evi %u vtep %pI4 %s",
@@ -3503,8 +3529,10 @@ static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp,
else
UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
- bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ ret = bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
bgp_evpn_es_evi_vtep_free(evi_vtep);
+
+ return ret;
}
/* compare ES-IDs for the ES-EVI RB tree maintained per-VNI */
@@ -3780,18 +3808,20 @@ int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni)
/* Add remote ES-EVI entry. This is actually the remote VTEP add and the
* ES-EVI is implicity created on first VTEP's reference.
*/
-int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p)
+enum zclient_send_status bgp_evpn_remote_es_evi_add(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
{
char buf[ESI_STR_LEN];
struct bgp_evpn_es *es;
struct bgp_evpn_es_evi *es_evi;
bool ead_es;
const esi_t *esi = &p->prefix.ead_addr.esi;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
if (!vpn)
/* local EAD-ES need not be sent back to zebra */
- return 0;
+ return ret;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug("add remote %s es %s evi %u vtep %pI4",
@@ -3808,27 +3838,29 @@ int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
es_evi = bgp_evpn_es_evi_new(es, vpn);
ead_es = !!p->prefix.ead_addr.eth_tag;
- bgp_evpn_es_evi_vtep_add(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
- ead_es);
+ ret = bgp_evpn_es_evi_vtep_add(bgp, es_evi,
+ p->prefix.ead_addr.ip.ipaddr_v4, ead_es);
bgp_evpn_es_evi_remote_info_re_eval(es_evi);
- return 0;
+ return ret;
}
/* A remote VTEP has withdrawn. The es-evi-vtep will be deleted and the
* parent es-evi freed up implicitly in last VTEP's deref.
*/
-int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p)
+enum zclient_send_status bgp_evpn_remote_es_evi_del(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
{
char buf[ESI_STR_LEN];
struct bgp_evpn_es *es;
struct bgp_evpn_es_evi *es_evi;
bool ead_es;
+ enum zclient_send_status ret = ZCLIENT_SEND_SUCCESS;
if (!vpn)
/* local EAD-ES need not be sent back to zebra */
- return 0;
+ return ret;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug(
@@ -3847,7 +3879,7 @@ int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
esi_to_str(&p->prefix.ead_addr.esi, buf,
sizeof(buf)),
vpn->vni, &p->prefix.ead_addr.ip.ipaddr_v4);
- return 0;
+ return ret;
}
es_evi = bgp_evpn_es_evi_find(es, vpn);
if (!es_evi) {
@@ -3860,14 +3892,15 @@ int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
sizeof(buf)),
vpn->vni,
&p->prefix.ead_addr.ip.ipaddr_v4);
- return 0;
+ return ret;
}
ead_es = !!p->prefix.ead_addr.eth_tag;
- bgp_evpn_es_evi_vtep_del(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
- ead_es);
+ ret = bgp_evpn_es_evi_vtep_del(bgp, es_evi,
+ p->prefix.ead_addr.ip.ipaddr_v4, ead_es);
bgp_evpn_es_evi_remote_info_re_eval(es_evi);
- return 0;
+
+ return ret;
}
/* If a VNI is being deleted we need to force del all remote VTEPs */
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
index cebabb9fd0..5d393c37a2 100644
--- a/bgpd/bgp_evpn_mh.h
+++ b/bgpd/bgp_evpn_mh.h
@@ -418,10 +418,12 @@ extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi);
extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni);
extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni);
-extern int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p);
-extern int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
- const struct prefix_evpn *p);
+extern enum zclient_send_status
+bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
+extern enum zclient_send_status
+bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
extern void bgp_evpn_mh_init(void);
extern void bgp_evpn_mh_finish(void);
void bgp_evpn_vni_es_init(struct bgpevpn *vpn);
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index 5af99afa34..07bba9b426 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -716,7 +716,8 @@ extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
struct bgp_path_info **pi);
int vni_list_cmp(void *p1, void *p2);
extern int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
- struct bgp_dest *dest);
+ struct bgp_dest *dest,
+ struct bgp_path_info *pi);
extern struct bgp_dest *
bgp_evpn_global_node_get(struct bgp_table *table, afi_t afi, safi_t safi,
const struct prefix_evpn *evp, struct prefix_rd *prd,
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index a2d3172882..7866adbdcd 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -1796,6 +1796,22 @@ bgp_connect_fail(struct peer_connection *connection)
return bgp_stop(connection);
}
+/* after connect is called(), getpeername is able to return
+ * port and address on non established streams
+ */
+static void bgp_connect_in_progress_update_connection(struct peer *peer)
+{
+ bgp_getsockname(peer);
+ if (!peer->su_remote && !BGP_CONNECTION_SU_UNSPEC(peer->connection)) {
+ /* if connect initiated, then dest port and dest addresses are well known */
+ peer->su_remote = sockunion_dup(&peer->connection->su);
+ if (sockunion_family(peer->su_remote) == AF_INET)
+ peer->su_remote->sin.sin_port = htons(peer->port);
+ else if (sockunion_family(peer->su_remote) == AF_INET6)
+ peer->su_remote->sin6.sin6_port = htons(peer->port);
+ }
+}
+
/* This function is the first starting point of all BGP connection. It
* try to connect to remote peer with non-blocking IO.
*/
@@ -1892,6 +1908,8 @@ static enum bgp_fsm_state_progress bgp_start(struct peer_connection *connection)
__func__, peer->connection->fd);
return BGP_FSM_FAILURE;
}
+ bgp_connect_in_progress_update_connection(peer);
+
/*
* - when the socket becomes ready, poll() will signify POLLOUT
* - if it fails to connect, poll() will signify POLLHUP
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index 7327ab5182..68104967b2 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -74,7 +74,7 @@ int bgp_parse_fec_update(void)
bgp_set_valid_label(&dest->local_label);
}
SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, NULL, afi, safi);
bgp_dest_unlock_node(dest);
return 1;
}
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 851c4880c3..44d5ee68cc 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -207,6 +207,8 @@ static __attribute__((__noreturn__)) void bgp_exit(int status)
bgp_evpn_mh_finish();
bgp_nhg_finish();
+ zebra_announce_fini(&bm->zebra_announce_head);
+
/* reverse bgp_dump_init */
bgp_dump_finish();
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 91bc3b1a88..d17fdc7169 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -388,6 +388,9 @@ void vpn_leak_zebra_vrf_sid_update_per_af(struct bgp *bgp, afi_t afi)
tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
*tovpn_sid_ls = *tovpn_sid;
+ if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent);
bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls;
}
@@ -435,6 +438,8 @@ void vpn_leak_zebra_vrf_sid_update_per_vrf(struct bgp *bgp)
tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
*tovpn_sid_ls = *tovpn_sid;
+ if (bgp->tovpn_zebra_vrf_sid_last_sent)
+ XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent);
bgp->tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls;
}
@@ -482,6 +487,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi)
bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL);
XFREE(MTYPE_BGP_SRV6_SID,
bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent);
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = NULL;
}
/*
@@ -508,6 +514,7 @@ void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp)
bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC,
NULL);
XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent);
+ bgp->tovpn_zebra_vrf_sid_last_sent = NULL;
}
/*
@@ -1201,7 +1208,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
/* Process change. */
bgp_aggregate_increment(to_bgp, p, bpi, afi, safi);
- bgp_process(to_bgp, bn, afi, safi);
+ bgp_process(to_bgp, bn, bpi, afi, safi);
if (debug)
zlog_debug("%s: ->%s: %pBD Found route, changed attr",
@@ -1263,7 +1270,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
bgp_aggregate_increment(to_bgp, p, new, afi, safi);
bgp_path_info_add(bn, new);
- bgp_process(to_bgp, bn, afi, safi);
+ bgp_process(to_bgp, bn, new, afi, safi);
if (debug)
zlog_debug("%s: ->%s: %pBD: Added new route", __func__,
@@ -1434,6 +1441,16 @@ _vpn_leak_from_vrf_get_per_nexthop_label(struct bgp_path_info *pi,
return blnc->label;
}
+static mpls_label_t bgp_mplsvpn_get_vpn_label(struct vpn_policy *bgp_policy)
+{
+ if (bgp_policy->tovpn_label == MPLS_LABEL_NONE &&
+ CHECK_FLAG(bgp_policy->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+ bgp_lp_get(LP_TYPE_VRF, bgp_policy, vpn_leak_label_callback);
+ return MPLS_INVALID_LABEL;
+ }
+ return bgp_policy->tovpn_label;
+}
+
/* Filter out all the cases where a per nexthop label is not possible:
* - return an invalid label when the nexthop is invalid
* - return the per VRF label when the per nexthop label is not supported
@@ -1462,7 +1479,7 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi,
* Fallback to the per VRF label.
*/
bgp_mplsvpn_path_nh_label_unlink(pi);
- return from_bgp->vpn_policy[afi].tovpn_label;
+ return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
}
if (is_bgp_static_route == false && afi == AFI_IP &&
@@ -1474,7 +1491,7 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi,
* Fallback to the per VRF label.
*/
bgp_mplsvpn_path_nh_label_unlink(pi);
- return from_bgp->vpn_policy[afi].tovpn_label;
+ return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
}
if (is_bgp_static_route == false && afi == AFI_IP6 &&
@@ -1488,7 +1505,7 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi,
* Fallback to the per VRF label.
*/
bgp_mplsvpn_path_nh_label_unlink(pi);
- return from_bgp->vpn_policy[afi].tovpn_label;
+ return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
}
/* Check the next-hop reachability.
@@ -1510,7 +1527,7 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi,
* table. Fallback to the per-vrf label
*/
bgp_mplsvpn_path_nh_label_unlink(pi);
- return from_bgp->vpn_policy[afi].tovpn_label;
+ return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
}
if (!nh_valid || !pi->nexthop || pi->nexthop->nexthop_num == 0 ||
@@ -1533,7 +1550,7 @@ vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi,
* Fallback to per-vrf label.
*/
bgp_mplsvpn_path_nh_label_unlink(pi);
- return from_bgp->vpn_policy[afi].tovpn_label;
+ return bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
}
return _vpn_leak_from_vrf_get_per_nexthop_label(pi, to_bgp, from_bgp,
@@ -1748,12 +1765,10 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
label_val = vpn_leak_from_vrf_get_per_nexthop_label(
afi, path_vrf, from_bgp, to_bgp);
else
- /* per VRF label mode */
- label_val = from_bgp->vpn_policy[afi].tovpn_label;
+ label_val =
+ bgp_mplsvpn_get_vpn_label(&from_bgp->vpn_policy[afi]);
- if (label_val == MPLS_INVALID_LABEL &&
- CHECK_FLAG(from_bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) {
+ if (label_val == MPLS_INVALID_LABEL) {
/* no valid label for the moment
* when the 'bgp_mplsvpn_get_label_per_nexthop_cb' callback gets
* a valid label value, it will call the current function again.
@@ -1949,7 +1964,7 @@ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
bgp_aggregate_decrement(to_bgp, p, bpi, afi, safi);
bgp_path_info_delete(bn, bpi);
- bgp_process(to_bgp, bn, afi, safi);
+ bgp_process(to_bgp, bn, bpi, afi, safi);
}
bgp_dest_unlock_node(bn);
}
@@ -1969,7 +1984,7 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp,
struct bgp_table *table;
struct bgp_dest *bn;
- struct bgp_path_info *bpi;
+ struct bgp_path_info *bpi, *next;
/* This is the per-RD table of prefixes */
table = bgp_dest_get_bgp_table_info(pdest);
@@ -1984,7 +1999,8 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp,
__func__, bn);
}
- for (; bpi; bpi = bpi->next) {
+ for (; (bpi != NULL) && (next = bpi->next, 1);
+ bpi = next) {
if (debug)
zlog_debug("%s: type %d, sub_type %d",
__func__, bpi->type,
@@ -2005,7 +2021,7 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp,
to_bgp, bgp_dest_get_prefix(bn),
bpi, afi, safi);
bgp_path_info_delete(bn, bpi);
- bgp_process(to_bgp, bn, afi, safi);
+ bgp_process(to_bgp, bn, bpi, afi, safi);
bgp_mplsvpn_path_nh_label_unlink(
bpi->extra->vrfleak->parent);
}
@@ -2499,7 +2515,7 @@ void vpn_leak_to_vrf_withdraw(struct bgp_path_info *path_vpn)
bpi);
bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
bgp_path_info_delete(bn, bpi);
- bgp_process(bgp, bn, afi, safi);
+ bgp_process(bgp, bn, bpi, afi, safi);
}
bgp_dest_unlock_node(bn);
}
@@ -2508,7 +2524,7 @@ void vpn_leak_to_vrf_withdraw(struct bgp_path_info *path_vpn)
void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi)
{
struct bgp_dest *bn;
- struct bgp_path_info *bpi;
+ struct bgp_path_info *bpi, *next;
safi_t safi = SAFI_UNICAST;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
@@ -2519,9 +2535,8 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi)
*/
for (bn = bgp_table_top(to_bgp->rib[afi][safi]); bn;
bn = bgp_route_next(bn)) {
-
- for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
- bpi = bpi->next) {
+ for (bpi = bgp_dest_get_bgp_path_info(bn);
+ (bpi != NULL) && (next = bpi->next, 1); bpi = next) {
if (bpi->extra && bpi->extra->vrfleak &&
bpi->extra->vrfleak->bgp_orig != to_bgp &&
bpi->extra->vrfleak->parent &&
@@ -2531,7 +2546,7 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi)
bgp_dest_get_prefix(bn),
bpi, afi, safi);
bgp_path_info_delete(bn, bpi);
- bgp_process(to_bgp, bn, afi, safi);
+ bgp_process(to_bgp, bn, bpi, afi, safi);
}
}
}
@@ -2560,8 +2575,11 @@ void vpn_leak_no_retain(struct bgp *to_bgp, struct bgp *vpn_from, afi_t afi)
continue;
for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) {
- for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
- bpi = bpi->next) {
+ struct bgp_path_info *next;
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn);
+ (bpi != NULL) && (next = bpi->next, 1);
+ bpi = next) {
if (bpi->extra && bpi->extra->vrfleak &&
bpi->extra->vrfleak->bgp_orig == to_bgp)
continue;
@@ -4182,7 +4200,7 @@ static int bgp_mplsvpn_nh_label_bind_get_local_label_cb(mpls_label_t label,
if (!table)
continue;
SET_FLAG(pi->net->flags, BGP_NODE_LABEL_CHANGED);
- bgp_process(table->bgp, pi->net, table->afi, table->safi);
+ bgp_process(table->bgp, pi->net, pi, table->afi, table->safi);
}
return 0;
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index cd25899965..92a9fba887 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -170,16 +170,6 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
return 0;
}
- /* Is there an "auto" export label that isn't allocated yet? */
- if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
- (bgp_vrf->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE)) {
-
- if (pmsg)
- *pmsg = "auto label not allocated";
- return 0;
- }
-
/* Is there a "manual" export label that isn't allocated yet? */
if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index dbb34b048f..b409cbe706 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -875,11 +875,7 @@ int bgp_getsockname(struct peer *peer)
}
peer->su_local = sockunion_getsockname(peer->connection->fd);
- if (!peer->su_local)
- return -1;
peer->su_remote = sockunion_getpeername(peer->connection->fd);
- if (!peer->su_remote)
- return -1;
if (!bgp_zebra_nexthop_set(peer->su_local, peer->su_remote,
&peer->nexthop, peer)) {
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index d12dc22330..98eb9565bf 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -1003,6 +1003,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
if (bnc->is_evpn_gwip_nexthop)
json_object_boolean_true_add(json_nexthop,
"isEvpnGatewayIp");
+ json_object_string_addf(json, "resolvedPrefix", "%pFX",
+ &bnc->resolved_prefix);
} else {
vty_out(vty, " %s valid [IGP metric %d], #paths %d",
buf, bnc->metric, bnc->path_count);
@@ -1010,6 +1012,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
vty_out(vty, ", peer %s", peer->host);
if (bnc->is_evpn_gwip_nexthop)
vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n Resolved prefix %pFX",
+ &bnc->resolved_prefix);
vty_out(vty, "\n");
}
bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop);
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index 830883872e..430c8f17e8 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -90,6 +90,7 @@ struct bgp_nexthop_cache {
struct bgp_nexthop_cache_head *tree;
struct prefix prefix;
+ struct prefix resolved_prefix;
void *nht_info; /* In BGP, peer session */
LIST_HEAD(path_list, bgp_path_info) paths;
unsigned int path_count;
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 786d07c5a9..80ec9bcf77 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -626,6 +626,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
} else if (nhr->nexthop_num) {
struct peer *peer = bnc->nht_info;
+ prefix_copy(&bnc->resolved_prefix, &nhr->prefix);
+
/* notify bgp fsm if nbr ip goes from invalid->valid */
if (!bnc->nexthop_num)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
@@ -731,6 +733,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
}
}
} else {
+ memset(&bnc->resolved_prefix, 0, sizeof(bnc->resolved_prefix));
bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
bnc->flags &= ~BGP_NEXTHOP_VALID;
bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
@@ -1432,7 +1435,7 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc)
}
}
- bgp_process(bgp_path, dest, afi, safi);
+ bgp_process(bgp_path, dest, path, afi, safi);
}
if (peer) {
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 15738e673b..1c3bb6e775 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -1452,8 +1452,10 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
/* All OPEN option is parsed. Check capability when strict compare
flag is enabled.*/
if (CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) {
- /* If Unsupported Capability exists. */
- if (error != error_data) {
+ /* If Unsupported Capability exists or local capability does
+ * not negotiated with remote peer
+ */
+ if (error != error_data || !strict_capability_same(peer)) {
bgp_notify_send_with_data(peer->connection,
BGP_NOTIFY_OPEN_ERR,
BGP_NOTIFY_OPEN_UNSUP_CAPBL,
@@ -1461,14 +1463,6 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
error - error_data);
return -1;
}
-
- /* Check local capability does not negotiated with remote
- peer. */
- if (!strict_capability_same(peer)) {
- bgp_notify_send(peer->connection, BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_CAPBL);
- return -1;
- }
}
/* Extended Message Support */
@@ -1503,17 +1497,11 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
"%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities",
peer->host);
- if (error != error_data)
- bgp_notify_send_with_data(peer->connection,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_CAPBL,
- error_data,
- error - error_data);
- else
- bgp_notify_send(peer->connection,
- BGP_NOTIFY_OPEN_ERR,
- BGP_NOTIFY_OPEN_UNSUP_CAPBL);
- return -1;
+ bgp_notify_send_with_data(peer->connection,
+ BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data,
+ error - error_data);
}
}
return 0;
@@ -1975,7 +1963,7 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
}
/* Dynamic capability. */
- if (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) {
+ if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) {
SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
stream_putc(s, BGP_OPEN_OPT_CAP);
ext_opt_params
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 8a36a3f4c8..78554893ff 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -3240,11 +3240,13 @@ static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,
safi_t safi;
iana_afi_t pkt_afi;
iana_safi_t pkt_safi;
+ uint16_t paths_limit = 0;
struct bgp_paths_limit_capability bpl = {};
memcpy(&bpl, data, sizeof(bpl));
pkt_afi = ntohs(bpl.afi);
pkt_safi = safi_int2iana(bpl.safi);
+ paths_limit = ntohs(bpl.paths_limit);
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s limit: %u",
@@ -3252,8 +3254,7 @@ static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,
lookup_msg(capcode_str, hdr->code,
NULL),
iana_afi2str(pkt_afi),
- iana_safi2str(pkt_safi),
- bpl.paths_limit);
+ iana_safi2str(pkt_safi), paths_limit);
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi,
&safi)) {
@@ -3275,7 +3276,7 @@ static void bgp_dynamic_capability_paths_limit(uint8_t *pnt, int action,
SET_FLAG(peer->af_cap[afi][safi],
PEER_CAP_PATHS_LIMIT_AF_RCV);
peer->addpath_paths_limit[afi][safi].receive =
- bpl.paths_limit;
+ paths_limit;
ignore:
data += CAPABILITY_CODE_PATHS_LIMIT_LEN;
}
@@ -3733,6 +3734,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
zlog_err("%pBP: Capability length error", peer);
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ pnt += length;
return BGP_Stop;
}
action = *pnt;
@@ -3745,7 +3747,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
action);
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- return BGP_Stop;
+ goto done;
}
if (bgp_debug_neighbor_events(peer))
@@ -3757,12 +3759,13 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
zlog_err("%pBP: Capability length error", peer);
bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ pnt += length;
return BGP_Stop;
}
/* Ignore capability when override-capability is set. */
if (CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
- continue;
+ goto done;
capability = lookup_msg(capcode_str, hdr->code, "Unknown");
@@ -3777,7 +3780,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
peer, capability,
sizeof(struct capability_mp_data),
hdr->length);
- return BGP_Stop;
+ goto done;
}
memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data));
@@ -3792,7 +3795,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
peer, capability,
iana_afi2str(pkt_afi),
iana_safi2str(pkt_safi));
- continue;
+ goto done;
}
/* Address family check. */
@@ -3819,7 +3822,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
if (peer_active_nego(peer))
bgp_clear_route(peer, afi, safi);
else
- return BGP_Stop;
+ goto done;
}
break;
case CAPABILITY_CODE_RESTART:
@@ -3829,7 +3832,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
bgp_notify_send(peer->connection,
BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- return BGP_Stop;
+ goto done;
}
bgp_dynamic_capability_graceful_restart(pnt, action,
@@ -3865,7 +3868,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
bgp_notify_send(peer->connection,
BGP_NOTIFY_CEASE,
BGP_NOTIFY_SUBCODE_UNSPECIFIC);
- return BGP_Stop;
+ goto done;
}
uint8_t role;
@@ -3887,6 +3890,7 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
break;
}
+done:
pnt += hdr->length + 3;
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 520185e60f..848e8ffd8d 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -114,6 +114,46 @@ static const struct message bgp_pmsi_tnltype_str[] = {
#define VRFID_NONE_STR "-"
#define SOFT_RECONFIG_TASK_MAX_PREFIX 25000
+static inline char *bgp_route_dump_path_info_flags(struct bgp_path_info *pi,
+ char *buf, size_t len)
+{
+ uint32_t flags = pi->flags;
+
+ if (flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(buf, len, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ CHECK_FLAG(flags, BGP_PATH_IGP_CHANGED) ? "IGP Changed " : "",
+ CHECK_FLAG(flags, BGP_PATH_DAMPED) ? "Damped" : "",
+ CHECK_FLAG(flags, BGP_PATH_HISTORY) ? "History " : "",
+ CHECK_FLAG(flags, BGP_PATH_SELECTED) ? "Selected " : "",
+ CHECK_FLAG(flags, BGP_PATH_VALID) ? "Valid " : "",
+ CHECK_FLAG(flags, BGP_PATH_ATTR_CHANGED) ? "Attr Changed "
+ : "",
+ CHECK_FLAG(flags, BGP_PATH_DMED_CHECK) ? "Dmed Check " : "",
+ CHECK_FLAG(flags, BGP_PATH_DMED_SELECTED) ? "Dmed Selected "
+ : "",
+ CHECK_FLAG(flags, BGP_PATH_STALE) ? "Stale " : "",
+ CHECK_FLAG(flags, BGP_PATH_REMOVED) ? "Removed " : "",
+ CHECK_FLAG(flags, BGP_PATH_COUNTED) ? "Counted " : "",
+ CHECK_FLAG(flags, BGP_PATH_MULTIPATH) ? "Mpath " : "",
+ CHECK_FLAG(flags, BGP_PATH_MULTIPATH_CHG) ? "Mpath Chg " : "",
+ CHECK_FLAG(flags, BGP_PATH_RIB_ATTR_CHG) ? "Rib Chg " : "",
+ CHECK_FLAG(flags, BGP_PATH_ANNC_NH_SELF) ? "NH Self " : "",
+ CHECK_FLAG(flags, BGP_PATH_LINK_BW_CHG) ? "LinkBW Chg " : "",
+ CHECK_FLAG(flags, BGP_PATH_ACCEPT_OWN) ? "Accept Own " : "",
+ CHECK_FLAG(flags, BGP_PATH_MPLSVPN_LABEL_NH) ? "MPLS Label "
+ : "",
+ CHECK_FLAG(flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND)
+ ? "MPLS Label Bind "
+ : "",
+ CHECK_FLAG(flags, BGP_PATH_UNSORTED) ? "Unsorted " : "");
+
+ return buf;
+}
+
DEFINE_HOOK(bgp_process,
(struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn,
struct peer *peer, bool withdraw),
@@ -442,6 +482,7 @@ void bgp_path_info_add_with_caller(const char *name, struct bgp_dest *dest,
top->prev = pi;
bgp_dest_set_bgp_path_info(dest, pi);
+ SET_FLAG(pi->flags, BGP_PATH_UNSORTED);
bgp_path_info_lock(pi);
bgp_dest_lock_node(dest);
peer_lock(pi->peer); /* bgp_path_info peer reference */
@@ -462,8 +503,26 @@ struct bgp_dest *bgp_path_info_reap(struct bgp_dest *dest,
bgp_dest_set_bgp_path_info(dest, pi->next);
bgp_path_info_mpath_dequeue(pi);
+
+ pi->next = NULL;
+ pi->prev = NULL;
+
+ hook_call(bgp_snmp_update_stats, dest, pi, false);
+
bgp_path_info_unlock(pi);
+ return bgp_dest_unlock_node(dest);
+}
+
+static struct bgp_dest *bgp_path_info_reap_unsorted(struct bgp_dest *dest,
+ struct bgp_path_info *pi)
+{
+ bgp_path_info_mpath_dequeue(pi);
+
+ pi->next = NULL;
+ pi->prev = NULL;
+
hook_call(bgp_snmp_update_stats, dest, pi, false);
+ bgp_path_info_unlock(pi);
return bgp_dest_unlock_node(dest);
}
@@ -553,6 +612,15 @@ void bgp_path_info_unset_flag(struct bgp_dest *dest, struct bgp_path_info *pi,
bgp_pcount_adjust(dest, pi);
}
+static bool use_bgp_med_value(struct attr *attr, struct bgp *bgp)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC) ||
+ CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST))
+ return true;
+
+ return false;
+}
+
/* Get MED value. If MED value is missing and "bgp bestpath
missing-as-worst" is specified, treat it as the worst value. */
static uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp)
@@ -679,12 +747,18 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
}
if (debug) {
+ char buf1[256], buf2[256];
+
bpi_ultimate = bgp_get_imported_bpi_ultimate(exist);
bgp_path_info_path_with_addpath_rx_str(bpi_ultimate, exist_buf,
sizeof(exist_buf));
- zlog_debug("%s(%s): Comparing %s flags 0x%x with %s flags 0x%x",
- pfx_buf, bgp->name_pretty, new_buf, new->flags,
- exist_buf, exist->flags);
+ zlog_debug("%s(%s): Comparing %s flags %s with %s flags %s",
+ pfx_buf, bgp->name_pretty, new_buf,
+ bgp_route_dump_path_info_flags(new, buf1,
+ sizeof(buf1)),
+ exist_buf,
+ bgp_route_dump_path_info_flags(exist, buf2,
+ sizeof(buf2)));
}
newattr = new->attr;
@@ -2242,7 +2316,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
}
/* AS path loop check. */
- if (peer->as_path_loop_detection &&
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_AS_LOOP_DETECTION) &&
aspath_loop_check(piattr->aspath, peer->as)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
@@ -2736,17 +2810,18 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
struct bgp_path_info_pair *result, afi_t afi,
safi_t safi)
{
- struct bgp_path_info *new_select;
- struct bgp_path_info *old_select;
+ struct bgp_path_info *new_select, *look_thru;
+ struct bgp_path_info *old_select, *worse, *first;
struct bgp_path_info *pi;
struct bgp_path_info *pi1;
struct bgp_path_info *pi2;
- struct bgp_path_info *nextpi = NULL;
int paths_eq, do_mpath;
- bool debug;
+ bool debug, any_comparisons;
struct list mp_list;
char pfx_buf[PREFIX2STR_BUFFER] = {};
char path_buf[PATH_ADDPATH_STR_BUFFER];
+ enum bgp_path_selection_reason reason = bgp_path_selection_none;
+ bool unsorted_items = true;
bgp_mp_list_init(&mp_list);
do_mpath =
@@ -2757,16 +2832,16 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
if (debug)
prefix2str(bgp_dest_get_prefix(dest), pfx_buf, sizeof(pfx_buf));
- dest->reason = bgp_path_selection_none;
/* bgp deterministic-med */
new_select = NULL;
if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) {
-
/* Clear BGP_PATH_DMED_SELECTED for all paths */
for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1;
- pi1 = pi1->next)
+ pi1 = pi1->next) {
bgp_path_info_unset_flag(dest, pi1,
BGP_PATH_DMED_SELECTED);
+ UNSET_FLAG(pi1->flags, BGP_PATH_DMED_CHECK);
+ }
for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1;
pi1 = pi1->next) {
@@ -2829,68 +2904,273 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
}
}
- /* Check old selected route and new selected route. */
+ /*
+ * Let's grab the unsorted items from the list
+ */
+ struct bgp_path_info *unsorted_list = NULL;
+ struct bgp_path_info *unsorted_list_spot = NULL;
+ struct bgp_path_info *unsorted_holddown = NULL;
+
old_select = NULL;
- new_select = NULL;
- for (pi = bgp_dest_get_bgp_path_info(dest);
- (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
- enum bgp_path_selection_reason reason;
+ pi = bgp_dest_get_bgp_path_info(dest);
+ while (pi && CHECK_FLAG(pi->flags, BGP_PATH_UNSORTED)) {
+ struct bgp_path_info *next = pi->next;
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
old_select = pi;
- if (BGP_PATH_HOLDDOWN(pi)) {
- /* reap REMOVED routes, if needs be
+ /*
+ * Pull off pi off the list
+ */
+ if (pi->next)
+ pi->next->prev = NULL;
+
+ bgp_dest_set_bgp_path_info(dest, pi->next);
+ pi->next = NULL;
+ pi->prev = NULL;
+
+ /*
+ * Place it on the unsorted list
+ */
+ if (unsorted_list_spot) {
+ unsorted_list_spot->next = pi;
+ pi->prev = unsorted_list_spot;
+ pi->next = NULL;
+ } else {
+ unsorted_list = pi;
+
+ pi->next = NULL;
+ pi->prev = NULL;
+ }
+
+ unsorted_list_spot = pi;
+ pi = next;
+ }
+
+ if (!old_select) {
+ old_select = bgp_dest_get_bgp_path_info(dest);
+ if (old_select &&
+ !CHECK_FLAG(old_select->flags, BGP_PATH_SELECTED))
+ old_select = NULL;
+ }
+
+ if (!unsorted_list)
+ unsorted_items = true;
+ else
+ unsorted_items = false;
+
+ any_comparisons = false;
+ worse = NULL;
+ while (unsorted_list) {
+ first = unsorted_list;
+ unsorted_list = unsorted_list->next;
+
+ if (unsorted_list)
+ unsorted_list->prev = NULL;
+ first->next = NULL;
+ first->prev = NULL;
+
+ /*
+ * It's not likely that the just received unsorted entry
+ * is in holddown and scheduled for removal but we should
+ * check
+ */
+ if (BGP_PATH_HOLDDOWN(first)) {
+ /*
+ * reap REMOVED routes, if needs be
* selected route must stay for a while longer though
*/
if (debug)
- zlog_debug(
- "%s: %pBD(%s) pi from %s in holddown",
- __func__, dest, bgp->name_pretty,
- pi->peer->host);
+ zlog_debug("%s: %pBD(%s) pi %p from %s in holddown",
+ __func__, dest, bgp->name_pretty,
+ first, first->peer->host);
- if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) &&
- (pi != old_select)) {
- dest = bgp_path_info_reap(dest, pi);
+ if (old_select != first &&
+ CHECK_FLAG(first->flags, BGP_PATH_REMOVED)) {
+ dest = bgp_path_info_reap_unsorted(dest, first);
assert(dest);
- }
+ } else {
+ /*
+ * We are in hold down, so we cannot sort this
+ * item yet. Let's wait, so hold the unsorted
+ * to the side
+ */
+ if (unsorted_holddown) {
+ first->next = unsorted_holddown;
+ unsorted_holddown->prev = first;
+ unsorted_holddown = first;
+ } else
+ unsorted_holddown = first;
+ UNSET_FLAG(first->flags, BGP_PATH_UNSORTED);
+ }
continue;
}
- if (pi->peer && pi->peer != bgp->peer_self
- && !CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT))
- if (!peer_established(pi->peer->connection)) {
+ bgp_path_info_unset_flag(dest, first, BGP_PATH_DMED_CHECK);
+
+ worse = NULL;
+
+ struct bgp_path_info *look_thru_next;
+
+ for (look_thru = bgp_dest_get_bgp_path_info(dest); look_thru;
+ look_thru = look_thru_next) {
+ /* look thru can be reaped save the next pointer */
+ look_thru_next = look_thru->next;
+
+ /*
+ * Now we have the first unsorted and the best selected
+ * Let's do best path comparison
+ */
+ if (BGP_PATH_HOLDDOWN(look_thru)) {
+ /* reap REMOVED routes, if needs be
+ * selected route must stay for a while longer though
+ */
if (debug)
- zlog_debug(
- "%s: %pBD(%s) non self peer %s not estab state",
- __func__, dest,
- bgp->name_pretty,
- pi->peer->host);
+ zlog_debug("%s: %pBD(%s) pi from %s %p in holddown",
+ __func__, dest,
+ bgp->name_pretty,
+ look_thru->peer->host,
+ look_thru);
+
+ if (CHECK_FLAG(look_thru->flags,
+ BGP_PATH_REMOVED) &&
+ (look_thru != old_select)) {
+ dest = bgp_path_info_reap(dest,
+ look_thru);
+ assert(dest);
+ }
continue;
}
- bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK);
+ if (look_thru->peer &&
+ look_thru->peer != bgp->peer_self &&
+ !CHECK_FLAG(look_thru->peer->sflags,
+ PEER_STATUS_NSF_WAIT))
+ if (!peer_established(
+ look_thru->peer->connection)) {
+ if (debug)
+ zlog_debug("%s: %pBD(%s) non self peer %s not estab state",
+ __func__, dest,
+ bgp->name_pretty,
+ look_thru->peer->host);
- if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED) &&
- (!CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))) {
- if (debug)
- zlog_debug("%s: %pBD(%s) pi %s dmed", __func__,
- dest, bgp->name_pretty,
- pi->peer->host);
- continue;
+ continue;
+ }
+
+ bgp_path_info_unset_flag(dest, look_thru,
+ BGP_PATH_DMED_CHECK);
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED) &&
+ (!CHECK_FLAG(look_thru->flags,
+ BGP_PATH_DMED_SELECTED))) {
+ bgp_path_info_unset_flag(dest, look_thru,
+ BGP_PATH_DMED_CHECK);
+ if (debug)
+ zlog_debug("%s: %pBD(%s) pi %s dmed",
+ __func__, dest,
+ bgp->name_pretty,
+ look_thru->peer->host);
+
+ worse = look_thru;
+ continue;
+ }
+
+ reason = dest->reason;
+ any_comparisons = true;
+ if (bgp_path_info_cmp(bgp, first, look_thru, &paths_eq,
+ mpath_cfg, debug, pfx_buf, afi,
+ safi, &reason)) {
+ first->reason = reason;
+ worse = look_thru;
+ /*
+ * We can stop looking
+ */
+ break;
+ }
+
+ look_thru->reason = reason;
}
- reason = dest->reason;
- if (bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, mpath_cfg,
- debug, pfx_buf, afi, safi,
- &dest->reason)) {
- if (new_select == NULL &&
- reason != bgp_path_selection_none)
- dest->reason = reason;
- new_select = pi;
+ if (!any_comparisons)
+ first->reason = bgp_path_selection_first;
+
+ /*
+ * At this point worse if NON-NULL is where the first
+ * pointer should be before. if worse is NULL then
+ * first is bestpath too. Let's remove first from the
+ * list and place it in the right spot
+ */
+
+ if (!worse) {
+ struct bgp_path_info *end =
+ bgp_dest_get_bgp_path_info(dest);
+
+ for (; end && end->next != NULL; end = end->next)
+ ;
+
+ if (end)
+ end->next = first;
+ else
+ bgp_dest_set_bgp_path_info(dest, first);
+ first->prev = end;
+ first->next = NULL;
+
+ dest->reason = first->reason;
+ } else {
+ if (worse->prev)
+ worse->prev->next = first;
+ first->next = worse;
+ if (worse) {
+ first->prev = worse->prev;
+ worse->prev = first;
+ } else
+ first->prev = NULL;
+
+ if (dest->info == worse) {
+ bgp_dest_set_bgp_path_info(dest, first);
+ dest->reason = first->reason;
+ }
}
+ UNSET_FLAG(first->flags, BGP_PATH_UNSORTED);
+ }
+
+ if (!unsorted_items) {
+ new_select = bgp_dest_get_bgp_path_info(dest);
+ while (new_select && BGP_PATH_HOLDDOWN(new_select))
+ new_select = new_select->next;
+
+ if (new_select) {
+ if (new_select->reason == bgp_path_selection_none)
+ new_select->reason = bgp_path_selection_first;
+ else if (new_select == bgp_dest_get_bgp_path_info(dest) &&
+ new_select->next == NULL)
+ new_select->reason = bgp_path_selection_first;
+ dest->reason = new_select->reason;
+ } else
+ dest->reason = bgp_path_selection_none;
+ } else
+ new_select = old_select;
+
+
+ /*
+ * Reinsert all the unsorted_holddown items for future processing
+ * at the end of the list.
+ */
+ if (unsorted_holddown) {
+ struct bgp_path_info *top = bgp_dest_get_bgp_path_info(dest);
+ struct bgp_path_info *prev = NULL;
+
+ while (top != NULL) {
+ prev = top;
+ top = top->next;
+ }
+
+ if (prev) {
+ prev->next = unsorted_holddown;
+ unsorted_holddown->prev = prev;
+ } else
+ bgp_dest_set_bgp_path_info(dest, unsorted_holddown);
}
/* Now that we know which path is the bestpath see if any of the other
@@ -2979,7 +3259,7 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
{
const struct prefix *p;
struct peer *onlypeer;
- struct attr attr;
+ struct attr attr = { 0 }, *pattr = &attr;
struct bgp *bgp;
bool advertise;
@@ -3007,26 +3287,30 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
advertise = bgp_check_advertise(bgp, dest, safi);
if (selected) {
- if (subgroup_announce_check(dest, selected, subgrp, p, &attr,
+ if (subgroup_announce_check(dest, selected, subgrp, p, pattr,
NULL)) {
/* Route is selected, if the route is already installed
* in FIB, then it is advertised
*/
if (advertise) {
if (!bgp_check_withdrawal(bgp, dest, safi)) {
- struct attr *adv_attr =
- bgp_attr_intern(&attr);
-
- bgp_adj_out_set_subgroup(dest, subgrp,
- adv_attr,
- selected);
- } else
+ if (!bgp_adj_out_set_subgroup(dest,
+ subgrp,
+ pattr,
+ selected))
+ bgp_attr_flush(pattr);
+ } else {
bgp_adj_out_unset_subgroup(
dest, subgrp, 1, addpath_tx_id);
- }
- } else
+ bgp_attr_flush(pattr);
+ }
+ } else
+ bgp_attr_flush(pattr);
+ } else {
bgp_adj_out_unset_subgroup(dest, subgrp, 1,
addpath_tx_id);
+ bgp_attr_flush(pattr);
+ }
}
/* If selected is NULL we must withdraw the path using addpath_tx_id */
@@ -3335,7 +3619,9 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
return;
}
+#ifdef ENABLE_BGP_VNC
const struct prefix *p = bgp_dest_get_prefix(dest);
+#endif
debug = bgp_debug_bestpath(dest);
if (debug)
@@ -3398,9 +3684,9 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
&& (new_select->sub_type == BGP_ROUTE_NORMAL
|| new_select->sub_type
== BGP_ROUTE_IMPORTED))
-
- bgp_zebra_announce(dest, p, old_select,
- bgp, afi, safi);
+ bgp_zebra_route_install(dest, old_select,
+ bgp, true, NULL,
+ false);
}
}
@@ -3460,7 +3746,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
if (new_select) {
if (debug)
- zlog_debug("%s: setting SELECTED flag", __func__);
+ zlog_debug("%s: %pBD setting SELECTED flag", __func__,
+ dest);
bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED);
bgp_path_info_unset_flag(dest, new_select,
BGP_PATH_ATTR_CHANGED);
@@ -3494,14 +3781,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
}
#endif
- group_announce_route(bgp, afi, safi, dest, new_select);
-
- /* unicast routes must also be annouced to labeled-unicast update-groups
- */
- if (safi == SAFI_UNICAST)
- group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest,
- new_select);
-
/* FIB update. */
if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)
&& !bgp_option_check(BGP_OPT_NO_FIB)) {
@@ -3517,10 +3796,11 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
*/
if (old_select &&
is_route_parent_evpn(old_select))
- bgp_zebra_withdraw(p, old_select, bgp, afi,
- safi);
+ bgp_zebra_route_install(dest, old_select, bgp,
+ false, NULL, false);
- bgp_zebra_announce(dest, p, new_select, bgp, afi, safi);
+ bgp_zebra_route_install(dest, new_select, bgp, true,
+ NULL, false);
} else {
/* Withdraw the route from the kernel. */
if (old_select && old_select->type == ZEBRA_ROUTE_BGP
@@ -3528,11 +3808,20 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
|| old_select->sub_type == BGP_ROUTE_AGGREGATE
|| old_select->sub_type == BGP_ROUTE_IMPORTED))
- bgp_zebra_withdraw(p, old_select, bgp, afi,
- safi);
+ bgp_zebra_route_install(dest, old_select, bgp,
+ false, NULL, false);
}
}
+ group_announce_route(bgp, afi, safi, dest, new_select);
+
+ /* unicast routes must also be annouced to labeled-unicast update-groups
+ */
+ if (safi == SAFI_UNICAST)
+ group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest,
+ new_select);
+
+
bgp_process_evpn_route_injection(bgp, afi, safi, dest, new_select,
old_select);
@@ -3683,13 +3972,38 @@ static struct bgp_process_queue *bgp_processq_alloc(struct bgp *bgp)
return pqnode;
}
-void bgp_process(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi)
+void bgp_process(struct bgp *bgp, struct bgp_dest *dest,
+ struct bgp_path_info *pi, afi_t afi, safi_t safi)
{
#define ARBITRARY_PROCESS_QLEN 10000
struct work_queue *wq = bgp->process_queue;
struct bgp_process_queue *pqnode;
int pqnode_reuse = 0;
+ /*
+ * Indicate that *this* pi is in an unsorted
+ * situation, even if the node is already
+ * scheduled.
+ */
+ if (pi) {
+ struct bgp_path_info *first = bgp_dest_get_bgp_path_info(dest);
+
+ SET_FLAG(pi->flags, BGP_PATH_UNSORTED);
+
+ if (pi != first) {
+ if (pi->next)
+ pi->next->prev = pi->prev;
+ if (pi->prev)
+ pi->prev->next = pi->next;
+
+ if (first)
+ first->prev = pi;
+ pi->next = first;
+ pi->prev = NULL;
+ bgp_dest_set_bgp_path_info(dest, pi);
+ }
+ }
+
/* already scheduled for processing? */
if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED))
return;
@@ -3938,7 +4252,7 @@ void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
}
hook_call(bgp_process, peer->bgp, afi, safi, dest, peer, true);
- bgp_process(peer->bgp, dest, afi, safi);
+ bgp_process(peer->bgp, dest, pi, afi, safi);
}
static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi,
@@ -4426,7 +4740,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (pi && pi->attr->rmap_table_id != new_attr.rmap_table_id) {
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
/* remove from RIB previous entry */
- bgp_zebra_withdraw(p, pi, bgp, afi, safi);
+ bgp_zebra_route_install(dest, pi, bgp, false, NULL,
+ false);
}
if (peer->sort == BGP_PEER_EBGP) {
@@ -4543,7 +4858,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
!= BGP_DAMP_SUPPRESSED) {
bgp_aggregate_increment(bgp, p, pi, afi,
safi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
} else /* Duplicate - odd */
{
@@ -4571,7 +4886,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
bgp_path_info_unset_flag(
dest, pi, BGP_PATH_STALE);
bgp_dest_set_defer_flag(dest, false);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
}
@@ -4861,7 +5176,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Process change. */
bgp_aggregate_increment(bgp, p, pi, afi, safi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
bgp_dest_unlock_node(dest);
if (SAFI_UNICAST == safi
@@ -5006,7 +5321,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
hook_call(bgp_process, bgp, afi, safi, dest, peer, false);
/* Process change. */
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, new, afi, safi);
if (SAFI_UNICAST == safi
&& (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
@@ -5577,7 +5892,7 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data)
struct bgp_clear_node_queue *cnq = data;
struct bgp_dest *dest = cnq->dest;
struct peer *peer = wq->spec.data;
- struct bgp_path_info *pi;
+ struct bgp_path_info *pi, *next;
struct bgp *bgp;
afi_t afi = bgp_dest_table(dest)->afi;
safi_t safi = bgp_dest_table(dest)->safi;
@@ -5588,7 +5903,8 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data)
/* It is possible that we have multiple paths for a prefix from a peer
* if that peer is using AddPath.
*/
- for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (next = pi->next, 1); pi = next) {
if (pi->peer != peer)
continue;
@@ -5850,7 +6166,7 @@ void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi)
void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
{
struct bgp_dest *dest;
- struct bgp_path_info *pi;
+ struct bgp_path_info *pi, *next;
struct bgp_table *table;
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
@@ -5865,8 +6181,9 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
for (rm = bgp_table_top(table); rm;
rm = bgp_route_next(rm))
- for (pi = bgp_dest_get_bgp_path_info(rm); pi;
- pi = pi->next) {
+ for (pi = bgp_dest_get_bgp_path_info(rm);
+ (pi != NULL) && (next = pi->next, 1);
+ pi = next) {
if (pi->peer != peer)
continue;
if (CHECK_FLAG(
@@ -5899,8 +6216,8 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
} else {
for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest))
- for (pi = bgp_dest_get_bgp_path_info(dest); pi;
- pi = pi->next) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (next = pi->next, 1); pi = next) {
if (pi->peer != peer)
continue;
if (CHECK_FLAG(peer->af_sflags[afi][safi],
@@ -6006,10 +6323,10 @@ bool bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
if (peer->sort == BGP_PEER_IBGP)
return true;
- if (peer->sort == BGP_PEER_EBGP
- && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter)
- || FILTER_LIST_OUT_NAME(filter)
- || DISTRIBUTE_OUT_NAME(filter)))
+ if (peer->sort == BGP_PEER_EBGP &&
+ (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter) ||
+ FILTER_LIST_OUT_NAME(filter) || DISTRIBUTE_OUT_NAME(filter) ||
+ UNSUPPRESS_MAP_NAME(filter)))
return true;
return false;
}
@@ -6052,8 +6369,7 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table,
|| pi->sub_type == BGP_ROUTE_IMPORTED)) {
if (bgp_fibupd_safi(safi))
- bgp_zebra_withdraw(p, pi, bgp, afi,
- safi);
+ bgp_zebra_withdraw_actual(dest, pi, bgp);
}
dest = bgp_path_info_reap(dest, pi);
@@ -6286,13 +6602,14 @@ static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi,
struct bgp_path_info *bpi,
const struct prefix *p,
struct bgp_dest *dest,
- struct bgp *bgp)
+ struct bgp *bgp,
+ struct bgp *bgp_nexthop)
{
/* Nexthop reachability check. */
if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST) {
if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) {
- if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, bpi,
- NULL, 0, p))
+ if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi,
+ bpi, NULL, 0, p))
bgp_path_info_set_flag(dest, bpi,
BGP_PATH_VALID);
else {
@@ -6350,6 +6667,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
mpls_label_t label = 0;
#endif
uint32_t num_labels = 0;
+ struct bgp *bgp_nexthop = bgp;
assert(bgp_static);
@@ -6504,13 +6822,15 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
&pi->extra->label[0]);
}
#endif
+ if (pi->extra && pi->extra->vrfleak->bgp_orig)
+ bgp_nexthop = pi->extra->vrfleak->bgp_orig;
bgp_nexthop_reachability_check(afi, safi, pi, p, dest,
- bgp);
+ bgp, bgp_nexthop);
/* Process change. */
bgp_aggregate_increment(bgp, p, pi, afi, safi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
if (SAFI_MPLS_VPN == safi &&
bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
@@ -6556,7 +6876,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
#endif
}
- bgp_nexthop_reachability_check(afi, safi, new, p, dest, bgp);
+ bgp_nexthop_reachability_check(afi, safi, new, p, dest, bgp, bgp);
/* Aggregate address increment. */
bgp_aggregate_increment(bgp, p, new, afi, safi);
@@ -6568,7 +6888,7 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p,
bgp_dest_unlock_node(dest);
/* Process change. */
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, new, afi, safi);
if (SAFI_UNICAST == safi &&
(bgp->inst_type == BGP_INSTANCE_TYPE_VRF ||
@@ -6607,6 +6927,7 @@ void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi,
/* Withdraw static BGP route from routing table. */
if (pi) {
+ SET_FLAG(pi->flags, BGP_PATH_UNSORTED);
#ifdef ENABLE_BGP_VNC
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
rfapiProcessWithdraw(pi->peer, NULL, p, prd, pi->attr,
@@ -6625,7 +6946,7 @@ void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi,
bgp_aggregate_decrement(bgp, p, pi, afi, safi);
bgp_unlink_nexthop(pi);
bgp_path_info_delete(dest, pi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
/* Unlock bgp_node_lookup. */
@@ -7026,7 +7347,7 @@ static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi,
safi);
bgp_unlink_nexthop(pi);
bgp_path_info_delete(dest, pi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
}
}
@@ -7389,8 +7710,10 @@ static void bgp_aggregate_install(
/*
* Mark the old as unusable
*/
- if (pi)
+ if (pi) {
bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, pi, afi, safi);
+ }
attr = bgp_attr_aggregate_intern(
bgp, origin, aspath, community, ecommunity, lcommunity,
@@ -7415,7 +7738,7 @@ static void bgp_aggregate_install(
SET_FLAG(new->flags, BGP_PATH_VALID);
bgp_path_info_add(dest, new);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, new, afi, safi);
} else {
uninstall_aggregate_route:
for (pi = orig; pi; pi = pi->next)
@@ -7427,7 +7750,7 @@ static void bgp_aggregate_install(
/* Withdraw static BGP route from routing table. */
if (pi) {
bgp_path_info_delete(dest, pi);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest, pi, afi, safi);
}
}
@@ -7513,7 +7836,6 @@ void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate,
const struct prefix *dest_p;
struct bgp_dest *dest, *top;
struct bgp_path_info *pi;
- bool toggle_suppression;
/* We've found a different MED we must revert any suppressed routes. */
top = bgp_node_get(table, p);
@@ -7523,7 +7845,6 @@ void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate,
if (dest_p->prefixlen <= p->prefixlen)
continue;
- toggle_suppression = false;
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
if (BGP_PATH_HOLDDOWN(pi))
continue;
@@ -7534,17 +7855,14 @@ void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate,
if (suppress) {
/* Suppress route if not suppressed already. */
if (aggr_suppress_path(aggregate, pi))
- toggle_suppression = true;
+ bgp_process(bgp, dest, pi, afi, safi);
continue;
}
/* Install route if there is no more suppression. */
if (aggr_unsuppress_path(aggregate, pi))
- toggle_suppression = true;
+ bgp_process(bgp, dest, pi, afi, safi);
}
-
- if (toggle_suppression)
- bgp_process(bgp, dest, afi, safi);
}
bgp_dest_unlock_node(top);
}
@@ -7603,7 +7921,6 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
struct ecommunity *ecommunity = NULL;
struct lcommunity *lcommunity = NULL;
struct bgp_path_info *pi;
- unsigned long match = 0;
uint8_t atomic_aggregate = 0;
/* If the bgp instance is being deleted or self peer is deleted
@@ -7653,8 +7970,6 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
if (!bgp_check_advertise(bgp, dest, safi))
continue;
- match = 0;
-
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
if (BGP_PATH_HOLDDOWN(pi))
continue;
@@ -7678,7 +7993,7 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
if (aggregate->summary_only
&& AGGREGATE_MED_VALID(aggregate)) {
if (aggr_suppress_path(aggregate, pi))
- match++;
+ bgp_process(bgp, dest, pi, afi, safi);
}
/*
@@ -7694,7 +8009,7 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
&& AGGREGATE_MED_VALID(aggregate)
&& aggr_suppress_map_test(bgp, aggregate, pi)) {
if (aggr_suppress_path(aggregate, pi))
- match++;
+ bgp_process(bgp, dest, pi, afi, safi);
}
aggregate->count++;
@@ -7755,8 +8070,6 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
aggregate,
bgp_attr_get_lcommunity(pi->attr));
}
- if (match)
- bgp_process(bgp, dest, afi, safi);
}
if (aggregate->as_set) {
bgp_compute_aggregate_aspath_val(aggregate);
@@ -7816,7 +8129,6 @@ void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi,
struct bgp_dest *top;
struct bgp_dest *dest;
struct bgp_path_info *pi;
- unsigned long match;
table = bgp->rib[afi][safi];
@@ -7828,7 +8140,6 @@ void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi,
if (dest_p->prefixlen <= p->prefixlen)
continue;
- match = 0;
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
if (BGP_PATH_HOLDDOWN(pi))
@@ -7846,10 +8157,11 @@ void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi,
if (pi->extra && pi->extra->aggr_suppressors &&
listcount(pi->extra->aggr_suppressors)) {
if (aggr_unsuppress_path(aggregate, pi))
- match++;
+ bgp_process(bgp, dest, pi, afi, safi);
}
- aggregate->count--;
+ if (aggregate->count > 0)
+ aggregate->count--;
if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
aggregate->incomplete_origin_count--;
@@ -7888,10 +8200,6 @@ void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi,
pi->attr));
}
}
-
- /* If this node was suppressed, process the change. */
- if (match)
- bgp_process(bgp, dest, afi, safi);
}
if (aggregate->as_set) {
aspath_free(aggregate->aspath);
@@ -8040,7 +8348,6 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi,
struct community *community = NULL;
struct ecommunity *ecommunity = NULL;
struct lcommunity *lcommunity = NULL;
- unsigned long match = 0;
/* If the bgp instance is being deleted or self peer is deleted
* then do not create aggregate route
@@ -8057,12 +8364,12 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi,
if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate))
if (aggr_unsuppress_path(aggregate, pi))
- match++;
+ bgp_process(bgp, pi->net, pi, afi, safi);
if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate)
&& aggr_suppress_map_test(bgp, aggregate, pi))
if (aggr_unsuppress_path(aggregate, pi))
- match++;
+ bgp_process(bgp, pi->net, pi, afi, safi);
/*
* This must be called after `summary`, `suppress-map` check to avoid
@@ -8104,10 +8411,6 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi,
aggregate, bgp_attr_get_lcommunity(pi->attr));
}
- /* If this node was suppressed, process the change. */
- if (match)
- bgp_process(bgp, pi->net, afi, safi);
-
origin = BGP_ORIGIN_IGP;
if (aggregate->incomplete_origin_count > 0)
origin = BGP_ORIGIN_INCOMPLETE;
@@ -8717,7 +9020,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
/* Process change. */
bgp_aggregate_increment(bgp, p, bpi, afi,
SAFI_UNICAST);
- bgp_process(bgp, bn, afi, SAFI_UNICAST);
+ bgp_process(bgp, bn, bpi, afi, SAFI_UNICAST);
bgp_dest_unlock_node(bn);
aspath_unintern(&attr.aspath);
@@ -8740,7 +9043,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
bgp_path_info_add(bn, new);
bgp_dest_unlock_node(bn);
SET_FLAG(bn->flags, BGP_NODE_FIB_INSTALLED);
- bgp_process(bgp, bn, afi, SAFI_UNICAST);
+ bgp_process(bgp, bn, new, afi, SAFI_UNICAST);
if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
|| (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
@@ -8781,7 +9084,7 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type,
}
bgp_aggregate_decrement(bgp, p, pi, afi, SAFI_UNICAST);
bgp_path_info_delete(dest, pi);
- bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ bgp_process(bgp, dest, pi, afi, SAFI_UNICAST);
}
bgp_dest_unlock_node(dest);
}
@@ -8815,7 +9118,7 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,
bgp_path_info_delete(dest, pi);
if (!CHECK_FLAG(bgp->flags,
BGP_FLAG_DELETE_IN_PROGRESS))
- bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ bgp_process(bgp, dest, pi, afi, SAFI_UNICAST);
else {
dest = bgp_path_info_reap(dest, pi);
assert(dest);
@@ -8961,6 +9264,9 @@ static void route_vty_short_status_out(struct vty *vty,
if (path->extra && bgp_path_suppressed(path))
json_object_boolean_true_add(json_path, "suppressed");
+ if (CHECK_FLAG(path->flags, BGP_PATH_UNSORTED))
+ json_object_boolean_true_add(json_path, "unsorted");
+
if (CHECK_FLAG(path->flags, BGP_PATH_VALID)
&& !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
json_object_boolean_true_add(json_path, "valid");
@@ -9023,6 +9329,8 @@ static void route_vty_short_status_out(struct vty *vty,
/* Selected */
if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
vty_out(vty, "h");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_UNSORTED))
+ vty_out(vty, "u");
else if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED))
vty_out(vty, "d");
else if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
@@ -9038,6 +9346,9 @@ static void route_vty_short_status_out(struct vty *vty,
vty_out(vty, "i");
else
vty_out(vty, " ");
+
+ /* adding space between next column */
+ vty_out(vty, " ");
}
static char *bgp_nexthop_hostname(struct peer *peer,
@@ -9392,14 +9703,16 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
}
/* MED/Metric */
- if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ if (use_bgp_med_value(attr, path->peer->bgp)) {
+ uint32_t value = bgp_med_value(attr, path->peer->bgp);
+
if (json_paths)
- json_object_int_add(json_path, "metric", attr->med);
+ json_object_int_add(json_path, "metric", value);
else if (wide)
- vty_out(vty, "%7u", attr->med);
+ vty_out(vty, "%7u", value);
else
- vty_out(vty, "%10u", attr->med);
- else if (!json_paths) {
+ vty_out(vty, "%10u", value);
+ } else if (!json_paths) {
if (wide)
vty_out(vty, "%*s", 7, " ");
else
@@ -9520,7 +9833,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
}
/* called from terminal list command */
-void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest,
+void route_vty_out_tmp(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest,
const struct prefix *p, struct attr *attr, safi_t safi,
bool use_json, json_object *json_ar, bool wide)
{
@@ -9579,10 +9892,11 @@ void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest,
&attr->mp_nexthop_global_in);
}
- if (attr->flag
- & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
- json_object_int_add(json_net, "metric",
- attr->med);
+ if (use_bgp_med_value(attr, bgp)) {
+ uint32_t value = bgp_med_value(attr, bgp);
+
+ json_object_int_add(json_net, "metric", value);
+ }
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
json_object_int_add(json_net, "locPrf",
@@ -9622,13 +9936,15 @@ void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest,
else
vty_out(vty, "%*s", len, " ");
}
- if (attr->flag
- & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+
+ if (use_bgp_med_value(attr, bgp)) {
+ uint32_t value = bgp_med_value(attr, bgp);
+
if (wide)
- vty_out(vty, "%7u", attr->med);
+ vty_out(vty, "%7u", value);
else
- vty_out(vty, "%10u", attr->med);
- else if (wide)
+ vty_out(vty, "%10u", value);
+ } else if (wide)
vty_out(vty, " ");
else
vty_out(vty, " ");
@@ -10625,11 +10941,13 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
vty_out(vty, " Origin %s",
bgp_origin_long_str[attr->origin]);
- if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (use_bgp_med_value(attr, bgp)) {
+ uint32_t value = bgp_med_value(attr, bgp);
+
if (json_paths)
- json_object_int_add(json_path, "metric", attr->med);
+ json_object_int_add(json_path, "metric", value);
else
- vty_out(vty, ", metric %u", attr->med);
+ vty_out(vty, ", metric %u", value);
}
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
@@ -11869,7 +12187,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
} else {
if (incremental_print) {
vty_out(vty, "\"prefix\": \"%pFX\",\n", p);
- vty_out(vty, "\"version\": \"%" PRIu64 "\",\n",
+ vty_out(vty, "\"version\": \"%" PRIu64 "\",",
dest->version);
} else {
json_object_string_addf(json, "prefix", "%pFX",
@@ -13660,21 +13978,23 @@ enum bgp_pcounts {
PCOUNT_COUNTED,
PCOUNT_BPATH_SELECTED,
PCOUNT_PFCNT, /* the figure we display to users */
+ PCOUNT_UNSORTED,
PCOUNT_MAX,
};
static const char *const pcount_strs[] = {
- [PCOUNT_ADJ_IN] = "Adj-in",
- [PCOUNT_DAMPED] = "Damped",
- [PCOUNT_REMOVED] = "Removed",
- [PCOUNT_HISTORY] = "History",
- [PCOUNT_STALE] = "Stale",
- [PCOUNT_VALID] = "Valid",
- [PCOUNT_ALL] = "All RIB",
- [PCOUNT_COUNTED] = "PfxCt counted",
- [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected",
- [PCOUNT_PFCNT] = "Useable",
- [PCOUNT_MAX] = NULL,
+ [PCOUNT_ADJ_IN] = "Adj-in",
+ [PCOUNT_DAMPED] = "Damped",
+ [PCOUNT_REMOVED] = "Removed",
+ [PCOUNT_HISTORY] = "History",
+ [PCOUNT_STALE] = "Stale",
+ [PCOUNT_VALID] = "Valid",
+ [PCOUNT_ALL] = "All RIB",
+ [PCOUNT_COUNTED] = "PfxCt counted",
+ [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected",
+ [PCOUNT_PFCNT] = "Useable",
+ [PCOUNT_UNSORTED] = "Unsorted",
+ [PCOUNT_MAX] = NULL,
};
struct peer_pcounts {
@@ -13715,6 +14035,8 @@ static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc)
pc->count[PCOUNT_PFCNT]++;
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
pc->count[PCOUNT_BPATH_SELECTED]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_UNSORTED))
+ pc->count[PCOUNT_UNSORTED]++;
if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
pc->count[PCOUNT_COUNTED]++;
@@ -14273,7 +14595,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
json_ar, json_net,
"%pFX", rn_p);
} else
- route_vty_out_tmp(vty, dest, rn_p,
+ route_vty_out_tmp(vty, bgp, dest, rn_p,
&attr, safi, use_json,
json_ar, wide);
bgp_attr_flush(&attr);
@@ -14336,11 +14658,15 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
"%pFX",
rn_p);
} else
- route_vty_out_tmp(
- vty, dest, rn_p,
- &attr, safi,
- use_json,
- json_ar, wide);
+ route_vty_out_tmp(vty,
+ bgp,
+ dest,
+ rn_p,
+ &attr,
+ safi,
+ use_json,
+ json_ar,
+ wide);
(*output_count)++;
} else {
(*filtered_count)++;
@@ -14378,9 +14704,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
json_ar, json_net,
"%pFX", rn_p);
} else
- route_vty_out_tmp(
- vty, dest, rn_p, pi->attr, safi,
- use_json, json_ar, wide);
+ route_vty_out_tmp(vty, bgp, dest, rn_p,
+ pi->attr, safi,
+ use_json, json_ar,
+ wide);
(*output_count)++;
}
}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 2d82f0f206..25fc327a17 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -59,15 +59,15 @@ enum bgp_show_adj_route_type {
#define BGP_SHOW_SCODE_HEADER \
"Status codes: s suppressed, d damped, " \
- "h history, * valid, > best, = multipath,\n" \
+ "h history, u unsorted, * valid, > best, = multipath,\n" \
" i internal, r RIB-failure, S Stale, R Removed\n"
#define BGP_SHOW_OCODE_HEADER \
"Origin codes: i - IGP, e - EGP, ? - incomplete\n"
#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n"
#define BGP_SHOW_RPKI_HEADER \
"RPKI validation codes: V valid, I invalid, N Not found\n\n"
-#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
-#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n"
+#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
+#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n"
/* Maximum number of labels we can process or send with a prefix. We
* really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN.
@@ -327,6 +327,7 @@ struct bgp_path_info {
#define BGP_PATH_ACCEPT_OWN (1 << 16)
#define BGP_PATH_MPLSVPN_LABEL_NH (1 << 17)
#define BGP_PATH_MPLSVPN_NH_LABEL_BIND (1 << 18)
+#define BGP_PATH_UNSORTED (1 << 19)
/* BGP route type. This can be static, RIP, OSPF, BGP etc. */
uint8_t type;
@@ -345,6 +346,8 @@ struct bgp_path_info {
unsigned short instance;
+ enum bgp_path_selection_reason reason;
+
/* Addpath identifiers */
uint32_t addpath_rx_id;
struct bgp_addpath_info_data tx_addpath;
@@ -801,7 +804,8 @@ extern void bgp_withdraw(struct peer *peer, const struct prefix *p,
struct bgp_route_evpn *evpn);
/* for bgp_nexthop and bgp_damp */
-extern void bgp_process(struct bgp *, struct bgp_dest *, afi_t, safi_t);
+extern void bgp_process(struct bgp *bgp, struct bgp_dest *dest,
+ struct bgp_path_info *pi, afi_t afi, safi_t safi);
/*
* Add an end-of-initial-update marker to the process queue. This is just a
@@ -845,10 +849,10 @@ extern void route_vty_out(struct vty *vty, const struct prefix *p,
extern void route_vty_out_tag(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
safi_t safi, json_object *json);
-extern void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest,
- const struct prefix *p, struct attr *attr,
- safi_t safi, bool use_json, json_object *json_ar,
- bool wide);
+extern void route_vty_out_tmp(struct vty *vty, struct bgp *bgp,
+ struct bgp_dest *dest, const struct prefix *p,
+ struct attr *attr, safi_t safi, bool use_json,
+ json_object *json_ar, bool wide);
extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
json_object *json);
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index a091b67680..15828b6594 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -4460,6 +4460,13 @@ static void bgp_route_map_update_peer_group(const char *rmap_name,
filter->map[direct].name)
== 0))
filter->map[direct].map = map;
+
+ if (group->conf->default_rmap[afi][safi].name &&
+ strmatch(group->conf->default_rmap[afi][safi]
+ .name,
+ rmap_name))
+ group->conf->default_rmap[afi][safi].map =
+ map;
}
if (filter->usmap.name
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 56b0263bf6..67f59edb93 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -2088,16 +2088,18 @@ DEFPY (show_rpki_prefix_table,
DEFPY (show_rpki_as_number,
show_rpki_as_number_cmd,
- "show rpki as-number ASNUM$by_asn [vrf NAME$vrfname] [json$uj]",
+ "show rpki as-number <0$zero|ASNUM$by_asn> [vrf NAME$vrfname] [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
"Lookup by ASN in prefix table\n"
+ "AS Number of 0, see RFC-7607\n"
"AS Number\n"
VRF_CMD_HELP_STR
JSON_STR)
{
struct json_object *json = NULL;
struct rpki_vrf *rpki_vrf;
+ as_t as;
if (uj)
json = json_object_new_object();
@@ -2118,18 +2120,24 @@ DEFPY (show_rpki_as_number,
return CMD_WARNING;
}
- print_prefix_table_by_asn(vty, by_asn, rpki_vrf, json);
+ if (zero)
+ as = 0;
+ else
+ as = by_asn;
+
+ print_prefix_table_by_asn(vty, as, rpki_vrf, json);
return CMD_SUCCESS;
}
DEFPY (show_rpki_prefix,
show_rpki_prefix_cmd,
- "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [ASNUM$asn] [vrf NAME$vrfname] [json$uj]",
+ "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [0$zero|ASNUM$asn] [vrf NAME$vrfname] [json$uj]",
SHOW_STR
RPKI_OUTPUT_STRING
"Lookup IP prefix and optionally ASN in prefix table\n"
"IPv4 prefix\n"
"IPv6 prefix\n"
+ "AS Number of 0, see RFC-7607\n"
"AS Number\n"
VRF_CMD_HELP_STR
JSON_STR)
@@ -2138,6 +2146,7 @@ DEFPY (show_rpki_prefix,
json_object *json_records = NULL;
enum asnotation_mode asnotation;
struct rpki_vrf *rpki_vrf;
+ as_t as;
if (uj)
json = json_object_new_object();
@@ -2153,6 +2162,11 @@ DEFPY (show_rpki_prefix,
return CMD_WARNING;
}
+ if (zero)
+ as = 0;
+ else
+ as = asn;
+
struct lrtr_ip_addr addr;
char addr_str[INET6_ADDRSTRLEN];
size_t addr_len = strchr(prefix_str, '/') - prefix_str;
@@ -2174,7 +2188,7 @@ DEFPY (show_rpki_prefix,
enum pfxv_state result;
if (pfx_table_validate_r(rpki_vrf->rtr_config->pfx_table, &matches,
- &match_count, asn, &addr, prefix->prefixlen,
+ &match_count, as, &addr, prefix->prefixlen,
&result) != PFX_SUCCESS) {
if (json) {
json_object_string_add(json, "error", "Prefix lookup failed.");
@@ -2198,7 +2212,7 @@ DEFPY (show_rpki_prefix,
const struct pfx_record *record = &matches[i];
if (record->max_len >= prefix->prefixlen &&
- ((asn != 0 && (uint32_t)asn == record->asn) || asn == 0)) {
+ ((as != 0 && (uint32_t)as == record->asn) || asn == 0)) {
print_record(&matches[i], vty, json_records,
asnotation);
}
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 8465ada996..781909b65d 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -239,7 +239,11 @@ static ssize_t printfrr_bd(struct fbuf *buf, struct printfrr_eargs *ea,
if (!dest)
return bputs(buf, "(null)");
+#if !defined(DEV_BUILD)
/* need to get the real length even if buffer too small */
prefix2str(p, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
+#else
+ return bprintfrr(buf, "%s(%p)", prefix2str(p, cbuf, sizeof(cbuf)), dest);
+#endif
}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 5b4c3be212..130f5ca749 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -76,6 +76,11 @@ struct bgp_dest {
STAILQ_ENTRY(bgp_dest) pq;
+ struct zebra_announce_item zai;
+ struct bgp_path_info *za_bgp_pi;
+ struct bgpevpn *za_vpn;
+ bool za_is_sync;
+
uint64_t version;
mpls_label_t local_label;
@@ -91,12 +96,16 @@ struct bgp_dest {
#define BGP_NODE_LABEL_REQUESTED (1 << 7)
#define BGP_NODE_SOFT_RECONFIG (1 << 8)
#define BGP_NODE_PROCESS_CLEAR (1 << 9)
+#define BGP_NODE_SCHEDULE_FOR_INSTALL (1 << 10)
+#define BGP_NODE_SCHEDULE_FOR_DELETE (1 << 11)
struct bgp_addpath_node_data tx_addpath;
enum bgp_path_selection_reason reason;
};
+DECLARE_LIST(zebra_announce, struct bgp_dest, zai);
+
extern void bgp_delete_listnode(struct bgp_dest *dest);
/*
* bgp_table_iter_t
diff --git a/bgpd/bgp_trace.c b/bgpd/bgp_trace.c
index 02afbeb46f..af45e7a9b8 100644
--- a/bgpd/bgp_trace.c
+++ b/bgpd/bgp_trace.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#define TRACEPOINT_CREATE_PROBES
#define TRACEPOINT_DEFINE
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index c522865eba..124e7a388b 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -151,7 +151,6 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->change_local_as = src->change_local_as;
dst->shared_network = src->shared_network;
dst->local_role = src->local_role;
- dst->as_path_loop_detection = src->as_path_loop_detection;
if (src->soo[afi][safi]) {
ecommunity_free(&dst->soo[afi][safi]);
@@ -360,9 +359,12 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(peer->max_packet_size, key);
key = jhash_1word(peer->pmax_out[afi][safi], key);
- if (peer->as_path_loop_detection)
- key = jhash_2words(peer->as, peer->as_path_loop_detection, key);
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_AS_LOOP_DETECTION))
+ key = jhash_2words(peer->as,
+ CHECK_FLAG(peer->flags,
+ PEER_FLAG_AS_LOOP_DETECTION),
+ key);
if (peer->group)
key = jhash_1word(jhash(peer->group->name,
strlen(peer->group->name), SEED1),
@@ -443,7 +445,7 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word((peer->flags & PEER_FLAG_AIGP), key);
if (peer->soo[afi][safi]) {
- char *soo_str = ecommunity_str(peer->soo[afi][safi]);
+ const char *soo_str = ecommunity_str(peer->soo[afi][safi]);
key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key);
}
@@ -464,7 +466,8 @@ static unsigned int updgrp_hash_key_make(const void *p)
CHECK_FLAG(peer->af_cap[afi][safi],
PEER_UPDGRP_AF_CAP_FLAGS),
peer->v_routeadv, peer->change_local_as,
- peer->as_path_loop_detection);
+ !!CHECK_FLAG(peer->flags,
+ PEER_FLAG_AS_LOOP_DETECTION));
zlog_debug("%pBP Update Group Hash: addpath paths-limit: (send %u, receive %u)",
peer, peer->addpath_paths_limit[afi][safi].send,
peer->addpath_paths_limit[afi][safi].receive);
@@ -1729,18 +1732,6 @@ static int update_group_walkcb(struct hash_bucket *bucket, void *arg)
return ret;
}
-static int update_group_periodic_merge_walkcb(struct update_group *updgrp,
- void *arg)
-{
- struct update_subgroup *subgrp;
- struct update_subgroup *tmp_subgrp;
- const char *reason = arg;
-
- UPDGRP_FOREACH_SUBGRP_SAFE (updgrp, subgrp, tmp_subgrp)
- update_subgroup_check_merge(subgrp, reason);
- return UPDWALK_CONTINUE;
-}
-
/********************
* PUBLIC FUNCTIONS
********************/
@@ -2079,14 +2070,6 @@ void update_group_walk(struct bgp *bgp, updgrp_walkcb cb, void *ctx)
}
}
-void update_group_periodic_merge(struct bgp *bgp)
-{
- char reason[] = "periodic merge check";
-
- update_group_walk(bgp, update_group_periodic_merge_walkcb,
- (void *)reason);
-}
-
static int
update_group_default_originate_route_map_walkcb(struct update_group *updgrp,
void *arg)
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
index d4c6ecfdbb..d0fd226d99 100644
--- a/bgpd/bgp_updgrp.h
+++ b/bgpd/bgp_updgrp.h
@@ -369,7 +369,6 @@ extern void update_group_policy_update(struct bgp *bgp,
extern void update_group_af_walk(struct bgp *bgp, afi_t afi, safi_t safi,
updgrp_walkcb cb, void *ctx);
extern void update_group_walk(struct bgp *bgp, updgrp_walkcb cb, void *ctx);
-extern void update_group_periodic_merge(struct bgp *bgp);
extern void
update_group_refresh_default_originate_route_map(struct event *thread);
extern void update_group_start_advtimer(struct bgp *bgp);
@@ -441,7 +440,7 @@ extern struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp,
extern void bgp_adj_out_remove_subgroup(struct bgp_dest *dest,
struct bgp_adj_out *adj,
struct update_subgroup *subgrp);
-extern void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+extern bool bgp_adj_out_set_subgroup(struct bgp_dest *dest,
struct update_subgroup *subgrp,
struct attr *attr,
struct bgp_path_info *path);
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index cc039e3e11..0a852c75de 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -328,15 +328,16 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp,
}
if ((flags & UPDWALK_FLAGS_ADVQUEUE) && adj->adv &&
adj->adv->baa) {
- route_vty_out_tmp(
- vty, dest, dest_p, adj->adv->baa->attr,
- SUBGRP_SAFI(subgrp), 0, NULL, false);
+ route_vty_out_tmp(vty, bgp, dest, dest_p,
+ adj->adv->baa->attr,
+ SUBGRP_SAFI(subgrp), 0, NULL,
+ false);
output_count++;
}
if ((flags & UPDWALK_FLAGS_ADVERTISED) && adj->attr) {
- route_vty_out_tmp(vty, dest, dest_p, adj->attr,
- SUBGRP_SAFI(subgrp), 0, NULL,
- false);
+ route_vty_out_tmp(vty, bgp, dest, dest_p,
+ adj->attr, SUBGRP_SAFI(subgrp),
+ 0, NULL, false);
output_count++;
}
}
@@ -520,7 +521,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
return next;
}
-void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+bool bgp_adj_out_set_subgroup(struct bgp_dest *dest,
struct update_subgroup *subgrp, struct attr *attr,
struct bgp_path_info *path)
{
@@ -532,7 +533,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
struct peer *adv_peer;
struct peer_af *paf;
struct bgp *bgp;
- uint32_t attr_hash = attrhash_key_make(attr);
+ uint32_t attr_hash = 0;
peer = SUBGRP_PEER(subgrp);
afi = SUBGRP_AFI(subgrp);
@@ -540,7 +541,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
bgp = SUBGRP_INST(subgrp);
if (DISABLE_BGP_ANNOUNCE)
- return;
+ return false;
/* Look for adjacency information. */
adj = adj_lookup(
@@ -556,7 +557,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
bgp_addpath_id_for_peer(peer, afi, safi,
&path->tx_addpath));
if (!adj)
- return;
+ return false;
subgrp->pscount++;
}
@@ -567,9 +568,11 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
* the route wasn't changed actually.
* Do not suppress BGP UPDATES for route-refresh.
*/
- if (CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES)
- && !CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES)
- && adj->attr_hash == attr_hash) {
+ if (likely(CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES)))
+ attr_hash = attrhash_key_make(attr);
+
+ if (!CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES) &&
+ attr_hash && adj->attr_hash == attr_hash) {
if (BGP_DEBUG(update, UPDATE_OUT)) {
char attr_str[BUFSIZ] = {0};
@@ -595,7 +598,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
* will never be able to coalesce the 3rd peer down
*/
subgrp->version = MAX(subgrp->version, dest->version);
- return;
+ return false;
}
if (adj->adv)
@@ -643,6 +646,8 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
bgp_adv_fifo_add_tail(&subgrp->sync->update, adv);
subgrp->version = MAX(subgrp->version, dest->version);
+
+ return true;
}
/* The only time 'withdraw' will be false is if we are sending
@@ -852,8 +857,9 @@ void subgroup_announce_route(struct update_subgroup *subgrp)
void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
{
struct bgp *bgp;
- struct attr attr;
+ struct attr attr = { 0 };
struct attr *new_attr = &attr;
+ struct aspath *aspath;
struct prefix p;
struct peer *from;
struct bgp_dest *dest;
@@ -891,6 +897,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
/* make coverity happy */
assert(attr.aspath);
+ aspath = attr.aspath;
attr.med = 0;
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
@@ -991,18 +998,19 @@ void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
if (dest) {
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
- if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
- if (subgroup_announce_check(
- dest, pi, subgrp,
- bgp_dest_get_prefix(dest),
- &attr, NULL)) {
- struct attr *default_attr =
- bgp_attr_intern(&attr);
-
- bgp_adj_out_set_subgroup(
- dest, subgrp,
- default_attr, pi);
- }
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ continue;
+
+ if (subgroup_announce_check(dest, pi, subgrp,
+ bgp_dest_get_prefix(
+ dest),
+ &attr, NULL)) {
+ if (!bgp_adj_out_set_subgroup(dest,
+ subgrp,
+ &attr, pi))
+ bgp_attr_flush(&attr);
+ } else
+ bgp_attr_flush(&attr);
}
bgp_dest_unlock_node(dest);
}
@@ -1046,7 +1054,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw)
}
}
- aspath_unintern(&attr.aspath);
+ aspath_unintern(&aspath);
}
/*
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
index 8fe24ebe20..2470fb8844 100644
--- a/bgpd/bgp_vpn.c
+++ b/bgpd/bgp_vpn.c
@@ -191,7 +191,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
}
rd_header = 0;
}
- route_vty_out_tmp(vty, rm, bgp_dest_get_prefix(rm),
+ route_vty_out_tmp(vty, bgp, rm, bgp_dest_get_prefix(rm),
attr, safi, use_json, json_routes,
false);
output_count++;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 2920405780..f8d8e8ad3a 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -126,6 +126,10 @@ FRR_CFG_DEFAULT_BOOL(BGP_SOFT_VERSION_CAPABILITY,
{ .val_bool = true, .match_profile = "datacenter", },
{ .val_bool = false },
);
+FRR_CFG_DEFAULT_BOOL(BGP_DYNAMIC_CAPABILITY,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
FRR_CFG_DEFAULT_BOOL(BGP_ENFORCE_FIRST_AS,
{ .val_bool = false, .match_version = "< 9.1", },
{ .val_bool = true },
@@ -623,6 +627,9 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
if (DFLT_BGP_SOFT_VERSION_CAPABILITY)
SET_FLAG((*bgp)->flags,
BGP_FLAG_SOFT_VERSION_CAPABILITY);
+ if (DFLT_BGP_DYNAMIC_CAPABILITY)
+ SET_FLAG((*bgp)->flags,
+ BGP_FLAG_DYNAMIC_CAPABILITY);
if (DFLT_BGP_ENFORCE_FIRST_AS)
SET_FLAG((*bgp)->flags, BGP_FLAG_ENFORCE_FIRST_AS);
@@ -3028,14 +3035,17 @@ DEFUN (bgp_graceful_restart,
VTY_DECLVAR_CONTEXT(bgp, bgp);
ret = bgp_gr_update_all(bgp, GLOBAL_GR_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
- ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
+ bgp->peer,
+ ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset all peers to take effect\n");
+
return bgp_vty_return(vty, ret);
}
@@ -3055,14 +3065,16 @@ DEFUN (no_bgp_graceful_restart,
int ret = BGP_GR_FAILURE;
ret = bgp_gr_update_all(bgp, NO_GLOBAL_GR_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
- ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
+ bgp->peer,
+ ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset all peers to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3270,24 +3282,25 @@ DEFUN (bgp_graceful_restart_disable,
VTY_DECLVAR_CONTEXT(bgp, bgp);
ret = bgp_gr_update_all(bgp, GLOBAL_DISABLE_CMD);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
+ bgp->peer,
+ ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
- VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
- bgp->peer, ret);
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ bgp_capability_send(peer, AFI_IP, SAFI_UNICAST,
+ CAPABILITY_CODE_RESTART,
+ CAPABILITY_ACTION_UNSET);
+ bgp_capability_send(peer, AFI_IP, SAFI_UNICAST,
+ CAPABILITY_CODE_LLGR,
+ CAPABILITY_ACTION_UNSET);
+ }
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
- zlog_debug(
- "[BGP_GR] bgp_graceful_restart_disable_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset all peers to take effect\n");
-
- for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- bgp_capability_send(peer, AFI_IP, SAFI_UNICAST,
- CAPABILITY_CODE_RESTART,
- CAPABILITY_ACTION_UNSET);
- bgp_capability_send(peer, AFI_IP, SAFI_UNICAST,
- CAPABILITY_CODE_LLGR,
- CAPABILITY_ACTION_UNSET);
- }
+ zlog_debug("[BGP_GR] bgp_graceful_restart_disable_cmd : END ");
return bgp_vty_return(vty, ret);
}
@@ -3309,15 +3322,17 @@ DEFUN (no_bgp_graceful_restart_disable,
int ret = BGP_GR_FAILURE;
ret = bgp_gr_update_all(bgp, NO_GLOBAL_DISABLE_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
- ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
+ bgp->peer,
+ ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] no_bgp_graceful_restart_disable_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset all peers to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3345,15 +3360,16 @@ DEFUN (bgp_neighbor_graceful_restart_set,
return CMD_WARNING_CONFIG_FAILED;
ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3382,15 +3398,16 @@ DEFUN (no_bgp_neighbor_graceful_restart,
"[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START ");
ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3420,15 +3437,16 @@ DEFUN (bgp_neighbor_graceful_restart_helper_set,
ret = bgp_neighbor_graceful_restart(peer, PEER_HELPER_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3457,15 +3475,16 @@ DEFUN (no_bgp_neighbor_graceful_restart_helper,
"[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : START ");
ret = bgp_neighbor_graceful_restart(peer, NO_PEER_HELPER_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3493,18 +3512,19 @@ DEFUN (bgp_neighbor_graceful_restart_disable_set,
return CMD_WARNING_CONFIG_FAILED;
ret = bgp_neighbor_graceful_restart(peer, PEER_DISABLE_CMD);
+ if (ret == BGP_GR_SUCCESS) {
+ if (peer->bgp->t_startup)
+ bgp_peer_gr_flags_update(peer);
- if (peer->bgp->t_startup)
- bgp_peer_gr_flags_update(peer);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR]bgp_neighbor_graceful_restart_disable_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -3533,15 +3553,16 @@ DEFUN (no_bgp_neighbor_graceful_restart_disable,
"[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : START ");
ret = bgp_neighbor_graceful_restart(peer, NO_PEER_DISABLE_CMD);
-
- VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
- VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ if (ret == BGP_GR_SUCCESS) {
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+ }
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
zlog_debug(
"[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : END ");
- vty_out(vty,
- "Graceful restart configuration changed, reset this peer to take effect\n");
return bgp_vty_return(vty, ret);
}
@@ -4298,6 +4319,24 @@ DEFPY (bgp_default_software_version_capability,
return CMD_SUCCESS;
}
+DEFPY (bgp_default_dynamic_capability,
+ bgp_default_dynamic_capability_cmd,
+ "[no] bgp default dynamic-capability",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Advertise dynamic capability for all neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY);
+
+ return CMD_SUCCESS;
+}
+
/* "bgp network import-check" configuration. */
DEFUN (bgp_network_import_check,
bgp_network_import_check_cmd,
@@ -4315,6 +4354,9 @@ DEFUN (bgp_network_import_check,
return CMD_SUCCESS;
}
+#if CONFDATE > 20241013
+CPP_NOTICE("Drop `bgp network import-check exact` command")
+#endif
ALIAS_HIDDEN(bgp_network_import_check, bgp_network_import_check_exact_cmd,
"bgp network import-check exact",
BGP_STR
@@ -9198,15 +9240,7 @@ DEFPY(
NEIGHBOR_ADDR_STR2
"Detect AS loops before sending to neighbor\n")
{
- struct peer *peer;
-
- peer = peer_and_group_lookup_vty(vty, neighbor);
- if (!peer)
- return CMD_WARNING_CONFIG_FAILED;
-
- peer->as_path_loop_detection = true;
-
- return CMD_SUCCESS;
+ return peer_flag_set_vty(vty, neighbor, PEER_FLAG_AS_LOOP_DETECTION);
}
DEFPY (neighbor_addpath_paths_limit,
@@ -9261,7 +9295,7 @@ DEFPY (no_neighbor_addpath_paths_limit,
peer->addpath_paths_limit[afi][safi].send = 0;
bgp_capability_send(peer, afi, safi, CAPABILITY_CODE_PATHS_LIMIT,
- CAPABILITY_ACTION_UNSET);
+ CAPABILITY_ACTION_SET);
return ret;
}
@@ -9275,15 +9309,7 @@ DEFPY(
NEIGHBOR_ADDR_STR2
"Detect AS loops before sending to neighbor\n")
{
- struct peer *peer;
-
- peer = peer_and_group_lookup_vty(vty, neighbor);
- if (!peer)
- return CMD_WARNING_CONFIG_FAILED;
-
- peer->as_path_loop_detection = false;
-
- return CMD_SUCCESS;
+ return peer_flag_unset_vty(vty, neighbor, PEER_FLAG_AS_LOOP_DETECTION);
}
DEFPY(neighbor_path_attribute_discard,
@@ -9685,8 +9711,6 @@ DEFPY (af_label_vpn_export,
BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
/* fetch a label */
bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
- bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi],
- vpn_leak_label_callback);
} else {
bgp->vpn_policy[afi].tovpn_label = label;
UNSET_FLAG(bgp->vpn_policy[afi].flags,
@@ -10729,7 +10753,10 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name,
if (rm_p->prefixlen == match.prefixlen) {
SET_FLAG(rm->flags,
BGP_NODE_USER_CLEAR);
- bgp_process(bgp, rm, afi, safi);
+ bgp_process(bgp, rm,
+ bgp_dest_get_bgp_path_info(
+ rm),
+ afi, safi);
}
bgp_dest_unlock_node(rm);
}
@@ -10741,7 +10768,9 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name,
if (dest_p->prefixlen == match.prefixlen) {
SET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
- bgp_process(bgp, dest, afi, safi);
+ bgp_process(bgp, dest,
+ bgp_dest_get_bgp_path_info(dest),
+ afi, safi);
}
bgp_dest_unlock_node(dest);
}
@@ -12741,7 +12770,7 @@ static void bgp_show_neighbor_graceful_restart_remote_mode(struct vty *vty,
if (json)
json_object_string_add(json, "remoteGrMode", mode);
else
- vty_out(vty, "%s\n", mode);
+ vty_out(vty, "%s", mode);
}
static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty,
@@ -12773,7 +12802,7 @@ static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty,
if (json)
json_object_string_add(json, "localGrMode", mode);
else
- vty_out(vty, "%s\n", mode);
+ vty_out(vty, "%s", mode);
}
static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi(
@@ -14127,33 +14156,28 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
CHECK_FLAG(
p->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_TX_RCV)) {
- if (CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_TX_ADV) &&
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_TX_RCV))
- json_object_boolean_true_add(
- json_sub,
- "txAdvertisedAndReceived");
- else if (
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_TX_ADV))
- json_object_boolean_true_add(
- json_sub,
- "txAdvertised");
- else if (
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_TX_RCV))
- json_object_boolean_true_add(
- json_sub,
- "txReceived");
+ json_object_boolean_add(
+ json_sub,
+ "txAdvertisedAndReceived",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV) &&
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV));
+
+ json_object_boolean_add(
+ json_sub, "txAdvertised",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV));
+
+ json_object_boolean_add(
+ json_sub, "txReceived",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV));
}
if (CHECK_FLAG(
@@ -14162,33 +14186,28 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
CHECK_FLAG(
p->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_RCV)) {
- if (CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_RX_ADV) &&
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_RX_RCV))
- json_object_boolean_true_add(
- json_sub,
- "rxAdvertisedAndReceived");
- else if (
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_RX_ADV))
- json_object_boolean_true_add(
- json_sub,
- "rxAdvertised");
- else if (
- CHECK_FLAG(
- p->af_cap[afi]
- [safi],
- PEER_CAP_ADDPATH_AF_RX_RCV))
- json_object_boolean_true_add(
- json_sub,
- "rxReceived");
+ json_object_boolean_add(
+ json_sub,
+ "rxAdvertisedAndReceived",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV) &&
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV));
+
+ json_object_boolean_add(
+ json_sub, "rxAdvertised",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV));
+
+ json_object_boolean_add(
+ json_sub, "rxReceived",
+ CHECK_FLAG(p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV));
}
if (CHECK_FLAG(
@@ -17690,6 +17709,76 @@ DEFUN (bgp_redistribute_ipv6_rmap_metric,
return bgp_redistribute_set(bgp, AFI_IP6, type, 0, changed);
}
+DEFPY(bgp_redistribute_ipv6_table, bgp_redistribute_ipv6_table_cmd,
+ "redistribute table-direct (1-65535)$table_id [{metric$metric (0-4294967295)$metric_val|route-map WORD$rmap}]",
+ "Redistribute information from another routing protocol\n"
+ "Non-main Kernel Routing Table - Direct\n"
+ "Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bool changed = false;
+ struct route_map *route_map = NULL;
+ struct bgp_redist *red;
+
+ if (rmap)
+ route_map = route_map_lookup_warn_noexist(vty, rmap);
+
+ if (bgp->vrf_id != VRF_DEFAULT) {
+ vty_out(vty,
+ "%% Only default BGP instance can use 'table-direct'\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (table_id == RT_TABLE_MAIN || table_id == RT_TABLE_LOCAL) {
+ vty_out(vty,
+ "%% 'table-direct', can not use %lu routing table\n",
+ table_id);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ red = bgp_redist_add(bgp, AFI_IP6, ZEBRA_ROUTE_TABLE_DIRECT, table_id);
+ if (rmap)
+ changed = bgp_redistribute_rmap_set(red, rmap, route_map);
+ if (metric)
+ changed |= bgp_redistribute_metric_set(bgp, red, AFI_IP6,
+ ZEBRA_ROUTE_TABLE_DIRECT,
+ metric_val);
+ return bgp_redistribute_set(bgp, AFI_IP6, ZEBRA_ROUTE_TABLE_DIRECT,
+ table_id, changed);
+}
+
+DEFPY(no_bgp_redistribute_ipv6_table, no_bgp_redistribute_ipv6_table_cmd,
+ "no redistribute table-direct (1-65535)$table_id [{metric (0-4294967295)|route-map WORD}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n"
+ "Non-main Kernel Routing Table - Direct\n"
+ "Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (bgp->vrf_id != VRF_DEFAULT) {
+ vty_out(vty,
+ "%% Only default BGP instance can use 'table-direct'\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (table_id == RT_TABLE_MAIN || table_id == RT_TABLE_LOCAL) {
+ vty_out(vty,
+ "%% 'table-direct', can not use %lu routing table\n",
+ table_id);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_redistribute_unset(bgp, AFI_IP6, ZEBRA_ROUTE_TABLE_DIRECT, table_id);
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_redistribute_ipv6_metric_rmap,
bgp_redistribute_ipv6_metric_rmap_cmd,
"redistribute " FRR_IP6_REDIST_STR_BGPD " metric (0-4294967295) route-map RMAP_NAME",
@@ -18409,9 +18498,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
vty_out(vty, " neighbor %s timers delayopen %u\n", addr,
peer->bgp->default_delayopen);
- /* capability dynamic */
- if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
- vty_out(vty, " neighbor %s capability dynamic\n", addr);
+ /* capability software-version */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY)) {
+ if (!peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
+ vty_out(vty, " no neighbor %s capability dynamic\n",
+ addr);
+ } else {
+ 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)) {
@@ -18460,7 +18555,7 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
vty_out(vty, " neighbor %s strict-capability-match\n", addr);
/* Sender side AS path loop detection. */
- if (peer->as_path_loop_detection)
+ if (peergroup_flag_check(peer, PEER_FLAG_AS_LOOP_DETECTION))
vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
addr);
@@ -19102,6 +19197,15 @@ int bgp_config_write(struct vty *vty)
? ""
: "no ");
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY) !=
+ SAVE_BGP_DYNAMIC_CAPABILITY)
+ vty_out(vty,
+ " %sbgp default dynamic-capability\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DYNAMIC_CAPABILITY)
+ ? ""
+ : "no ");
+
/* BGP default subgroup-pkt-queue-max. */
if (bgp->default_subgroup_pkt_queue_max
!= BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
@@ -20150,6 +20254,9 @@ void bgp_vty_init(void)
/* bgp default software-version-capability */
install_element(BGP_NODE, &bgp_default_software_version_capability_cmd);
+ /* bgp default dynamic-capability */
+ install_element(BGP_NODE, &bgp_default_dynamic_capability_cmd);
+
/* "bgp default subgroup-pkt-queue-max" commands. */
install_element(BGP_NODE, &bgp_default_subgroup_pkt_queue_max_cmd);
install_element(BGP_NODE, &no_bgp_default_subgroup_pkt_queue_max_cmd);
@@ -21170,6 +21277,15 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE,
&neighbor_maximum_prefix_threshold_restart_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_EVPN_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_EVPN_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_maximum_prefix_cmd);
/* "neighbor allowas-in" */
install_element(BGP_NODE, &neighbor_allowas_in_hidden_cmd);
@@ -21320,6 +21436,8 @@ void bgp_vty_init(void)
install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_cmd);
install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd);
install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_table_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_table_cmd);
/* import|export vpn [route-map RMAP_NAME] */
install_element(BGP_IPV4_NODE, &bgp_imexport_vpn_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 26194f8601..9a81965773 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1524,9 +1524,9 @@ static void bgp_debug_zebra_nh(struct zapi_route *api)
}
}
-void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
- struct bgp_path_info *info, struct bgp *bgp, afi_t afi,
- safi_t safi)
+static enum zclient_send_status
+bgp_zebra_announce_actual(struct bgp_dest *dest, struct bgp_path_info *info,
+ struct bgp *bgp)
{
struct bgp_path_info *bpi_ultimate;
struct zapi_route api = { 0 };
@@ -1539,34 +1539,19 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
bool is_add;
uint32_t nhg_id = 0;
uint32_t recursion_flag = 0;
+ struct bgp_table *table = bgp_dest_table(dest);
+ const struct prefix *p = bgp_dest_get_prefix(dest);
- /*
- * BGP is installing this route and bgp has been configured
- * to suppress announcements until the route has been installed
- * let's set the fact that we expect this route to be installed
- */
- if (BGP_SUPPRESS_FIB_ENABLED(bgp))
- SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
-
- /* Don't try to install if we're not connected to Zebra or Zebra doesn't
- * know of this instance.
- */
- if (!bgp_install_info_to_zebra(bgp))
- return;
-
- if (bgp->main_zebra_update_hold)
- return;
-
- if (safi == SAFI_FLOWSPEC) {
- bgp_pbr_update_entry(bgp, bgp_dest_get_prefix(dest), info, afi,
- safi, true);
- return;
+ if (table->safi == SAFI_FLOWSPEC) {
+ bgp_pbr_update_entry(bgp, p, info, table->afi, table->safi,
+ true);
+ return ZCLIENT_SEND_SUCCESS;
}
/* Make Zebra API structure. */
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
- api.safi = safi;
+ api.safi = table->safi;
api.prefix = *p;
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
@@ -1603,8 +1588,8 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
metric = info->attr->med;
bgp_zebra_announce_parse_nexthop(info, p, bgp, &api, &valid_nh_count,
- afi, safi, &nhg_id, &metric, &tag,
- &allow_recursion);
+ table->afi, table->safi, &nhg_id,
+ &metric, &tag, &allow_recursion);
is_add = (valid_nh_count || nhg_id) ? true : false;
@@ -1657,7 +1642,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
api.tag = tag;
}
- distance = bgp_distance_apply(p, info, afi, safi, bgp);
+ distance = bgp_distance_apply(p, info, table->afi, table->safi, bgp);
if (distance) {
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
api.distance = distance;
@@ -1677,10 +1662,11 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
zlog_debug("%s: %pFX: announcing to zebra (recursion %sset)",
__func__, p, (recursion_flag ? "" : "NOT "));
}
- zclient_route_send(is_add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
- zclient, &api);
+ return zclient_route_send(is_add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
}
+
/* Announce all routes of a table to zebra */
void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)
{
@@ -1701,14 +1687,11 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest))
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) &&
-
(pi->type == ZEBRA_ROUTE_BGP
&& (pi->sub_type == BGP_ROUTE_NORMAL
|| pi->sub_type == BGP_ROUTE_IMPORTED)))
-
- bgp_zebra_announce(dest,
- bgp_dest_get_prefix(dest),
- pi, bgp, afi, safi);
+ bgp_zebra_route_install(dest, pi, bgp, true,
+ NULL, false);
}
/* Announce routes of any bgp subtype of a table to zebra */
@@ -1730,39 +1713,30 @@ void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi,
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) &&
pi->type == ZEBRA_ROUTE_BGP)
- bgp_zebra_announce(dest,
- bgp_dest_get_prefix(dest),
- pi, bgp, afi, safi);
+ bgp_zebra_route_install(dest, pi, bgp, true,
+ NULL, false);
}
-void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info,
- struct bgp *bgp, afi_t afi, safi_t safi)
+enum zclient_send_status bgp_zebra_withdraw_actual(struct bgp_dest *dest,
+ struct bgp_path_info *info,
+ struct bgp *bgp)
{
struct zapi_route api;
struct peer *peer;
+ struct bgp_table *table = bgp_dest_table(dest);
+ const struct prefix *p = bgp_dest_get_prefix(dest);
- /*
- * If we are withdrawing the route, we don't need to have this
- * flag set. So unset it.
- */
- UNSET_FLAG(info->net->flags, BGP_NODE_FIB_INSTALL_PENDING);
-
- /* Don't try to install if we're not connected to Zebra or Zebra doesn't
- * know of this instance.
- */
- if (!bgp_install_info_to_zebra(bgp))
- return;
-
- if (safi == SAFI_FLOWSPEC) {
+ if (table->safi == SAFI_FLOWSPEC) {
peer = info->peer;
- bgp_pbr_update_entry(peer->bgp, p, info, afi, safi, false);
- return;
+ bgp_pbr_update_entry(peer->bgp, p, info, table->afi,
+ table->safi, false);
+ return ZCLIENT_SEND_SUCCESS;
}
memset(&api, 0, sizeof(api));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
- api.safi = safi;
+ api.safi = table->safi;
api.prefix = *p;
if (info->attr->rmap_table_id) {
@@ -1774,7 +1748,206 @@ void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info,
zlog_debug("Tx route delete VRF %u %pFX", bgp->vrf_id,
&api.prefix);
- zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+ return zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+}
+
+/*
+ * Walk the new Fifo list one by one and invoke bgp_zebra_announce/withdraw
+ * to install/withdraw the routes to zebra.
+ *
+ * If status = ZCLIENT_SEND_SUCCESS (Buffer empt)y i.e. Zebra is free to
+ * receive more incoming data, then pick the next item on the list and
+ * continue processing.
+ *
+ * If status = ZCLIENT_SEND_BUFFERED (Buffer pending) i.e. Zebra is busy,
+ * break and bail out of the function because once at some point when zebra
+ * is free, a callback is triggered which inturn call this same function and
+ * continue processing items on list.
+ */
+#define ZEBRA_ANNOUNCEMENTS_LIMIT 1000
+static void bgp_handle_route_announcements_to_zebra(struct event *e)
+{
+ bool is_evpn = false;
+ uint32_t count = 0;
+ struct bgp_dest *dest = NULL;
+ struct bgp_table *table = NULL;
+ enum zclient_send_status status = ZCLIENT_SEND_SUCCESS;
+ bool install;
+
+ while (count < ZEBRA_ANNOUNCEMENTS_LIMIT) {
+ dest = zebra_announce_pop(&bm->zebra_announce_head);
+
+ if (!dest)
+ break;
+
+ table = bgp_dest_table(dest);
+ install = CHECK_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL);
+ if (table->afi == AFI_L2VPN && table->safi == SAFI_EVPN)
+ is_evpn = true;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("BGP %s route %pBD(%s) with dest %p and flags 0x%x to zebra",
+ install ? "announcing" : "withdrawing", dest,
+ table->bgp->name_pretty, dest, dest->flags);
+
+ if (install) {
+ if (is_evpn)
+ status =
+ evpn_zebra_install(table->bgp,
+ dest->za_vpn,
+ (const struct prefix_evpn
+ *)
+ bgp_dest_get_prefix(
+ dest),
+ dest->za_bgp_pi);
+ else
+ status = bgp_zebra_announce_actual(dest,
+ dest->za_bgp_pi,
+ table->bgp);
+ UNSET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL);
+ } else {
+ if (is_evpn)
+ status = evpn_zebra_uninstall(
+ table->bgp, dest->za_vpn,
+ (const struct prefix_evpn *)
+ bgp_dest_get_prefix(dest),
+ dest->za_bgp_pi, false);
+ else
+ status = bgp_zebra_withdraw_actual(dest,
+ dest->za_bgp_pi,
+ table->bgp);
+
+ UNSET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_DELETE);
+ }
+
+ bgp_path_info_unlock(dest->za_bgp_pi);
+ dest->za_bgp_pi = NULL;
+ dest->za_vpn = NULL;
+ bgp_dest_unlock_node(dest);
+
+ if (status == ZCLIENT_SEND_BUFFERED)
+ break;
+
+ count++;
+ }
+
+ if (status != ZCLIENT_SEND_BUFFERED &&
+ zebra_announce_count(&bm->zebra_announce_head))
+ event_add_event(bm->master,
+ bgp_handle_route_announcements_to_zebra, NULL,
+ 0, &bm->t_bgp_zebra_route);
+}
+
+/*
+ * Callback function invoked when zclient_flush_data() receives a BUFFER_EMPTY
+ * i.e. zebra is free to receive more incoming data.
+ */
+static void bgp_zebra_buffer_write_ready(void)
+{
+ bgp_handle_route_announcements_to_zebra(NULL);
+}
+
+/*
+ * BGP is now keeping a list of dests with the dest having a pointer
+ * to the bgp_path_info that it will be working on.
+ * Here is the sequence of events that should happen:
+ *
+ * Current State New State Action
+ * ------------- --------- ------
+ * ---- Install Place dest on list, save pi, mark
+ * as going to be installed
+ * ---- Withdrawal Place dest on list, save pi, mark
+ * as going to be deleted
+ *
+ * Install Install Leave dest on list, release old pi,
+ * save new pi, mark as going to be
+ * Installed
+ * Install Withdrawal Leave dest on list, release old pi,
+ * save new pi, mark as going to be
+ * withdrawan, remove install flag
+ *
+ * Withdrawal Install Special case, send withdrawal immediately
+ * Leave dest on list, release old pi,
+ * save new pi, mark as going to be
+ * installed. <see note about evpn
+ * in bgp_route.c in bgp_process_main_one>
+ * Withdrawal Withdrawal Leave dest on list, release old pi,
+ * save new pi, mark as going to be
+ * withdrawn.
+ */
+void bgp_zebra_route_install(struct bgp_dest *dest, struct bgp_path_info *info,
+ struct bgp *bgp, bool install, struct bgpevpn *vpn,
+ bool is_sync)
+{
+ bool is_evpn = false;
+ struct bgp_table *table = NULL;
+
+ table = bgp_dest_table(dest);
+ if (table && table->afi == AFI_L2VPN && table->safi == SAFI_EVPN)
+ is_evpn = true;
+
+ /*
+ * BGP is installing this route and bgp has been configured
+ * to suppress announcements until the route has been installed
+ * let's set the fact that we expect this route to be installed
+ */
+ if (install) {
+ if (BGP_SUPPRESS_FIB_ENABLED(bgp))
+ SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+
+ if (bgp->main_zebra_update_hold && !is_evpn)
+ return;
+ } else {
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+ }
+
+ /*
+ * Don't try to install if we're not connected to Zebra or Zebra doesn't
+ * know of this instance.
+ */
+ if (!bgp_install_info_to_zebra(bgp) && !is_evpn)
+ return;
+
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL) &&
+ !CHECK_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_DELETE)) {
+ zebra_announce_add_tail(&bm->zebra_announce_head, dest);
+ /*
+ * If neither flag is set and za_bgp_pi is not set then it is a bug
+ */
+ assert(!dest->za_bgp_pi);
+ bgp_path_info_lock(info);
+ bgp_dest_lock_node(dest);
+ dest->za_bgp_pi = info;
+ } else if (CHECK_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL)) {
+ assert(dest->za_bgp_pi);
+ bgp_path_info_unlock(dest->za_bgp_pi);
+ bgp_path_info_lock(info);
+ dest->za_bgp_pi = info;
+ } else if (CHECK_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_DELETE)) {
+ assert(dest->za_bgp_pi);
+ if (install & !is_evpn)
+ bgp_zebra_withdraw_actual(dest, dest->za_bgp_pi, bgp);
+
+ bgp_path_info_unlock(dest->za_bgp_pi);
+ bgp_path_info_lock(info);
+ dest->za_bgp_pi = info;
+ }
+
+ if (is_evpn) {
+ dest->za_vpn = vpn;
+ dest->za_is_sync = is_sync;
+ }
+
+ if (install) {
+ UNSET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_DELETE);
+ SET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL);
+ } else {
+ UNSET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_INSTALL);
+ SET_FLAG(dest->flags, BGP_NODE_SCHEDULE_FOR_DELETE);
+ }
+
+ event_add_event(bm->master, bgp_handle_route_announcements_to_zebra,
+ NULL, 0, &bm->t_bgp_zebra_route);
}
/* Withdraw all entries in a BGP instances RIB table from Zebra */
@@ -1795,8 +1968,8 @@ void bgp_zebra_withdraw_table_all_subtypes(struct bgp *bgp, afi_t afi, safi_t sa
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
&& (pi->type == ZEBRA_ROUTE_BGP))
- bgp_zebra_withdraw(bgp_dest_get_prefix(dest),
- pi, bgp, afi, safi);
+ bgp_zebra_route_install(dest, pi, bgp, false,
+ NULL, false);
}
}
}
@@ -1981,7 +2154,7 @@ bool bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red,
bgp_path_info_set_flag(dest, pi,
BGP_PATH_ATTR_CHANGED);
- bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ bgp_process(bgp, dest, pi, afi, SAFI_UNICAST);
}
}
}
@@ -2614,8 +2787,12 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient,
/* Find the bgp route node */
dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi, &p,
&bgp->vrf_prd);
- if (!dest)
+ if (!dest) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: %pFX does not exist in the BGP table, nothing to do for %u",
+ __func__, &p, note);
return -1;
+ }
switch (note) {
case ZAPI_ROUTE_INSTALLED:
@@ -3471,6 +3648,7 @@ void bgp_zebra_init(struct event_loop *master, unsigned short instance)
zclient = zclient_new(master, &zclient_options_default, bgp_handlers,
array_size(bgp_handlers));
zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs);
+ zclient->zebra_buffer_write_ready = bgp_zebra_buffer_write_ready;
zclient->zebra_connected = bgp_zebra_connected;
zclient->zebra_capabilities = bgp_zebra_capabilities;
zclient->nexthop_update = bgp_nexthop_update;
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
index 396c8335f8..ef296b963c 100644
--- a/bgpd/bgp_zebra.h
+++ b/bgpd/bgp_zebra.h
@@ -28,13 +28,11 @@ extern void bgp_zebra_destroy(void);
extern int bgp_zebra_get_table_range(struct zclient *zc, uint32_t chunk_size,
uint32_t *start, uint32_t *end);
extern int bgp_if_update_all(void);
-extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
- struct bgp_path_info *path, struct bgp *bgp,
- afi_t afi, safi_t safi);
+extern void bgp_zebra_route_install(struct bgp_dest *dest,
+ struct bgp_path_info *path, struct bgp *bgp,
+ bool install, struct bgpevpn *vpn,
+ bool is_sync);
extern void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi);
-extern void bgp_zebra_withdraw(const struct prefix *p,
- struct bgp_path_info *path, struct bgp *bgp,
- afi_t afi, safi_t safi);
/* Announce routes of any bgp subtype of a table to zebra */
extern void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi,
@@ -127,4 +125,7 @@ extern void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label,
extern bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size,
bool label_auto);
extern void bgp_zebra_release_label_range(uint32_t start, uint32_t end);
+extern enum zclient_send_status
+bgp_zebra_withdraw_actual(struct bgp_dest *dest, struct bgp_path_info *info,
+ struct bgp *bgp);
#endif /* _QUAGGA_BGP_ZEBRA_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index be816c3361..5fd4ebe8cc 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1377,7 +1377,7 @@ int bgp_global_gr_init(struct bgp *bgp)
/*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/
GLOBAL_GR, GLOBAL_INVALID,
/*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/
- GLOBAL_INVALID, GLOBAL_HELPER
+ GLOBAL_DISABLE, GLOBAL_HELPER
},
/* GLOBAL_INVALID Mode */
{
@@ -1411,13 +1411,13 @@ int bgp_peer_gr_init(struct peer *peer)
/* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
{PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL },
/* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
- { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT,
+ { PEER_HELPER, NULL }, {PEER_GLOBAL_INHERIT,
bgp_peer_gr_action }
},
{
/* PEER_GR Mode */
/* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
- { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ { PEER_GR, NULL }, { PEER_GLOBAL_INHERIT,
bgp_peer_gr_action },
/* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
{PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL },
@@ -1429,7 +1429,7 @@ int bgp_peer_gr_init(struct peer *peer)
/* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
{ PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
/* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
- { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ { PEER_DISABLE, NULL }, { PEER_GLOBAL_INHERIT,
bgp_peer_gr_action },
/* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
{ PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
@@ -1544,6 +1544,9 @@ struct peer *peer_new(struct bgp *bgp)
if (CHECK_FLAG(bgp->flags, BGP_FLAG_SOFT_VERSION_CAPABILITY))
SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_SOFT_VERSION);
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DYNAMIC_CAPABILITY))
+ SET_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY);
+
SET_FLAG(peer->flags_invert, PEER_FLAG_CAPABILITY_FQDN);
SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_FQDN);
@@ -1850,21 +1853,31 @@ void bgp_peer_conf_if_to_su_update(struct peer_connection *connection)
void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi)
{
struct bgp_dest *dest, *ndest;
+ struct bgp_path_info *pi, *next;
struct bgp_table *table;
for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest)) {
table = bgp_dest_get_bgp_table_info(dest);
- if (table != NULL) {
- /* Special handling for 2-level routing
- * tables. */
- if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
- || safi == SAFI_EVPN) {
- for (ndest = bgp_table_top(table); ndest;
- ndest = bgp_route_next(ndest))
- bgp_process(bgp, ndest, afi, safi);
- } else
- bgp_process(bgp, dest, afi, safi);
+
+ if (!table)
+ continue;
+
+ /* Special handling for 2-level routing
+ * tables. */
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest)) {
+ for (pi = bgp_dest_get_bgp_path_info(ndest);
+ (pi != NULL) && (next = pi->next, 1);
+ pi = next)
+ bgp_process(bgp, ndest, pi, afi, safi);
+ }
+ } else {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (next = pi->next, 1); pi = next)
+ bgp_process(bgp, dest, pi, afi, safi);
}
}
}
@@ -2919,6 +2932,13 @@ static void peer_group2peer_config_copy(struct peer_group *group,
SET_FLAG(peer->flags,
PEER_FLAG_CAPABILITY_SOFT_VERSION);
+ /* capability dynamic apply */
+ if (!CHECK_FLAG(peer->flags_override,
+ PEER_FLAG_DYNAMIC_CAPABILITY))
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_DYNAMIC_CAPABILITY))
+ SET_FLAG(peer->flags,
+ PEER_FLAG_DYNAMIC_CAPABILITY);
+
/* password apply */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
PEER_STR_ATTR_INHERIT(peer, group, password,
@@ -2973,9 +2993,21 @@ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as,
peer_as_change(group->conf, *as, as_type, as_str);
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
- if (((peer->as_type == AS_SPECIFIED) && peer->as != *as)
- || (peer->as_type != as_type))
+ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as) ||
+ (peer->as_type != as_type)) {
peer_as_change(peer, *as, as_type, as_str);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s peer %s set to as_type %u curr status %s trigger BGP_Start",
+ __func__, peer->host, peer->as_type,
+ lookup_msg(bgp_status_msg,
+ peer->connection->status, NULL));
+ /* Start Peer FSM to form neighbor using new as,
+ * NOTE: the connection is triggered upon start
+ * timer expiry.
+ */
+ if (!BGP_PEER_START_SUPPRESSED(peer))
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ }
}
return 0;
@@ -3070,7 +3102,7 @@ int peer_group_delete(struct peer_group *group)
int peer_group_remote_as_delete(struct peer_group *group)
{
- struct peer *peer, *other;
+ struct peer *peer;
struct listnode *node, *nnode;
if ((group->conf->as_type == AS_UNSPECIFIED)
@@ -3078,19 +3110,12 @@ int peer_group_remote_as_delete(struct peer_group *group)
return 0;
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
- other = peer->doppelganger;
-
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
bgp_zebra_terminate_radv(peer->bgp, peer);
- peer_delete(peer);
-
- if (other && other->connection->status != Deleted) {
- other->group = NULL;
- peer_delete(other);
- }
+ /* reset existing peer connection */
+ peer_as_change(peer, 0, AS_UNSPECIFIED, NULL);
}
- list_delete_all_node(group->peer);
group->conf->as = 0;
group->conf->as_type = AS_UNSPECIFIED;
@@ -3850,10 +3875,20 @@ int bgp_delete(struct bgp *bgp)
afi_t afi;
safi_t safi;
int i;
+ struct bgp_dest *dest = NULL;
struct graceful_restart_info *gr_info;
assert(bgp);
+ while (zebra_announce_count(&bm->zebra_announce_head)) {
+ dest = zebra_announce_pop(&bm->zebra_announce_head);
+ if (dest->za_bgp_pi->peer->bgp == bgp) {
+ bgp_path_info_unlock(dest->za_bgp_pi);
+ bgp_dest_unlock_node(dest);
+ } else
+ zebra_announce_add_tail(&bm->zebra_announce_head, dest);
+ }
+
bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);
/* make sure we withdraw any exported routes */
@@ -4587,6 +4622,7 @@ static const struct peer_flag_action peer_flag_action_list[] = {
{PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
{PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_none},
{PEER_FLAG_CAPABILITY_FQDN, 0, peer_change_none},
+ {PEER_FLAG_AS_LOOP_DETECTION, 0, peer_change_none},
{0, 0, 0}};
static const struct peer_flag_action peer_af_flag_action_list[] = {
@@ -8292,6 +8328,8 @@ void bgp_master_init(struct event_loop *master, const int buffer_size,
memset(&bgp_master, 0, sizeof(bgp_master));
bm = &bgp_master;
+
+ zebra_announce_init(&bm->zebra_announce_head);
bm->bgp = list_new();
bm->listen_sockets = list_new();
bm->port = BGP_PORT_DEFAULT;
@@ -8310,6 +8348,7 @@ void bgp_master_init(struct event_loop *master, const int buffer_size,
bm->outq_limit = BM_DEFAULT_Q_LIMIT;
bm->t_bgp_sync_label_manager = NULL;
bm->t_bgp_start_label_manager = NULL;
+ bm->t_bgp_zebra_route = NULL;
bgp_mac_init();
/* init the rd id space.
@@ -8560,6 +8599,7 @@ void bgp_terminate(void)
EVENT_OFF(bm->t_rmap_update);
EVENT_OFF(bm->t_bgp_sync_label_manager);
EVENT_OFF(bm->t_bgp_start_label_manager);
+ EVENT_OFF(bm->t_bgp_zebra_route);
bgp_mac_finish();
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 94bb107253..e882a181b5 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -18,6 +18,8 @@
#include "iana_afi.h"
#include "asn.h"
+PREDECL_LIST(zebra_announce);
+
/* For union sockunion. */
#include "queue.h"
#include "sockunion.h"
@@ -173,8 +175,13 @@ struct bgp_master {
struct event *t_bgp_sync_label_manager;
struct event *t_bgp_start_label_manager;
+ struct event *t_bgp_zebra_route;
+
bool v6_with_v4_nexthops;
+ /* To preserve ordering of installations into zebra across all Vrfs */
+ struct zebra_announce_head zebra_announce_head;
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(bgp_master);
@@ -522,6 +529,8 @@ struct bgp {
#define BGP_FLAG_LU_IPV6_EXPLICIT_NULL (1ULL << 34)
#define BGP_FLAG_SOFT_VERSION_CAPABILITY (1ULL << 35)
#define BGP_FLAG_ENFORCE_FIRST_AS (1ULL << 36)
+#define BGP_FLAG_DYNAMIC_CAPABILITY (1ULL << 37)
+#define BGP_FLAG_VNI_DOWN (1ULL << 38)
/* BGP default address-families.
* New peers inherit enabled afi/safis from bgp instance.
@@ -1470,6 +1479,7 @@ struct peer {
#define PEER_FLAG_GRACEFUL_SHUTDOWN (1ULL << 35)
#define PEER_FLAG_CAPABILITY_SOFT_VERSION (1ULL << 36)
#define PEER_FLAG_CAPABILITY_FQDN (1ULL << 37) /* fqdn capability */
+#define PEER_FLAG_AS_LOOP_DETECTION (1ULL << 38) /* as path loop detection */
/*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
@@ -1823,9 +1833,6 @@ struct peer {
char *hostname;
char *domainname;
- /* Sender side AS path loop detection. */
- bool as_path_loop_detection;
-
/* Extended Message Support */
uint16_t max_packet_size;
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
index 382af05c24..ae899daf82 100644
--- a/bgpd/rfapi/rfapi.c
+++ b/bgpd/rfapi/rfapi.c
@@ -463,7 +463,7 @@ void del_vnc_route(struct rfapi_descriptor *rfd,
bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
bgp_path_info_delete(bn, bpi);
- bgp_process(bgp, bn, afi, safi);
+ bgp_process(bgp, bn, bpi, afi, safi);
} else {
vnc_zlog_debug_verbose(
"%s: Couldn't find route (safi=%d) at prefix %pFX",
@@ -1001,7 +1001,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */
/* Process change. */
bgp_aggregate_increment(bgp, p, bpi, afi, safi);
- bgp_process(bgp, bn, afi, safi);
+ bgp_process(bgp, bn, bpi, afi, safi);
bgp_dest_unlock_node(bn);
vnc_zlog_debug_any(
@@ -1046,7 +1046,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */
}
bgp_dest_unlock_node(bn);
- bgp_process(bgp, bn, afi, safi);
+ bgp_process(bgp, bn, new, afi, safi);
vnc_zlog_debug_any(
"%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRDP)",
diff --git a/configure.ac b/configure.ac
index 9213bf0105..f11b345cf6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -751,8 +751,6 @@ AC_ARG_ENABLE([snmp],
AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx]))
AC_ARG_ENABLE([config_rollbacks],
AS_HELP_STRING([--enable-config-rollbacks], [enable configuration rollbacks (requires sqlite3)]))
-AC_ARG_ENABLE([confd],
- AS_HELP_STRING([--enable-confd=ARG], [enable confd integration]))
AC_ARG_ENABLE([sysrepo],
AS_HELP_STRING([--enable-sysrepo], [enable sysrepo integration]))
AC_ARG_ENABLE([grpc],
@@ -2082,22 +2080,6 @@ if test "$enable_config_rollbacks" = "yes"; then
fi
dnl ---------------
-dnl confd
-dnl ---------------
-if test "$enable_confd" != "" -a "$enable_confd" != "no"; then
- AC_CHECK_PROG([CONFD], [confd], [confd], [/bin/false], "${enable_confd}/bin")
- if test "$CONFD" = "/bin/false"; then
- AC_MSG_ERROR([confd was not found on your system.])]
- fi
- AC_CHECK_PROG([CONFDC], [confdc], [confdc], [/bin/false], "${enable_confd}/bin")
- CONFD_CFLAGS="-I${enable_confd}/include -L${enable_confd}/lib"
- AC_SUBST([CONFD_CFLAGS])
- CONFD_LIBS="-lconfd"
- AC_SUBST([CONFD_LIBS])
- AC_DEFINE([HAVE_CONFD], [1], [Enable confd integration])
-fi
-
-dnl ---------------
dnl sysrepo
dnl ---------------
if test "$enable_sysrepo" = "yes"; then
@@ -2766,7 +2748,6 @@ AM_CONDITIONAL([ENABLE_BGP_VNC], [test "$enable_bgp_vnc" != "no"])
AM_CONDITIONAL([BGP_BMP], [$bgpd_bmp])
dnl northbound
AM_CONDITIONAL([SQLITE3], [$SQLITE3])
-AM_CONDITIONAL([CONFD], [test "$enable_confd" != ""])
AM_CONDITIONAL([SYSREPO], [test "$enable_sysrepo" = "yes"])
AM_CONDITIONAL([GRPC], [test "$enable_grpc" = "yes"])
AM_CONDITIONAL([ZEROMQ], [test "$ZEROMQ" = "true"])
diff --git a/debian/changelog b/debian/changelog
index b3b086e019..96c66db89b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,14 @@
-frr (9.2~dev-1) UNRELEASED; urgency=medium
+frr (10.1~dev-1) UNRELEASED; urgency=medium
- * FRR Dev 9.2
+ * FRR Dev 10.1
- -- Donatas Abraitis <donatas@opensourcerouting.org> Tue, 10 Oct 2023 12:00:00 -0600
+ -- Jafar Al-Gharaibeh <jafar@atcorp.com> Tue, 26 Mar 2024 02:00:00 -0600
+
+frr (10.0-0) unstable; urgency=medium
+
+ * New upstream release FRR 10.0
+
+ -- Jafar Al-Gharaibeh <jafar@atcorp.com> Mon, 25 Mar 2024 02:00:00 -0600
frr (9.1-0) unstable; urgency=medium
diff --git a/debian/control b/debian/control
index fb8c2162da..e6d65133d3 100644
--- a/debian/control
+++ b/debian/control
@@ -23,7 +23,7 @@ Build-Depends: bison,
librtr-dev (>= 0.8.0~) <!pkg.frr.nortrlib>,
libsnmp-dev,
libssh-dev <!pkg.frr.nortrlib>,
- libyang2-dev (>= 2.1.80),
+ libyang2-dev (>= 2.1.128),
lsb-base,
pkg-config,
protobuf-c-compiler,
diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst
index 6058b8c0fc..82cc8b205e 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -383,7 +383,8 @@ bgpd
.. frrfmt:: %pBD (struct bgp_dest *)
- Print prefix for a BGP destination.
+ Print prefix for a BGP destination. When using ``--enable-dev-build`` include
+ the pointer value for the bgp_dest.
:frrfmtout:`fe80::1234/64`
diff --git a/doc/developer/northbound/advanced-topics.rst b/doc/developer/northbound/advanced-topics.rst
index edfc10bcbd..eb756026e0 100644
--- a/doc/developer/northbound/advanced-topics.rst
+++ b/doc/developer/northbound/advanced-topics.rst
@@ -12,8 +12,7 @@ Auto-generated CLI commands
In order to have less code to maintain, it should be possible to write a
tool that auto-generates CLI commands based on the FRR YANG models. As a
matter of fact, there are already a number of NETCONF-based CLIs that do
-exactly that (e.g. `Clixon <https://github.com/clicon/clixon>`__,
-ConfD’s CLI).
+exactly that (e.g. `Clixon <https://github.com/clicon/clixon>`__).
The problem however is that there isn’t an exact one-to-one mapping
between the existing CLI commands and the corresponding YANG nodes from
@@ -27,11 +26,6 @@ command for each YANG leaf, (leaf-)list and presence-container. The
ripd’s ``timers basic`` command, for instance, would become three
different commands, which would be undesirable.
- This Tail-f’s®
- `document <http://info.tail-f.com/hubfs/Whitepapers/Tail-f_ConfD-CLI__Cfg_Mode_App_Note_Rev%20C.pdf>`__
- shows how to customize ConfD auto-generated CLI commands using YANG
- annotations.
-
The good news is that *libyang* allows users to create plugins to
implement their own YANG extensions, which can be used to implement CLI
annotations. If done properly, a CLI generator can save FRR developers
@@ -76,8 +70,8 @@ Example of how this feature could be provided in the CLI:
``commit confirmed [minutes <1-60>]``. The ability to do confirmed
commits should also be exposed in the northbound API so that the
northbound plugins can also take advantage of it (in the case of the
-Sysrepo and ConfD plugins, confirmed commits are implemented externally
-in the *netopeer2-server* and *confd* daemons, respectively).
+Sysrepo plugin, confirmed commit is implemented externally in the
+*netopeer2-server* daemon).
Proposed feature: enable/disable configuration commands/sections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/developer/northbound/architecture.rst b/doc/developer/northbound/architecture.rst
index 5fd89c32f1..4e84f1d6a4 100644
--- a/doc/developer/northbound/architecture.rst
+++ b/doc/developer/northbound/architecture.rst
@@ -116,7 +116,7 @@ multitude of different management APIs, all of them connected to the
northbound layer of the FRR daemons. By default, only the CLI interface
is compiled built-in in the FRR daemons. The other management interfaces
are provided as optional plugins and need to be loaded during the daemon
-initialization (e.g. *zebra -M confd*). This design makes it possible to
+initialization (e.g. *zebra -M grpc*). This design makes it possible to
integrate FRR with different NETCONF solutions without introducing
vendor lock-in. The [[Plugins - Writing Your Own]] page explains how to
write custom northbound plugins that can be tailored to all needs
diff --git a/doc/developer/northbound/demos.rst b/doc/developer/northbound/demos.rst
index 8a0f6ad6b2..7c5ae0c229 100644
--- a/doc/developer/northbound/demos.rst
+++ b/doc/developer/northbound/demos.rst
@@ -8,23 +8,3 @@ This short demo shows some of the capabilities of the new transactional
CLI:
|asciicast1|
-
-ConfD + NETCONF + Cisco YDK
----------------------------
-
-This is a very simple demo of *ripd* being configured by a python
-script. The script uses NETCONF to communicate with *ripd*, which has
-the ConfD plugin loaded. The most interesting part, however, is the fact
-that the python script is not using handcrafted XML payloads to
-configure *ripd*. Instead, the script is using python bindings generated
-using Cisco’s YANG Development Kit (YDK).
-
-- Script used in the demo:
- https://gist.github.com/rwestphal/defa9bd1ccf216ab082d4711ae402f95
-
-|asciicast2|
-
-.. |asciicast1| image:: https://asciinema.org/a/jL0BS5HfP2kS6N1HfgsZvfZk1.png
- :target: https://asciinema.org/a/jL0BS5HfP2kS6N1HfgsZvfZk1
-.. |asciicast2| image:: https://asciinema.org/a/VfMElNxsjLcdvV7484E6ChxWv.png
- :target: https://asciinema.org/a/VfMElNxsjLcdvV7484E6ChxWv
diff --git a/doc/developer/northbound/links.rst b/doc/developer/northbound/links.rst
index 6cec176f8b..e8fb327238 100644
--- a/doc/developer/northbound/links.rst
+++ b/doc/developer/northbound/links.rst
@@ -195,14 +195,6 @@ pyangbind
- GitHub page: https://github.com/robshakir/pyangbind
- Documentation: http://pynms.io/pyangbind/
-ConfD
-^^^^^
-
-- Official webpage (for ConfD Basic):
- http://www.tail-f.com/confd-basic/
-- Training Videos: http://www.tail-f.com/confd-training-videos/
-- Forum: http://discuss.tail-f.com/
-
Sysrepo
^^^^^^^
diff --git a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst
index 5cb70ca6f1..07f92c2ca0 100644
--- a/doc/developer/northbound/operational-data-rpcs-and-notifications.rst
+++ b/doc/developer/northbound/operational-data-rpcs-and-notifications.rst
@@ -10,10 +10,10 @@ Operational data
~~~~~~~~~~~~~~~~
Writing API-agnostic code for YANG-modeled operational data is
-challenging. ConfD and Sysrepo, for instance, have completely different
-APIs to fetch operational data. So how can we write API-agnostic
-callbacks that can be used by both the ConfD and Sysrepo plugins, and
-any other northbound client that might be written in the future?
+challenging. Sysrepo, for instance, has completely different API to
+fetch operational data. So how can we write API-agnostic callbacks
+that can be used by both the Sysrepo plugin, and any other northbound
+client that might be written in the future?
As an additional requirement, the callbacks must be designed in a way
that makes in-place XPath filtering possible. As an example, a
@@ -94,27 +94,18 @@ in the northbound architecture:
*/
void *(*lookup_entry)(struct yang_list_keys *keys);
-These callbacks were designed to provide maximum flexibility, and borrow
-a lot of ideas from the ConfD API. Each callback does one and only one
-task, they are indivisible primitives that can be combined in several
-different ways to iterate over operational data. The extra flexibility
-certainly has a performance cost, but it’s the price to pay if we want
-to expose FRR operational data using several different management
-interfaces (e.g. NETCONF via either ConfD or Sysrepo+Netopeer2). In the
+These callbacks were designed to provide maximum flexibility. Each
+callback does one and only one task, they are indivisible primitives
+that can be combined in several different ways to iterate over operational
+data. The extra flexibility certainly has a performance cost, but it’s the
+price to pay if we want to expose FRR operational data using several
+different management interfaces (e.g. Sysrepo+Netopeer2). In the
future it might be possible to introduce optional callbacks that do
things like returning multiple objects at once. They would provide
enhanced performance when iterating over large lists, but their use
would be limited by the northbound plugins that can be integrated with
them.
- NOTE: using the northbound callbacks as a base, the ConfD plugin can
- provide up to 100 objects between each round trip between FRR and the
- *confd* daemon. Preliminary tests showed FRR taking ~7 seconds
- (asynchronously, without blocking the main pthread) to return a RIP
- table containing 100k routes to a NETCONF client connected to *confd*
- (JSON was used as the encoding format). Work needs to be done to find
- the bottlenecks and optimize this operation.
-
The [[Plugins - Writing Your Own]] page explains how the northbound
plugins can fetch operational data using the aforementioned northbound
callbacks, and how in-place XPath filtering can be implemented.
@@ -351,10 +342,10 @@ are being iterated over. If that is not done, the list entry returned by
this callback can become a dangling pointer when used in another
callback.
-Currently the ConfD and Sysrepo plugins run only in the main pthread.
-The plan in the short-term is to introduce a separate pthread only for
-handling operational data, and use the main pthread only for handling
-configuration changes, RPCs and notifications.
+Currently the Sysrepo plugin runs only in the main pthread. The plan in the
+short-term is to introduce a separate pthread only for handling operational
+data, and use the main pthread only for handling configuration changes,
+RPCs and notifications.
RPCs and Actions
~~~~~~~~~~~~~~~~
@@ -396,8 +387,8 @@ some EXEC-level commands using YANG so that their functionality is
exposed to other management interfaces other than the CLI. As an
example, if the ``clear bgp`` command is modeled using a YANG RPC, and a
corresponding ``rpc`` callback is written, then it should be possible to
-clear BGP neighbors using NETCONF and RESTCONF with that RPC (the ConfD
-and Sysrepo plugins have full support for YANG RPCs and actions).
+clear BGP neighbors using NETCONF and RESTCONF with that RPC (the Sysrepo
+plugin has full support for YANG RPCs and actions).
Here’s an example of a very simple RPC modeled using YANG:
@@ -568,8 +559,7 @@ Now sending the *authentication-failure* YANG notification should be as
simple as calling the above function and provide the appropriate
interface name. The notification will be processed by all northbound
plugins that subscribed a callback to the ``nb_notification_send`` hook.
-The ConfD and Sysrepo plugins, for instance, use this hook to relay the
-notifications to the *confd*/*sysrepod* daemons, which can generate
-NETCONF notifications to subscribed clients. When no northbound plugin
-is loaded, ``nb_notification_send()`` doesn’t do anything and the
-notifications are ignored.
+The Sysrepo plugin, for instance, uses this hook to relay the notifications
+to the *sysrepod* daemon, which can generate NETCONF notifications to subscribed
+clients. When no northbound plugin is loaded, ``nb_notification_send()`` doesn’t
+do anything and the notifications are ignored.
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 3af4048ed4..b89b63029b 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -36,16 +36,24 @@ Installing Topotest Requirements
tshark \
valgrind
python3 -m pip install wheel
- python3 -m pip install 'protobuf<4'
python3 -m pip install 'pytest>=6.2.4' 'pytest-xdist>=2.3.0'
python3 -m pip install 'scapy>=2.4.5'
python3 -m pip install xmltodict
python3 -m pip install git+https://github.com/Exa-Networks/exabgp@0659057837cd6c6351579e9f0fa47e9fb7de7311
useradd -d /var/run/exabgp/ -s /bin/false exabgp
- # To enable the gRPC topotest install:
- # It's important to include 'protobuf<4' here to avoid incompatible grpcio-tools versions.
- python3 -m pip install 'protobuf<4' grpcio grpcio-tools
+The version of protobuf package that is installed on your system will determine
+which versions of the python protobuf packages you need to install.
+
+.. code:: shell
+ # - Either - For protobuf version <= 3.12
+ python3 -m pip install 'protobuf<4'
+
+ # - OR- for protobuf version >= 3.21
+ python3 -m pip install 'protobuf>=4'
+
+ # To enable the gRPC topotest also install:
+ python3 -m pip install grpcio grpcio-tools
Enable Coredumps
@@ -704,6 +712,44 @@ Here's an example of collecting ``rr`` execution state from ``mgmtd`` on router
To specify additional arguments for ``rr record``, one can use the
``--rr-options``.
+.. _code_coverage:
+
+Code coverage
+"""""""""""""
+Code coverage reporting requires installation of the ``gcov`` and ``lcov``
+packages.
+
+Code coverage can automatically be gathered for any topotest run. To support
+this FRR must first be compiled with the ``--enable-gcov`` configure option.
+This will cause *.gnco files to be created during the build. When topotests are
+run the statistics are generated and stored in *.gcda files. Topotest
+infrastructure will gather these files, capture the information into a
+``coverage.info`` ``lcov`` file and also report the coverage summary.
+
+To enable code coverage support pass the ``--cov-topotest`` argument to pytest.
+If you build your FRR in a directory outside of the FRR source directory you
+will also need to pass the ``--cov-frr-build-dir`` argument specifying the build
+directory location.
+
+During the topotest run the *.gcda files are generated into a ``gcda``
+sub-directory of the top-level run directory (i.e., normally
+``/tmp/topotests/gcda``). These files will then be copied at the end of the
+topotest run into the FRR build directory where the ``gcov`` and ``lcov``
+utilities expect to find them. This is done to deal with the various different
+file ownership and permissions.
+
+At the end of the run ``lcov`` will be run to capture all of the coverage data
+into a ``coverage.info`` file. This file will be located in the top-level run
+directory (i.e., normally ``/tmp/topotests/coverage.info``).
+
+The ``coverage.info`` file can then be used to generate coverage reports or file
+markup (e.g., using the ``genhtml`` utility) or enable markup within your
+IDE/editor if supported (e.g., the emacs ``cov-mode`` package)
+
+NOTE: the *.gcda files in ``/tmp/topotests/gcda`` are cumulative so if you do
+not remove them they will aggregate data across multiple topotest runs.
+
+
.. _topotests_docker:
Running Tests with Docker
diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst
index be2952e71a..482df96267 100644
--- a/doc/developer/zebra.rst
+++ b/doc/developer/zebra.rst
@@ -161,6 +161,21 @@ Zebra Protocol Commands
The definitions of zebra protocol commands can be found at ``lib/zclient.h``.
+
+Zebra Dataplane
+===============
+
+The zebra dataplane subsystem provides a framework for FIB
+programming. Zebra uses the dataplane to program the local kernel as
+it makes changes to objects such as IP routes, MPLS LSPs, and
+interface IP addresses. The dataplane runs in its own pthread, in
+order to off-load work from the main zebra pthread.
+
+The zebra dataplane API is versioned; the version number must be
+updated along with API changes. Plugins can test the current version
+number and confirm that they are compatible with the current version.
+
+
Dataplane batching
==================
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 9ae9508b02..68ae796bc9 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -425,6 +425,11 @@ Route Selection
Disabled by default.
+.. clicmd:: bgp bestpath med missing-as-worst
+
+ If the paths MED value is missing and this command is configured
+ then treat it as the worse possible value that it can be.
+
.. clicmd:: maximum-paths (1-128)
Sets the maximum-paths value used for ecmp calculations for this
@@ -1059,7 +1064,7 @@ BGP GR Global Mode Commands
This command will enable BGP graceful restart functionality at the global
level.
-.. clicmd:: bgp graceful-restart disable
+.. clicmd:: bgp graceful-restart-disable
This command will disable both the functionality graceful restart and helper
mode.
@@ -1816,7 +1821,7 @@ Configuring Peers
This includes changing graceful-restart (LLGR also) timers,
enabling/disabling add-path, and other supported capabilities.
-.. clicmd:: neighbor PEER capability fqdn
+.. clicmd:: neighbor PEER capability fqdn
Allow BGP to negotiate the FQDN Capability with its peers.
@@ -1825,7 +1830,7 @@ Configuring Peers
This capability is activated by default. The ``no neighbor PEER capability
fqdn`` avoid negotiation of that capability. This is useful for peers who
- are not supporting this capability or supporting BGP Capabilities
+ are not supporting this capability or supporting BGP Capabilities
Negotiation RFC 2842.
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
@@ -1949,6 +1954,13 @@ Configuring Peers
outputs. It's easier to troubleshoot if you have a number of BGP peers
and a number of routes to check.
+.. clicmd:: bgp default dynamic-capability
+
+ This command enables dynamic capability advertisement by default
+ for all the neighbors.
+
+ For ``datacenter`` profile, this is enabled by default.
+
.. clicmd:: bgp default software-version-capability
This command enables software version capability advertisement by default
@@ -3219,7 +3231,7 @@ that the 2001:db8:2:2:: prefix is valid.
.. code-block:: frr
- r1# show bgp nexthop detail
+ r1# show bgp nexthop detail
Current BGP nexthop cache:
2001:db8:2:2:: valid [IGP metric 0], #paths 4
gate 2001:db8:12::2, if eth0
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index f07bade52c..d17112d8aa 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -326,11 +326,6 @@ options from the list below.
Build with configuration rollback support. Requires SQLite3.
-.. option:: --enable-confd=<dir>
-
- Build the ConfD northbound plugin. Look for the libconfd libs and headers
- in `dir`.
-
.. option:: --enable-sysrepo
Build the Sysrepo northbound plugin.
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index 6e4e42b811..40669a3c02 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -123,7 +123,7 @@ ISIS Timer
Set LSP refresh interval in seconds, globally, for an area (level-1) or a
domain (level-2).
-.. clicmd:: max-lsp-lifetime [level-1 | level-2] (360-65535)
+.. clicmd:: max-lsp-lifetime [level-1 | level-2] (350-65535)
Set LSP maximum LSP lifetime in seconds, globally, for an area (level-1) or
a domain (level-2).
@@ -224,17 +224,17 @@ ISIS interface
Add padding to IS-IS hello packets during adjacency formation only.
-.. clicmd:: isis hello-interval (1-600) [level-1 | level-2]
+.. clicmd:: isis hello-interval [level-1 | level-2] (1-600)
Set Hello interval in seconds globally, for an area (level-1) or a domain
(level-2).
-.. clicmd:: isis hello-multiplier (2-100) [level-1 | level-2]
+.. clicmd:: isis hello-multiplier [level-1 | level-2] (2-100)
Set multiplier for Hello holding time globally, for an area (level-1) or a
domain (level-2).
-.. clicmd:: isis metric [(0-255) | (0-16777215)] [level-1 | level-2]
+.. clicmd:: isis metric [level-1 | level-2] [(0-255) | (0-16777215)]
Set default metric value globally, for an area (level-1) or a domain
(level-2). Max value depend if metric support narrow or wide value (see
@@ -301,7 +301,7 @@ Showing ISIS information
Show summary information about ISIS.
-.. clicmd:: show isis hostname
+.. clicmd:: show isis [vrf <NAME|all>] hostname
Show information about ISIS node.
@@ -320,17 +320,17 @@ Showing ISIS information
Show the ISIS database globally, for a specific LSP id without or with
details.
-.. clicmd:: show isis topology [level-1|level-2] [algorithm [(128-255)]]
+.. clicmd:: show isis [vrf <NAME|all>] topology [level-1|level-2] [algorithm [(128-255)]]
Show topology IS-IS paths to Intermediate Systems, globally, in area
(level-1) or domain (level-2).
-.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] [algorithm [(128-255)]]
+.. clicmd:: show isis [vrf <NAME|all>] route [level-1|level-2] [prefix-sid|backup] [algorithm [(128-255)]]
Show the ISIS routing table, as determined by the most recent SPF
calculation.
-.. clicmd:: show isis fast-reroute summary [level-1|level-2]
+.. clicmd:: show isis [vrf <NAME|all>] fast-reroute summary [level-1|level-2]
Show information about the number of prefixes having LFA protection,
and network-wide LFA coverage.
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index ad5861051d..ea41ba53b3 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -494,11 +494,11 @@ Graceful Restart
Configure Graceful Restart (RFC 5187) helper support.
- By default, helper support is disabled for all neighbours.
+ By default, helper support is disabled for all neighbors.
This config enables/disables helper support on this router
- for all neighbours.
+ for all neighbors.
To enable/disable helper support for a specific
- neighbour, the router-id (A.B.C.D) has to be specified.
+ neighbor, the router-id (A.B.C.D) has to be specified.
.. clicmd:: graceful-restart helper strict-lsa-checking
diff --git a/doc/user/ospf_fundamentals.rst b/doc/user/ospf_fundamentals.rst
index c566059121..3032d2771e 100644
--- a/doc/user/ospf_fundamentals.rst
+++ b/doc/user/ospf_fundamentals.rst
@@ -12,7 +12,7 @@ OSPF Fundamentals
:term:`distance-vector` protocols, such as :abbr:`RIP` or :abbr:`BGP`, where
routers describe available `paths` (i.e. routes) to each other, in
:term:`link-state` protocols routers instead describe the state of their links
-to their immediate neighbouring routers.
+to their immediate neighboring routers.
.. index::
single: Link State Announcement
@@ -127,7 +127,7 @@ LSA Flooding
""""""""""""
OSPF defines several related mechanisms, used to manage synchronisation of
-:abbr:`LSDB` s between neighbours as neighbours form adjacencies and the
+:abbr:`LSDB` s between neighbors as neighbors form adjacencies and the
propagation, or `flooding` of new or updated :abbr:`LSA` s.
@@ -259,7 +259,7 @@ called `intra-area routes`.
LSA is originated for such a link.
Stub
- A link with no adjacent neighbours, or a host route.
+ A link with no adjacent neighbors, or a host route.
- Link ID and Data
@@ -339,8 +339,8 @@ The example below shows two :abbr:`LSA` s, both originated by the same router
of different LSA types.
The first LSA being the router LSA describing 192.168.0.49's links: 2 links
-to multi-access networks with fully-adjacent neighbours (i.e. Transit
-links) and 1 being a Stub link (no adjacent neighbours).
+to multi-access networks with fully-adjacent neighbors (i.e. Transit
+links) and 1 being a Stub link (no adjacent neighbors).
The second LSA being a Network LSA, for which 192.168.0.49 is the
:abbr:`DR`, listing the Router IDs of 4 routers on that network which
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 3bc4487f64..47f8fad17b 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -239,6 +239,17 @@ To start OSPF process you have to specify the OSPF router.
This configuration setting MUST be consistent across all routers within the
OSPF domain.
+.. clicmd:: neighbor A.B.C.D [poll-interval (1-65535)] [priority (0-255)]
+
+
+ Configures OSPF neighbors for non-broadcast multi-access (NBMA) networks
+ and point-to-multipoint non-broadcast networks. The `poll-interval`
+ specifies the rate for sending hello packets to neighbors that are not
+ active. When the configured neighbor is discovered, hello packets will be
+ sent at the rate of the hello-interval. The default `poll-interval` is 60
+ seconds. The `priority` is used to for the Designated Router (DR) election
+ on non-broadcast multi-access networks.
+
.. clicmd:: network A.B.C.D/M area A.B.C.D
.. clicmd:: network A.B.C.D/M area (0-4294967295)
@@ -580,7 +591,7 @@ Interfaces
Note that OSPF MD5 authentication requires that time never go backwards
(correct time is NOT important, only that it never goes backwards), even
across resets, if ospfd is to be able to promptly reestablish adjacencies
- with its neighbours after restarts/reboots. The host should have system time
+ with its neighbors after restarts/reboots. The host should have system time
be set at boot from an external or non-volatile source (e.g. battery backed
clock, NTP, etc.) or else the system clock should be periodically saved to
non-volatile storage and restored at boot if MD5 authentication is to be
@@ -612,7 +623,7 @@ Interfaces
Note that OSPF HMAC cryptographic authentication requires that time never go backwards
(correct time is NOT important, only that it never goes backwards), even
across resets, if ospfd is to be able to promptly reestablish adjacencies
- with its neighbours after restarts/reboots. The host should have system time
+ with its neighbors after restarts/reboots. The host should have system time
be set at boot from an external or non-volatile source (e.g. battery backed
clock, NTP, etc.) or else the system clock should be periodically saved to
non-volatile storage and restored at boot if HMAC cryptographic authentication is to be
@@ -679,7 +690,7 @@ Interfaces
it's recommended to set the hello delay and hello interval with the same values.
The default value is 10 seconds.
-.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint [delay-reflood]|point-to-point [dmvpn])
+.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint [delay-reflood|non-broadcast]|point-to-point [dmvpn])
When configuring a point-to-point network on an interface and the interface
has a /32 address associated with then OSPF will treat the interface
@@ -691,6 +702,13 @@ Interfaces
point-to-point, but the HUB will be a point-to-multipoint. To make this
topology work, specify the optional 'dmvpn' parameter at the spoke.
+ When the network is configured as point-to-multipoint and `non-broadcast`
+ is specified, the network doesn't support broadcast or multicast delivery
+ and neighbors cannot be discovered from OSPF hello received from the
+ OSPFAllRouters (224.0.0.5). Rather, they must be explicitly configured
+ using the :clicmd:`neighbor A.B.C.D` configuration command as they are
+ on non-broadcast networks.
+
When the network is configured as point-to-multipoint and `delay-reflood`
is specified, LSAs received on the interface from neighbors on the
interface will not be flooded back out on the interface immediately.
@@ -838,11 +856,11 @@ Graceful Restart
Configure Graceful Restart (RFC 3623) helper support.
- By default, helper support is disabled for all neighbours.
+ By default, helper support is disabled for all neighbors.
This config enables/disables helper support on this router
- for all neighbours.
+ for all neighbors.
To enable/disable helper support for a specific
- neighbour, the router-id (A.B.C.D) has to be specified.
+ neighbor, the router-id (A.B.C.D) has to be specified.
.. clicmd:: graceful-restart helper strict-lsa-checking
@@ -1082,7 +1100,7 @@ Router Information
respectively the PCE IP address, Autonomous System (AS) numbers of
controlled domains, neighbor ASs, flag and scope. For flag and scope, please
refer to :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor'
- command could be specified in order to specify all PCE neighbours.
+ command could be specified in order to specify all PCE neighbors.
.. clicmd:: show ip ospf router-info
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index 76910ee7b9..fe9e407ca9 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -215,15 +215,18 @@ Displaying RPKI
Display RPKI configuration state including timers values.
-.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)] [vrf NAME] [json]
+.. clicmd:: show rpki prefix <A.B.C.D/M|X:X::X:X/M> [ASN] [vrf NAME] [json]
Display validated prefixes received from the cache servers filtered
- by the specified prefix.
+ by the specified prefix. The AS number space has been increased
+ to allow the choice of using AS 0 because RFC-7607 specifically
+ calls out the usage of 0 in a special case.
.. clicmd:: show rpki as-number ASN [vrf NAME] [json]
Display validated prefixes received from the cache servers filtered
- by ASN.
+ by ASN. The usage of AS 0 is allowed because RFC-76067 specifically
+ calls out the usage of 0 in a special case.
.. clicmd:: show rpki prefix-table [vrf NAME] [json]
diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c
index 1491c569ec..d387b9be18 100644
--- a/eigrpd/eigrp_main.c
+++ b/eigrpd/eigrp_main.c
@@ -94,10 +94,11 @@ static void sighup(void)
static void sigint(void)
{
zlog_notice("Terminating on signal");
- eigrp_terminate();
keychain_terminate();
+ eigrp_terminate();
+
exit(0);
}
diff --git a/eigrpd/eigrp_pkt_tlv2.c b/eigrpd/eigrp_pkt_tlv2.c
deleted file mode 100644
index e69de29bb2..0000000000
--- a/eigrpd/eigrp_pkt_tlv2.c
+++ /dev/null
diff --git a/eigrpd/eigrp_routemap.h b/eigrpd/eigrp_routemap.h
index c471679619..c797d10882 100644
--- a/eigrpd/eigrp_routemap.h
+++ b/eigrpd/eigrp_routemap.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/*
* eigrp_routemap.h
*
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index cba1b91fae..7acd3a2b4e 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -687,7 +687,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json,
default:
continue;
}
- backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)"
+ backup = (sra->type == ISIS_SR_ADJ_BACKUP) ? " (backup)"
: "";
json_object_string_add(adj_sid_json, "nexthop",
@@ -862,7 +862,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
default:
continue;
}
- backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)"
+ backup = (sra->type == ISIS_SR_ADJ_BACKUP) ? " (backup)"
: "";
vty_out(vty, " %s %s%s: %u\n",
diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c
index 6f21f4cea2..4eb57aefb0 100644
--- a/isisd/isis_lfa.c
+++ b/isisd/isis_lfa.c
@@ -916,9 +916,8 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
adj = isis_adj_find(spftree_pc->area, spftree_pc->level,
vertex->N.id);
- if (adj
- && isis_sr_adj_sid_find(adj, spftree_pc->family,
- ISIS_SR_LAN_BACKUP)) {
+ if (adj && isis_sr_adj_sid_find(adj, spftree_pc->family,
+ ISIS_SR_ADJ_BACKUP)) {
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s already covered by node protection",
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index 6f410d342e..5be317018e 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -2082,7 +2082,7 @@ static void send_hello_cb(struct event *thread)
circuit->u.p2p.t_send_p2p_hello = NULL;
send_hello(circuit, 1);
send_hello_sched(circuit, ISIS_LEVEL1,
- 1000 * circuit->hello_interval[1]);
+ 1000 * circuit->hello_interval[0]);
return;
}
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 36986a19c5..294c03def1 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -2908,7 +2908,7 @@ static void show_isis_route_common(struct vty *vty, int levels,
struct isis_spftree *spftree;
struct listnode *node;
struct isis_area *area;
- char key[8];
+ char key[18];
if (!isis->area_list || isis->area_list->count == 0)
return;
diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c
index e8354fdf92..af22f56f8b 100644
--- a/isisd/isis_sr.c
+++ b/isisd/isis_sr.c
@@ -462,8 +462,7 @@ void isis_area_delete_backup_adj_sids(struct isis_area *area, int level)
struct listnode *node, *nnode;
for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra))
- if (sra->type == ISIS_SR_LAN_BACKUP
- && (sra->adj->level & level))
+ if (sra->type == ISIS_SR_ADJ_BACKUP && (sra->adj->level & level))
sr_adj_sid_del(sra);
}
@@ -689,7 +688,7 @@ void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, bool backup,
circuit->ext = isis_alloc_ext_subtlvs();
sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra));
- sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL;
+ sra->type = backup ? ISIS_SR_ADJ_BACKUP : ISIS_SR_ADJ_NORMAL;
sra->input_label = input_label;
sra->nexthop.family = family;
sra->nexthop.address = nexthop;
@@ -819,7 +818,7 @@ static void sr_adj_sid_del(struct sr_adjacency *sra)
exit(1);
}
- if (sra->type == ISIS_SR_LAN_BACKUP && sra->backup_nexthops) {
+ if (sra->type == ISIS_SR_ADJ_BACKUP && sra->backup_nexthops) {
sra->backup_nexthops->del =
(void (*)(void *))isis_nexthop_delete;
list_delete(&sra->backup_nexthops);
diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h
index 4378760299..76f776825d 100644
--- a/isisd/isis_sr.h
+++ b/isisd/isis_sr.h
@@ -82,7 +82,7 @@ struct sr_local_block {
/* Segment Routing Adjacency-SID type. */
enum sr_adj_type {
ISIS_SR_ADJ_NORMAL = 0,
- ISIS_SR_LAN_BACKUP,
+ ISIS_SR_ADJ_BACKUP,
};
/* Segment Routing Adjacency. */
diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c
index 18a0c49ceb..2412ec5e84 100644
--- a/isisd/isis_zebra.c
+++ b/isisd/isis_zebra.c
@@ -454,7 +454,7 @@ void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra)
znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL;
/* Set backup nexthops. */
- if (sra->type == ISIS_SR_LAN_BACKUP) {
+ if (sra->type == ISIS_SR_ADJ_BACKUP) {
int count;
count = isis_zebra_add_nexthops(isis, sra->backup_nexthops,
diff --git a/lib/checksum.c b/lib/checksum.c
index 6c5f06de45..b1ad813fd9 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/*
* Checksum routine for Internet Protocol family headers (C Version).
*
diff --git a/lib/checksum.h b/lib/checksum.h
index 508c3f38a6..2856a0d809 100644
--- a/lib/checksum.h
+++ b/lib/checksum.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#ifndef _FRR_CHECKSUM_H
#define _FRR_CHECKSUM_H
diff --git a/lib/command.c b/lib/command.c
index 0288ab7599..51f2529e3e 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1374,7 +1374,7 @@ DEFUN (disable,
}
/* Down vty node level. */
-DEFUN (config_exit,
+DEFUN_YANG (config_exit,
config_exit_cmd,
"exit",
"Exit current mode and down to previous mode\n")
diff --git a/lib/compiler.h b/lib/compiler.h
index 617b0c265b..03261052a2 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -32,7 +32,7 @@ extern "C" {
#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5)
# define _RET_NONNULL , returns_nonnull
#endif
-#if __has_attribute(fallthrough)
+#if __has_attribute(fallthrough) && !defined(__cplusplus)
# define fallthrough __attribute__((fallthrough));
#endif
# define _CONSTRUCTOR(x) constructor(x)
@@ -56,7 +56,7 @@ extern "C" {
#if __GNUC__ < 5
# define __has_attribute(x) 0
#endif
-#if __GNUC__ >= 7
+#if __GNUC__ >= 7 && !defined(__cplusplus)
# define fallthrough __attribute__((fallthrough));
#endif
#endif
@@ -112,7 +112,7 @@ extern "C" {
#ifndef _ALLOC_SIZE
# define _ALLOC_SIZE(x)
#endif
-#ifndef fallthrough
+#if !defined(fallthrough) && !defined(__cplusplus)
#define fallthrough
#endif
#ifndef _DEPRECATED
diff --git a/lib/explicit_bzero.c b/lib/explicit_bzero.c
index fa64ed85bf..e838f95e65 100644
--- a/lib/explicit_bzero.c
+++ b/lib/explicit_bzero.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/*
* Public domain.
* Written by Matthew Dempsky.
diff --git a/lib/keychain_nb.c b/lib/keychain_nb.c
index 6838268a93..57967b30a5 100644
--- a/lib/keychain_nb.c
+++ b/lib/keychain_nb.c
@@ -545,10 +545,6 @@ static int key_chains_key_chain_key_crypto_algorithm_modify(struct nb_cb_modify_
if (args->event != NB_EV_VALIDATE && args->event != NB_EV_APPLY)
return NB_OK;
- name = yang_dnode_get_string(args->dnode, "../../name");
- keychain = keychain_lookup(name);
- index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id");
- key = key_lookup(keychain, index);
name = yang_dnode_get_string(args->dnode, NULL);
if (!strncmp(name, prefix, prefix_len))
name += prefix_len;
@@ -570,6 +566,10 @@ static int key_chains_key_chain_key_crypto_algorithm_modify(struct nb_cb_modify_
}
assert(args->event == NB_EV_APPLY);
+ name = yang_dnode_get_string(args->dnode, "../../name");
+ keychain = keychain_lookup(name);
+ index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id");
+ key = key_lookup(keychain, index);
key->hash_algo = hash_algo;
keychain_touch(keychain);
diff --git a/lib/lib_errors.c b/lib/lib_errors.c
index a96fac9cd4..9d6c04325c 100644
--- a/lib/lib_errors.c
+++ b/lib/lib_errors.c
@@ -308,24 +308,6 @@ static struct log_ref ferr_lib_err[] = {
.suggestion = "Check if the FRR libyang plugins were installed correctly in the system",
},
{
- .code = EC_LIB_CONFD_INIT,
- .title = "ConfD initialization error",
- .description = "Upon startup FRR failed to properly initialize and startup the ConfD northbound plugin",
- .suggestion = "Check if ConfD is installed correctly in the system. Also, check if the confd daemon is running.",
- },
- {
- .code = EC_LIB_CONFD_DATA_CONVERT,
- .title = "ConfD data conversion error",
- .description = "An error has occurred while converting a ConfD data value (binary) to a string",
- .suggestion = "Open an Issue with all relevant log files and restart FRR"
- },
- {
- .code = EC_LIB_LIBCONFD,
- .title = "libconfd error",
- .description = "The northbound subsystem has detected that the libconfd library returned an error",
- .suggestion = "Open an Issue with all relevant log files and restart FRR"
- },
- {
.code = EC_LIB_SYSREPO_INIT,
.title = "Sysrepo initialization error",
.description = "Upon startup FRR failed to properly initialize and startup the Sysrepo northbound plugin",
diff --git a/lib/lib_errors.h b/lib/lib_errors.h
index 8cdfb166c7..9e0d539599 100644
--- a/lib/lib_errors.h
+++ b/lib/lib_errors.h
@@ -65,9 +65,6 @@ enum lib_log_refs {
EC_LIB_NB_TRANSACTION_RECORD_FAILED,
EC_LIB_LIBYANG,
EC_LIB_LIBYANG_PLUGIN_LOAD,
- EC_LIB_CONFD_INIT,
- EC_LIB_CONFD_DATA_CONVERT,
- EC_LIB_LIBCONFD,
EC_LIB_SYSREPO_INIT,
EC_LIB_SYSREPO_DATA_CONVERT,
EC_LIB_LIBSYSREPO,
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 9e472054dd..03025328b7 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -319,7 +319,12 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
char *p = strrchr(argv[0], '/');
di->progname = p ? p + 1 : argv[0];
- umask(0027);
+ if (!getenv("GCOV_PREFIX"))
+ umask(0027);
+ else {
+ /* If we are profiling use a more generous umask */
+ umask(0002);
+ }
log_args_init(daemon->early_logging);
@@ -957,8 +962,6 @@ static void frr_daemonize(void)
}
close(fds[1]);
- nb_terminate();
- yang_terminate();
frr_daemon_wait(fds[0]);
}
@@ -1447,3 +1450,12 @@ void _libfrr_version(void)
write(1, banner, sizeof(banner) - 1);
_exit(0);
}
+
+/* Render simple version tuple to string */
+const char *frr_vers2str(uint32_t version, char *buf, int buflen)
+{
+ snprintf(buf, buflen, "%d.%d.%d", MAJOR_FRRVERSION(version),
+ MINOR_FRRVERSION(version), SUB_FRRVERSION(version));
+
+ return buf;
+}
diff --git a/lib/libfrr.h b/lib/libfrr.h
index ee436d9f8f..77d70448a9 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -233,6 +233,17 @@ extern bool frr_is_after_fork;
extern bool debug_memstats_at_exit;
+/*
+ * Version numbering: MAJOR (8) | MINOR (16) | SUB (8)
+ */
+#define MAKE_FRRVERSION(maj, min, sub) \
+ ((((maj) & 0xff) << 24) | (((min) & 0xffff) << 8) | ((sub) & 0xff))
+#define MAJOR_FRRVERSION(v) (((v) >> 24) & 0xff)
+#define MINOR_FRRVERSION(v) (((v) >> 8) & 0xffff)
+#define SUB_FRRVERSION(v) ((v) & 0xff)
+
+const char *frr_vers2str(uint32_t version, char *buf, int buflen);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libfrr_trace.c b/lib/libfrr_trace.c
index 59320322ca..14f4a3cb20 100644
--- a/lib/libfrr_trace.c
+++ b/lib/libfrr_trace.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#define TRACEPOINT_CREATE_PROBES
#define TRACEPOINT_DEFINE
diff --git a/lib/libospf.h b/lib/libospf.h
index 45e7fb1870..0ac490a00e 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -69,6 +69,7 @@ extern "C" {
#define OSPF_MTU_IGNORE_DEFAULT 0
#define OSPF_FAST_HELLO_DEFAULT 0
#define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false
+#define OSPF_P2MP_NON_BROADCAST_DEFAULT false
#define OSPF_OPAQUE_CAPABLE_DEFAULT true
#define OSPF_PREFIX_SUPPRESSION_DEFAULT false
#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index 5896db1e58..f483d48d8d 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -880,7 +880,7 @@ done:
if (ret)
be_client_send_error(client, args->txn_id, args->req_id, false,
-EINVAL,
- "FE cilent %s txn-id %" PRIu64
+ "BE client %s txn-id %" PRIu64
" error fetching oper state %d",
client->name, args->txn_id, ret);
if (ret != NB_OK || !more)
@@ -984,7 +984,7 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
txn_id, msg->req_id, msg->code, client->name);
be_client_send_error(client, msg->refer_id, msg->req_id, false,
-1,
- "BE cilent %s recv msg unknown txn-id %" PRIu64,
+ "BE client %s recv msg unknown txn-id %" PRIu64,
client->name, txn_id);
break;
}
diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c
index a107582bea..3345505213 100644
--- a/lib/mgmt_fe_client.c
+++ b/lib/mgmt_fe_client.c
@@ -329,6 +329,36 @@ int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
return ret;
}
+int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id,
+ uint64_t req_id, uint8_t datastore,
+ LYD_FORMAT request_type, uint8_t flags,
+ uint8_t operation, const char *xpath, const char *data)
+{
+ struct mgmt_msg_edit *msg;
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit, 0,
+ MTYPE_MSG_NATIVE_EDIT);
+ msg->refer_id = session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_EDIT;
+ msg->request_type = request_type;
+ msg->flags = flags;
+ msg->datastore = datastore;
+ msg->operation = operation;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+ if (data)
+ mgmt_msg_native_append(msg, data, strlen(data) + 1);
+
+ debug_fe_client("Sending EDIT_REQ session-id %" PRIu64
+ " req-id %" PRIu64 " xpath: %s",
+ session_id, req_id, xpath);
+
+ ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false);
+ mgmt_msg_native_free_msg(msg);
+ return ret;
+}
static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
Mgmtd__FeMessage *fe_msg)
@@ -503,7 +533,9 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
struct mgmt_fe_client_session *session = NULL;
struct mgmt_msg_notify_data *notify_msg;
struct mgmt_msg_tree_data *tree_msg;
+ struct mgmt_msg_edit_reply *edit_msg;
struct mgmt_msg_error *err_msg;
+ const char *xpath = NULL;
const char *data = NULL;
size_t dlen;
@@ -554,6 +586,28 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
msg_len - sizeof(*tree_msg),
tree_msg->partial_error);
break;
+ case MGMT_MSG_CODE_EDIT_REPLY:
+ if (!session->client->cbs.edit_notify)
+ return;
+
+ edit_msg = (typeof(edit_msg))msg;
+ if (msg_len < sizeof(*edit_msg)) {
+ log_err_fe_client("Corrupt edit-reply msg recv");
+ return;
+ }
+
+ xpath = mgmt_msg_native_xpath_decode(edit_msg, msg_len);
+ if (!xpath) {
+ log_err_fe_client("Corrupt edit-reply msg recv");
+ return;
+ }
+
+ session->client->cbs.edit_notify(client, client->user_data,
+ session->client_id,
+ msg->refer_id,
+ session->user_ctx, msg->req_id,
+ xpath);
+ break;
case MGMT_MSG_CODE_NOTIFY:
if (!session->client->cbs.async_notification)
return;
diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h
index eee4594e17..9d569348ae 100644
--- a/lib/mgmt_fe_client.h
+++ b/lib/mgmt_fe_client.h
@@ -114,6 +114,12 @@ struct mgmt_fe_client_cbs {
LYD_FORMAT result_type, void *result, size_t len,
int partial_error);
+ /* Called when edit result is returned */
+ int (*edit_notify)(struct mgmt_fe_client *client, uintptr_t user_data,
+ uint64_t client_id, uint64_t session_id,
+ uintptr_t session_ctx, uint64_t req_id,
+ const char *xpath);
+
/* Called with asynchronous notifications from backends */
int (*async_notification)(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
@@ -410,6 +416,45 @@ extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client,
const char *xpath);
/*
+ * Send EDIT to MGMTD daemon.
+ *
+ * client
+ * Client object.
+ *
+ * session_id
+ * Client session ID.
+ *
+ * req_id
+ * Client request ID.
+ *
+ * datastore
+ * Datastore for editing.
+ *
+ * request_type
+ * The LYD_FORMAT of the request.
+ *
+ * flags
+ * Flags to control the behavior of the request.
+ *
+ * operation
+ * NB_OP_* operation to perform.
+ *
+ * xpath
+ * the xpath to edit.
+ *
+ * data
+ * the data tree.
+ *
+ * Returns:
+ * 0 on success, otherwise msg_conn_send_msg() return values.
+ */
+extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client,
+ uint64_t session_id, uint64_t req_id,
+ uint8_t datastore, LYD_FORMAT request_type,
+ uint8_t flags, uint8_t operation,
+ const char *xpath, const char *data);
+
+/*
* Destroy library and cleanup everything.
*/
extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client);
diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c
index 98b7da45ce..09ea43ece0 100644
--- a/lib/mgmt_msg_native.c
+++ b/lib/mgmt_msg_native.c
@@ -14,6 +14,8 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg");
+DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg");
int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok,
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
index 53bb81be28..b7c29862aa 100644
--- a/lib/mgmt_msg_native.h
+++ b/lib/mgmt_msg_native.h
@@ -21,6 +21,7 @@ extern "C" {
#include "memory.h"
#include "mgmt_msg.h"
#include "mgmt_defines.h"
+#include "northbound.h"
#include <stdalign.h>
@@ -149,6 +150,8 @@ DECLARE_MTYPE(MSG_NATIVE_GET_TREE);
DECLARE_MTYPE(MSG_NATIVE_TREE_DATA);
DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
+DECLARE_MTYPE(MSG_NATIVE_EDIT);
+DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
/*
* Native message codes
@@ -158,6 +161,8 @@ DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
#define MGMT_MSG_CODE_TREE_DATA 2
#define MGMT_MSG_CODE_GET_DATA 3
#define MGMT_MSG_CODE_NOTIFY 4
+#define MGMT_MSG_CODE_EDIT 5
+#define MGMT_MSG_CODE_EDIT_REPLY 6
/*
* Datastores
@@ -318,6 +323,60 @@ _Static_assert(sizeof(struct mgmt_msg_notify_data) ==
offsetof(struct mgmt_msg_notify_data, data),
"Size mismatch");
+#define EDIT_FLAG_IMPLICIT_LOCK 0x01
+#define EDIT_FLAG_IMPLICIT_COMMIT 0x02
+
+#define EDIT_OP_CREATE 0
+#define EDIT_OP_DELETE 4
+#define EDIT_OP_MERGE 2
+#define EDIT_OP_REPLACE 5
+#define EDIT_OP_REMOVE 3
+
+_Static_assert(EDIT_OP_CREATE == NB_OP_CREATE_EXCL, "Operation mismatch");
+_Static_assert(EDIT_OP_DELETE == NB_OP_DELETE, "Operation mismatch");
+_Static_assert(EDIT_OP_MERGE == NB_OP_MODIFY, "Operation mismatch");
+_Static_assert(EDIT_OP_REPLACE == NB_OP_REPLACE, "Operation mismatch");
+_Static_assert(EDIT_OP_REMOVE == NB_OP_DESTROY, "Operation mismatch");
+
+/**
+ * struct mgmt_msg_edit - frontend edit request.
+ *
+ * @request_type: ``LYD_FORMAT`` for the @data.
+ * @flags: combination of ``EDIT_FLAG_*`` flags.
+ * @datastore: the datastore to edit.
+ * @operation: one of ``EDIT_OP_*`` operations.
+ * @data: the xpath followed by the tree data for the operation.
+ * for CREATE, xpath points to the parent node.
+ */
+struct mgmt_msg_edit {
+ struct mgmt_msg_header;
+ uint8_t request_type;
+ uint8_t flags;
+ uint8_t datastore;
+ uint8_t operation;
+ uint8_t resv2[4];
+
+ alignas(8) char data[];
+};
+_Static_assert(sizeof(struct mgmt_msg_edit) ==
+ offsetof(struct mgmt_msg_edit, data),
+ "Size mismatch");
+
+/**
+ * struct mgmt_msg_edit_reply - frontend edit reply.
+ *
+ * @data: the xpath of the data node that was created.
+ */
+struct mgmt_msg_edit_reply {
+ struct mgmt_msg_header;
+ uint8_t resv2[8];
+
+ alignas(8) char data[];
+};
+_Static_assert(sizeof(struct mgmt_msg_edit_reply) ==
+ offsetof(struct mgmt_msg_edit_reply, data),
+ "Size mismatch");
+
/*
* Validate that the message ends in a NUL terminating byte
*/
@@ -504,13 +563,13 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
* The xpath string or NULL if there was an error decoding (i.e., the
* message is corrupt).
*/
-#define mgmt_msg_native_xpath_data_decode(msg, msglen, data) \
+#define mgmt_msg_native_xpath_data_decode(msg, msglen, __data) \
({ \
size_t __len = (msglen) - sizeof(*msg); \
const char *__s = NULL; \
if (msg->vsplit && msg->vsplit <= __len && \
msg->data[msg->vsplit - 1] == 0) { \
- (data) = msg->data + msg->vsplit; \
+ (__data) = msg->data + msg->vsplit; \
__s = msg->data; \
} \
__s; \
diff --git a/lib/northbound.c b/lib/northbound.c
index 487f225913..9a5d67cd1b 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -813,6 +813,223 @@ int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node
return NB_OK;
}
+static int nb_candidate_edit_tree_add(struct nb_config *candidate,
+ enum nb_operation operation,
+ LYD_FORMAT format, const char *xpath,
+ const char *data, char *xpath_created,
+ char *errmsg, size_t errmsg_len)
+{
+ struct lyd_node *tree = NULL;
+ struct lyd_node *parent = NULL;
+ struct lyd_node *dnode = NULL;
+ struct lyd_node *existing = NULL;
+ struct lyd_node *ex_parent = NULL;
+ char *parent_xpath = NULL;
+ struct ly_in *in;
+ LY_ERR err;
+ bool root;
+ int ret;
+
+ ly_in_new_memory(data, &in);
+
+ root = xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0);
+
+ /* get parent xpath if xpath is not root */
+ if (!root) {
+ /* NB_OP_CREATE_EXCT already expects parent xpath */
+ parent_xpath = XSTRDUP(MTYPE_TMP, xpath);
+
+ /* for other operations - pop one level */
+ if (operation != NB_OP_CREATE_EXCL) {
+ ret = yang_xpath_pop_node(parent_xpath);
+ if (ret) {
+ snprintf(errmsg, errmsg_len, "Invalid xpath");
+ goto done;
+ }
+
+ /* root is not actually a parent */
+ if (parent_xpath[0] == 0)
+ XFREE(MTYPE_TMP, parent_xpath);
+ }
+ }
+
+ /*
+ * Create parent if it's not root. We're creating a new tree here to be
+ * merged later with candidate.
+ */
+ if (parent_xpath) {
+ err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0,
+ 0, 0, &tree, &parent);
+ if (err) {
+ yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
+ ret = NB_ERR;
+ goto done;
+ }
+ assert(parent);
+ }
+
+ /* parse data */
+ err = yang_lyd_parse_data(ly_native_ctx, parent, in, format,
+ LYD_PARSE_ONLY | LYD_PARSE_STRICT |
+ LYD_PARSE_NO_STATE,
+ 0, &dnode);
+ if (err) {
+ yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
+ ret = NB_ERR;
+ goto done;
+ }
+
+ /* set the tree if we created a top-level node */
+ if (!parent)
+ tree = dnode;
+
+ /* save xpath of the created node */
+ lyd_path(dnode, LYD_PATH_STD, xpath_created, XPATH_MAXLEN);
+
+ /* verify that list keys are the same in the xpath and the data tree */
+ if (!root && (operation == NB_OP_REPLACE || operation == NB_OP_MODIFY)) {
+ if (lyd_find_path(tree, xpath, 0, NULL)) {
+ snprintf(errmsg, errmsg_len,
+ "List keys in xpath and data tree are different");
+ ret = NB_ERR;
+ goto done;
+ }
+ }
+
+ /* check if the node already exists in candidate */
+ if (operation == NB_OP_CREATE_EXCL || operation == NB_OP_REPLACE) {
+ existing = yang_dnode_get(candidate->dnode, xpath_created);
+
+ /* if the existing node is implicit default, ignore */
+ if (existing && (existing->flags & LYD_DEFAULT))
+ existing = NULL;
+
+ if (existing) {
+ if (operation == NB_OP_CREATE_EXCL) {
+ snprintf(errmsg, errmsg_len,
+ "Data already exists");
+ ret = NB_ERR;
+ goto done;
+ }
+
+ if (root) {
+ candidate->dnode = NULL;
+ } else {
+ /* if it's the first top-level node, update candidate */
+ if (candidate->dnode == existing)
+ candidate->dnode =
+ candidate->dnode->next;
+
+ ex_parent = lyd_parent(existing);
+ lyd_unlink_tree(existing);
+ }
+ }
+ }
+
+ err = lyd_merge_siblings(&candidate->dnode, tree,
+ LYD_MERGE_DESTRUCT | LYD_MERGE_WITH_FLAGS);
+ if (err) {
+ /* if replace failed, restore the original node */
+ if (existing) {
+ if (root) {
+ candidate->dnode = existing;
+ } else {
+ if (ex_parent)
+ lyd_insert_child(ex_parent, existing);
+ else
+ lyd_insert_sibling(candidate->dnode,
+ existing,
+ &candidate->dnode);
+ }
+ }
+ yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
+ ret = NB_ERR;
+ goto done;
+ } else {
+ /*
+ * Free existing node after replace.
+ * We're using `lyd_free_siblings` here to free the whole
+ * tree if we replaced the root node. It won't affect other
+ * siblings if it wasn't root, because the existing node
+ * was unlinked from the tree.
+ */
+ if (existing)
+ lyd_free_siblings(existing);
+
+ tree = NULL; /* LYD_MERGE_DESTRUCT deleted the tree */
+ }
+
+ ret = NB_OK;
+done:
+ if (tree)
+ lyd_free_all(tree);
+ XFREE(MTYPE_TMP, parent_xpath);
+ ly_in_free(in, 0);
+
+ return ret;
+}
+
+static int nb_candidate_edit_tree_del(struct nb_config *candidate,
+ enum nb_operation operation,
+ const char *xpath, char *errmsg,
+ size_t errmsg_len)
+{
+ struct lyd_node *dnode;
+
+ /* deleting root - remove the whole config */
+ if (xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0)) {
+ lyd_free_all(candidate->dnode);
+ candidate->dnode = NULL;
+ return NB_OK;
+ }
+
+ dnode = yang_dnode_get(candidate->dnode, xpath);
+ if (!dnode || (dnode->flags & LYD_DEFAULT)) {
+ if (operation == NB_OP_DELETE) {
+ snprintf(errmsg, errmsg_len, "Data missing");
+ return NB_ERR;
+ } else
+ return NB_OK;
+ }
+
+ /* if it's the first top-level node, update candidate */
+ if (candidate->dnode == dnode)
+ candidate->dnode = candidate->dnode->next;
+
+ lyd_free_tree(dnode);
+
+ return NB_OK;
+}
+
+int nb_candidate_edit_tree(struct nb_config *candidate,
+ enum nb_operation operation, LYD_FORMAT format,
+ const char *xpath, const char *data,
+ char *xpath_created, char *errmsg, size_t errmsg_len)
+{
+ int ret = NB_ERR;
+
+ switch (operation) {
+ case NB_OP_CREATE_EXCL:
+ case NB_OP_CREATE:
+ case NB_OP_MODIFY:
+ case NB_OP_REPLACE:
+ ret = nb_candidate_edit_tree_add(candidate, operation, format,
+ xpath, data, xpath_created,
+ errmsg, errmsg_len);
+ break;
+ case NB_OP_DESTROY:
+ case NB_OP_DELETE:
+ ret = nb_candidate_edit_tree_del(candidate, operation, xpath,
+ errmsg, errmsg_len);
+ break;
+ case NB_OP_MOVE:
+ /* not supported yet */
+ break;
+ }
+
+ return ret;
+}
+
const char *nb_operation_name(enum nb_operation operation)
{
switch (operation) {
@@ -2403,8 +2620,6 @@ const char *nb_client_name(enum nb_client client)
switch (client) {
case NB_CLIENT_CLI:
return "CLI";
- case NB_CLIENT_CONFD:
- return "ConfD";
case NB_CLIENT_SYSREPO:
return "Sysrepo";
case NB_CLIENT_GRPC:
diff --git a/lib/northbound.h b/lib/northbound.h
index 5be111cf0a..3bf1eacd61 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -621,11 +621,6 @@ struct nb_node {
/* Flags. */
uint8_t flags;
-
-#ifdef HAVE_CONFD
- /* ConfD hash value corresponding to this YANG path. */
- int confd_hash;
-#endif
};
/* The YANG container or list contains only config data. */
#define F_NB_NODE_CONFIG_ONLY 0x01
@@ -700,7 +695,6 @@ enum nb_error {
enum nb_client {
NB_CLIENT_NONE = 0,
NB_CLIENT_CLI,
- NB_CLIENT_CONFD,
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
NB_CLIENT_PCEP,
@@ -1005,6 +999,44 @@ extern int nb_candidate_edit(struct nb_config *candidate,
const struct yang_data *data);
/*
+ * Edit a candidate configuration. Value is given as JSON/XML.
+ *
+ * candidate
+ * Candidate configuration to edit.
+ *
+ * operation
+ * Operation to apply.
+ *
+ * format
+ * LYD_FORMAT of the value.
+ *
+ * xpath
+ * XPath of the configuration node being edited.
+ * For create, it must be the parent.
+ *
+ * data
+ * New data tree for the node.
+ *
+ * xpath_created
+ * XPath of the created node if operation is "create".
+ *
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR for other errors.
+ */
+extern int nb_candidate_edit_tree(struct nb_config *candidate,
+ enum nb_operation operation,
+ LYD_FORMAT format, const char *xpath,
+ const char *data, char *xpath_created,
+ char *errmsg, size_t errmsg_len);
+
+/*
* Create diff for configuration.
*
* dnode
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
deleted file mode 100644
index 8503d18002..0000000000
--- a/lib/northbound_confd.c
+++ /dev/null
@@ -1,1494 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2018 NetDEF, Inc.
- * Renato Westphal
- */
-
-#include <zebra.h>
-
-#include "log.h"
-#include "lib_errors.h"
-#include "command.h"
-#include "debug.h"
-#include "libfrr.h"
-#include "lib/version.h"
-#include "northbound.h"
-
-#include <confd_lib.h>
-#include <confd_cdb.h>
-#include <confd_dp.h>
-#include <confd_maapi.h>
-
-DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module");
-
-static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"};
-
-static struct event_loop *master;
-static struct sockaddr confd_addr;
-static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock;
-static struct event *t_cdb_sub, *t_dp_ctl, *t_dp_worker;
-static struct confd_daemon_ctx *dctx;
-static struct confd_notification_ctx *live_ctx;
-static bool confd_connected;
-static struct list *confd_spoints;
-static struct nb_transaction *transaction;
-
-static void frr_confd_finish_cdb(void);
-static void frr_confd_finish_dp(void);
-static int frr_confd_finish(void);
-
-#define flog_err_confd(funcname) \
- flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \
- (funcname), confd_strerror(confd_errno), confd_errno, \
- confd_lasterr())
-
-
-/* ------------ Utils ------------ */
-
-/* Get XPath string from ConfD hashed keypath. */
-static void frr_confd_get_xpath(const confd_hkeypath_t *kp, char *xpath,
- size_t len)
-{
- char *p;
-
- confd_xpath_pp_kpath(xpath, len, 0, kp);
-
- /*
- * Replace double quotes by single quotes (the format accepted by the
- * northbound API).
- */
- p = xpath;
- while ((p = strchr(p, '"')) != NULL)
- *p++ = '\'';
-}
-
-/* Convert ConfD binary value to a string. */
-static int frr_confd_val2str(const char *xpath, const confd_value_t *value,
- char *string, size_t string_size)
-{
- struct confd_cs_node *csp;
-
- csp = confd_cs_node_cd(NULL, xpath);
- if (!csp) {
- flog_err_confd("confd_cs_node_cd");
- return -1;
- }
- if (confd_val2str(csp->info.type, value, string, string_size)
- == CONFD_ERR) {
- flog_err_confd("confd_val2str");
- return -1;
- }
-
- return 0;
-}
-
-/* Obtain list entry from ConfD hashed keypath. */
-static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp,
- struct nb_node *nb_node,
- const void **list_entry)
-{
- struct nb_node *nb_node_list;
- int parent_lists = 0;
- int curr_list = 0;
-
- *list_entry = NULL;
-
- /*
- * Count the number of YANG lists in the path, disconsidering the
- * last element.
- */
- nb_node_list = nb_node;
- while (nb_node_list->parent_list) {
- nb_node_list = nb_node_list->parent_list;
- parent_lists++;
- }
- if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0)
- return 0;
-
- /* Start from the beginning and move down the tree. */
- for (int i = kp->len; i >= 0; i--) {
- struct yang_list_keys keys;
-
- /* Not a YANG list. */
- if (kp->v[i][0].type != C_BUF)
- continue;
-
- /* Obtain list keys. */
- memset(&keys, 0, sizeof(keys));
- for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) {
- strlcpy(keys.key[keys.num],
- (char *)kp->v[i][j].val.buf.ptr,
- sizeof(keys.key[keys.num]));
- keys.num++;
- }
-
- /* Obtain northbound node associated to the YANG list. */
- nb_node_list = nb_node;
- for (int j = curr_list; j < parent_lists; j++)
- nb_node_list = nb_node_list->parent_list;
-
- /* Obtain list entry. */
- if (!CHECK_FLAG(nb_node_list->flags, F_NB_NODE_KEYLESS_LIST)) {
- *list_entry = nb_callback_lookup_entry(
- nb_node, *list_entry, &keys);
- if (*list_entry == NULL)
- return -1;
- } else {
- unsigned long ptr_ulong;
-
- /* Retrieve list entry from pseudo-key (string). */
- if (sscanf(keys.key[0], "%lu", &ptr_ulong) != 1)
- return -1;
- *list_entry = (const void *)ptr_ulong;
- }
-
- curr_list++;
- }
-
- return 0;
-}
-
-/* Fill the current date and time into a confd_datetime structure. */
-static void getdatetime(struct confd_datetime *datetime)
-{
- struct tm tm;
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
- gmtime_r(&tv.tv_sec, &tm);
-
- memset(datetime, 0, sizeof(*datetime));
- datetime->year = 1900 + tm.tm_year;
- datetime->month = tm.tm_mon + 1;
- datetime->day = tm.tm_mday;
- datetime->sec = tm.tm_sec;
- datetime->micro = tv.tv_usec;
- datetime->timezone = 0;
- datetime->timezone_minutes = 0;
- datetime->hour = tm.tm_hour;
- datetime->min = tm.tm_min;
-}
-
-/* ------------ CDB code ------------ */
-
-struct cdb_iter_args {
- struct nb_config *candidate;
- bool error;
-};
-
-static enum cdb_iter_ret
-frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op,
- confd_value_t *oldv, confd_value_t *newv, void *args)
-{
- char xpath[XPATH_MAXLEN];
- struct nb_node *nb_node;
- enum nb_operation nb_op;
- struct cdb_iter_args *iter_args = args;
- char value_str[YANG_VALUE_MAXLEN];
- struct yang_data *data;
- char *sb1, *sb2;
- int ret;
-
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
-
- /*
- * HACK: obtain value of leaf-list elements from the XPath due to
- * a bug in the ConfD API.
- */
- value_str[0] = '\0';
- sb1 = strrchr(xpath, '[');
- sb2 = strrchr(xpath, ']');
- if (sb1 && sb2 && !strchr(sb1, '=')) {
- *sb2 = '\0';
- strlcpy(value_str, sb1 + 1, sizeof(value_str));
- *sb1 = '\0';
- }
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- iter_args->error = true;
- return ITER_STOP;
- }
-
- /* Map operation values. */
- switch (cdb_op) {
- case MOP_CREATED:
- nb_op = NB_OP_CREATE;
- break;
- case MOP_DELETED:
- nb_op = NB_OP_DESTROY;
- break;
- case MOP_VALUE_SET:
- if (nb_is_operation_allowed(nb_node, NB_OP_MODIFY))
- nb_op = NB_OP_MODIFY;
- else
- /* Ignore list keys modifications. */
- return ITER_RECURSE;
- break;
- case MOP_MOVED_AFTER:
- nb_op = NB_OP_MOVE;
- break;
- case MOP_MODIFIED:
- /* We're not interested on this. */
- return ITER_RECURSE;
- default:
- flog_err(EC_LIB_DEVELOPMENT,
- "%s: unexpected operation %u [xpath %s]", __func__,
- cdb_op, xpath);
- iter_args->error = true;
- return ITER_STOP;
- }
-
- /* Convert ConfD value to a string. */
- if (nb_node->snode->nodetype != LYS_LEAFLIST && newv
- && frr_confd_val2str(nb_node->xpath, newv, value_str,
- sizeof(value_str))
- != 0) {
- flog_err(EC_LIB_CONFD_DATA_CONVERT,
- "%s: failed to convert ConfD value to a string",
- __func__);
- iter_args->error = true;
- return ITER_STOP;
- }
-
- /* Edit the candidate configuration. */
- data = yang_data_new(xpath, value_str);
- ret = nb_candidate_edit(iter_args->candidate, nb_node, nb_op, xpath,
- NULL, data);
- yang_data_free(data);
- if (ret != NB_OK) {
- flog_warn(
- EC_LIB_NB_CANDIDATE_EDIT_ERROR,
- "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
- __func__, nb_operation_name(nb_op), xpath);
- iter_args->error = true;
- return ITER_STOP;
- }
-
- return ITER_RECURSE;
-}
-
-static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
-{
- struct nb_context context = {};
- struct nb_config *candidate;
- struct cdb_iter_args iter_args;
- char errmsg[BUFSIZ] = {0};
- int ret;
-
- candidate = nb_config_dup(running_config);
-
- /* Iterate over all configuration changes. */
- iter_args.candidate = candidate;
- iter_args.error = false;
- for (int i = 0; i < reslen; i++) {
- if (cdb_diff_iterate(fd, subp[i], frr_confd_cdb_diff_iter,
- ITER_WANT_PREV, &iter_args)
- != CONFD_OK) {
- flog_err_confd("cdb_diff_iterate");
- }
- }
- free(subp);
-
- if (iter_args.error) {
- nb_config_free(candidate);
-
- if (cdb_sub_abort_trans(
- cdb_sub_sock, CONFD_ERRCODE_APPLICATION_INTERNAL, 0,
- 0, "Couldn't apply configuration changes")
- != CONFD_OK) {
- flog_err_confd("cdb_sub_abort_trans");
- return;
- }
- return;
- }
-
- /*
- * Validate the configuration changes and allocate all resources
- * required to apply them.
- */
- transaction = NULL;
- context.client = NB_CLIENT_CONFD;
- ret = nb_candidate_commit_prepare(context, candidate, NULL,
- &transaction, false, false, errmsg,
- sizeof(errmsg));
- if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
- enum confd_errcode errcode;
-
- switch (ret) {
- case NB_ERR_LOCKED:
- errcode = CONFD_ERRCODE_IN_USE;
- break;
- case NB_ERR_RESOURCE:
- errcode = CONFD_ERRCODE_RESOURCE_DENIED;
- break;
- default:
- errcode = CONFD_ERRCODE_APPLICATION;
- break;
- }
-
- /* Reject the configuration changes. */
- if (cdb_sub_abort_trans(cdb_sub_sock, errcode, 0, 0, "%s",
- errmsg)
- != CONFD_OK) {
- flog_err_confd("cdb_sub_abort_trans");
- return;
- }
- } else {
- /* Acknowledge the notification. */
- if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY)
- != CONFD_OK) {
- flog_err_confd("cdb_sync_subscription_socket");
- return;
- }
-
- /* No configuration changes. */
- if (!transaction)
- nb_config_free(candidate);
- }
-}
-
-static void frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen)
-{
- /*
- * No need to process the configuration changes again as we're already
- * keeping track of them in the "transaction" variable.
- */
- free(subp);
-
- /* Apply the transaction. */
- if (transaction) {
- struct nb_config *candidate = transaction->config;
- char errmsg[BUFSIZ] = {0};
-
- nb_candidate_commit_apply(transaction, true, NULL, errmsg,
- sizeof(errmsg));
- nb_config_free(candidate);
- }
-
- /* Acknowledge the notification. */
- if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) {
- flog_err_confd("cdb_sync_subscription_socket");
- return;
- }
-}
-
-static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen)
-{
- /*
- * No need to process the configuration changes again as we're already
- * keeping track of them in the "transaction" variable.
- */
- free(subp);
-
- /* Abort the transaction. */
- if (transaction) {
- struct nb_config *candidate = transaction->config;
- char errmsg[BUFSIZ] = {0};
-
- nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg));
- nb_config_free(candidate);
- }
-
- /* Acknowledge the notification. */
- if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) {
- flog_err_confd("cdb_sync_subscription_socket");
- return -1;
- }
-
- return 0;
-}
-
-static void frr_confd_cdb_read_cb(struct event *thread)
-{
- int fd = EVENT_FD(thread);
- enum cdb_sub_notification cdb_ev;
- int flags;
- int *subp = NULL;
- int reslen = 0;
-
- event_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub);
-
- if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen)
- != CONFD_OK) {
- flog_err_confd("cdb_read_subscription_socket2");
- return;
- }
-
- switch (cdb_ev) {
- case CDB_SUB_PREPARE:
- frr_confd_cdb_read_cb_prepare(fd, subp, reslen);
- break;
- case CDB_SUB_COMMIT:
- frr_confd_cdb_read_cb_commit(fd, subp, reslen);
- break;
- case CDB_SUB_ABORT:
- frr_confd_cdb_read_cb_abort(fd, subp, reslen);
- break;
- default:
- flog_err_confd("unknown CDB event");
- break;
- }
-}
-
-/* Trigger CDB subscriptions to read the startup configuration. */
-static void *thread_cdb_trigger_subscriptions(void *data)
-{
- int sock;
- int *sub_points = NULL, len = 0;
- struct listnode *node;
- int *spoint;
- int i = 0;
-
- /* Create CDB data socket. */
- sock = socket(PF_INET, SOCK_STREAM, 0);
- if (sock < 0) {
- flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
- __func__, safe_strerror(errno));
- return NULL;
- }
-
- if (cdb_connect(sock, CDB_DATA_SOCKET, &confd_addr,
- sizeof(struct sockaddr_in))
- != CONFD_OK) {
- flog_err_confd("cdb_connect");
- return NULL;
- }
-
- /*
- * Fill array containing the subscription point of all loaded YANG
- * modules.
- */
- len = listcount(confd_spoints);
- sub_points = XCALLOC(MTYPE_CONFD, len * sizeof(int));
- for (ALL_LIST_ELEMENTS_RO(confd_spoints, node, spoint))
- sub_points[i++] = *spoint;
-
- if (cdb_trigger_subscriptions(sock, sub_points, len) != CONFD_OK) {
- flog_err_confd("cdb_trigger_subscriptions");
- return NULL;
- }
-
- /* Cleanup and exit thread. */
- XFREE(MTYPE_CONFD, sub_points);
- cdb_close(sock);
-
- return NULL;
-}
-
-static int frr_confd_subscribe(const struct lysc_node *snode, void *arg)
-{
- struct yang_module *module = arg;
- struct nb_node *nb_node;
- int *spoint;
- int ret;
-
- switch (snode->nodetype) {
- case LYS_CONTAINER:
- case LYS_LEAF:
- case LYS_LEAFLIST:
- case LYS_LIST:
- break;
- default:
- return YANG_ITER_CONTINUE;
- }
-
- if (CHECK_FLAG(snode->flags, LYS_CONFIG_R))
- return YANG_ITER_CONTINUE;
-
- nb_node = snode->priv;
- if (!nb_node)
- return YANG_ITER_CONTINUE;
-
- DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", __func__,
- nb_node->xpath);
-
- spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint));
- ret = cdb_subscribe2(cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE,
- CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint,
- module->confd_hash, nb_node->xpath);
- if (ret != CONFD_OK) {
- flog_err_confd("cdb_subscribe2");
- XFREE(MTYPE_CONFD, spoint);
- return YANG_ITER_CONTINUE;
- }
-
- listnode_add(confd_spoints, spoint);
- return YANG_ITER_CONTINUE;
-}
-
-static int frr_confd_init_cdb(void)
-{
- struct yang_module *module;
- pthread_t cdb_trigger_thread;
-
- /* Create CDB subscription socket. */
- cdb_sub_sock = socket(PF_INET, SOCK_STREAM, 0);
- if (cdb_sub_sock < 0) {
- flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
- __func__, safe_strerror(errno));
- return -1;
- }
-
- if (cdb_connect(cdb_sub_sock, CDB_SUBSCRIPTION_SOCKET, &confd_addr,
- sizeof(struct sockaddr_in))
- != CONFD_OK) {
- flog_err_confd("cdb_connect");
- goto error;
- }
-
- /* Subscribe to all loaded YANG data modules. */
- confd_spoints = list_new();
- RB_FOREACH (module, yang_modules, &yang_modules) {
- module->confd_hash = confd_str2hash(module->info->ns);
- if (module->confd_hash == 0) {
- flog_err(
- EC_LIB_LIBCONFD,
- "%s: failed to find hash value for namespace %s",
- __func__, module->info->ns);
- goto error;
- }
-
- /*
- * The CDB API doesn't provide a mechanism to subscribe to an
- * entire YANG module. So we have to find the top level
- * nodes ourselves and subscribe to their paths.
- */
- yang_snodes_iterate(module->info, frr_confd_subscribe, 0,
- module);
- }
-
- if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) {
- flog_err_confd("cdb_subscribe_done");
- goto error;
- }
-
- /* Create short lived pthread to trigger the CDB subscriptions. */
- if (pthread_create(&cdb_trigger_thread, NULL,
- thread_cdb_trigger_subscriptions, NULL)) {
- flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s",
- __func__, safe_strerror(errno));
- goto error;
- }
- pthread_detach(cdb_trigger_thread);
-
- event_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock,
- &t_cdb_sub);
-
- return 0;
-
-error:
- frr_confd_finish_cdb();
-
- return -1;
-}
-
-static void frr_confd_finish_cdb(void)
-{
- if (cdb_sub_sock > 0) {
- EVENT_OFF(t_cdb_sub);
- cdb_close(cdb_sub_sock);
- }
-}
-
-/* ------------ DP code ------------ */
-
-static int frr_confd_transaction_init(struct confd_trans_ctx *tctx)
-{
- confd_trans_set_fd(tctx, dp_worker_sock);
-
- return CONFD_OK;
-}
-
-#define CONFD_MAX_CHILD_NODES 32
-
-static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx,
- confd_hkeypath_t *kp)
-{
- struct nb_node *nb_node;
- char xpath[XPATH_MAXLEN];
- struct yang_data *data;
- confd_value_t v;
- const void *list_entry = NULL;
-
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- confd_data_reply_not_found(tctx);
- return CONFD_OK;
- }
-
- if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
- confd_data_reply_not_found(tctx);
- return CONFD_OK;
- }
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data) {
- if (data->value) {
- CONFD_SET_STR(&v, data->value);
- confd_data_reply_value(tctx, &v);
- } else
- confd_data_reply_found(tctx);
- yang_data_free(data);
- } else
- confd_data_reply_not_found(tctx);
-
- return CONFD_OK;
-}
-
-static int frr_confd_data_get_next(struct confd_trans_ctx *tctx,
- confd_hkeypath_t *kp, long next)
-{
- struct nb_node *nb_node;
- char xpath[XPATH_MAXLEN];
- struct yang_data *data;
- const void *parent_list_entry, *nb_next;
- confd_value_t v[LIST_MAXKEYS];
-
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- confd_data_reply_next_key(tctx, NULL, -1, -1);
- return CONFD_OK;
- }
-
- if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
- != 0) {
- /* List entry doesn't exist anymore. */
- confd_data_reply_next_key(tctx, NULL, -1, -1);
- return CONFD_OK;
- }
-
- nb_next = nb_callback_get_next(nb_node, parent_list_entry,
- (next == -1) ? NULL : (void *)next);
- if (!nb_next) {
- /* End of the list or leaf-list. */
- confd_data_reply_next_key(tctx, NULL, -1, -1);
- return CONFD_OK;
- }
-
- switch (nb_node->snode->nodetype) {
- case LYS_LIST:
- if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
- struct yang_list_keys keys;
-
- memset(&keys, 0, sizeof(keys));
- if (nb_callback_get_keys(nb_node, nb_next, &keys)
- != NB_OK) {
- flog_warn(EC_LIB_NB_CB_STATE,
- "%s: failed to get list keys",
- __func__);
- confd_data_reply_next_key(tctx, NULL, -1, -1);
- return CONFD_OK;
- }
-
- /* Feed keys to ConfD. */
- for (size_t i = 0; i < keys.num; i++)
- CONFD_SET_STR(&v[i], keys.key[i]);
- confd_data_reply_next_key(tctx, v, keys.num,
- (long)nb_next);
- } else {
- char pointer_str[32];
-
- /*
- * ConfD 6.6 user guide, chapter 6.11 (Operational data
- * lists without keys):
- * "To support this without having completely separate
- * APIs, we use a "pseudo" key in the ConfD APIs for
- * this type of list. This key is not part of the data
- * model, and completely hidden in the northbound agent
- * interfaces, but is used with e.g. the get_next() and
- * get_elem() callbacks as if it were a normal key. This
- * "pseudo" key is always a single signed 64-bit
- * integer, i.e. the confd_value_t type is C_INT64. The
- * values can be chosen arbitrarily by the application,
- * as long as a key value returned by get_next() can be
- * used to get the data for the corresponding list entry
- * with get_elem() or get_object() as usual. It could
- * e.g. be an index into an array that holds the data,
- * or even a memory address in integer form".
- *
- * Since we're using the CONFD_DAEMON_FLAG_STRINGSONLY
- * option, we must convert our pseudo-key (a void
- * pointer) to a string before sending it to confd.
- */
- snprintf(pointer_str, sizeof(pointer_str), "%lu",
- (unsigned long)nb_next);
- CONFD_SET_STR(&v[0], pointer_str);
- confd_data_reply_next_key(tctx, v, 1, (long)nb_next);
- }
- break;
- case LYS_LEAFLIST:
- data = nb_callback_get_elem(nb_node, xpath, nb_next);
- if (data) {
- if (data->value) {
- CONFD_SET_STR(&v[0], data->value);
- confd_data_reply_next_key(tctx, v, 1,
- (long)nb_next);
- }
- yang_data_free(data);
- } else
- confd_data_reply_next_key(tctx, NULL, -1, -1);
- break;
- default:
- break;
- }
-
- return CONFD_OK;
-}
-
-/*
- * Optional callback - implemented for performance reasons.
- */
-static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
- confd_hkeypath_t *kp)
-{
- struct nb_node *nb_node;
- const struct lysc_node *child;
- char xpath[XPATH_MAXLEN];
- char xpath_child[XPATH_MAXLEN * 2];
- struct list *elements;
- struct yang_data *data;
- const void *list_entry;
- confd_value_t values[CONFD_MAX_CHILD_NODES];
- size_t nvalues = 0;
-
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- confd_data_reply_not_found(tctx);
- return CONFD_ERR;
- }
-
- if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
- confd_data_reply_not_found(tctx);
- return CONFD_OK;
- }
-
- elements = yang_data_list_new();
-
- /* Loop through list child nodes. */
- LY_LIST_FOR (lysc_node_child(nb_node->snode), child) {
- struct nb_node *nb_node_child = child->priv;
- confd_value_t *v;
-
- if (nvalues > CONFD_MAX_CHILD_NODES)
- break;
-
- v = &values[nvalues++];
-
- /* Non-presence containers, lists and leaf-lists. */
- if (!nb_node_child->cbs.get_elem) {
- CONFD_SET_NOEXISTS(v);
- continue;
- }
-
- snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
- child->name);
- data = nb_callback_get_elem(nb_node_child, xpath_child,
- list_entry);
- if (data) {
- if (data->value)
- CONFD_SET_STR(v, data->value);
- else {
- /* Presence containers and empty leafs. */
- CONFD_SET_XMLTAG(
- v, nb_node_child->confd_hash,
- confd_str2hash(nb_node_child->snode
- ->module->ns));
- }
- listnode_add(elements, data);
- } else
- CONFD_SET_NOEXISTS(v);
- }
-
- confd_data_reply_value_array(tctx, values, nvalues);
-
- /* Release memory. */
- list_delete(&elements);
-
- return CONFD_OK;
-}
-
-/*
- * Optional callback - implemented for performance reasons.
- */
-static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
- confd_hkeypath_t *kp, long next)
-{
- char xpath[XPATH_MAXLEN];
- struct nb_node *nb_node;
- struct list *elements;
- const void *parent_list_entry;
- const void *nb_next;
-#define CONFD_OBJECTS_PER_TIME 100
- struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1];
- char pseudo_keys[CONFD_OBJECTS_PER_TIME][32];
- int nobjects = 0;
-
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- confd_data_reply_next_object_array(tctx, NULL, 0, 0);
- return CONFD_OK;
- }
-
- if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
- != 0) {
- confd_data_reply_next_object_array(tctx, NULL, 0, 0);
- return CONFD_OK;
- }
-
- elements = yang_data_list_new();
- nb_next = (next == -1) ? NULL : (void *)next;
-
- memset(objects, 0, sizeof(objects));
- for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) {
- struct confd_next_object *object;
- const struct lysc_node *child;
- struct yang_data *data;
- size_t nvalues = 0;
-
- object = &objects[j];
-
- nb_next = nb_callback_get_next(nb_node, parent_list_entry,
- nb_next);
- if (!nb_next)
- /* End of the list. */
- break;
-
- object->next = (long)nb_next;
-
- /* Leaf-lists require special handling. */
- if (nb_node->snode->nodetype == LYS_LEAFLIST) {
- object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t));
- data = nb_callback_get_elem(nb_node, xpath, nb_next);
- assert(data && data->value);
- CONFD_SET_STR(object->v, data->value);
- nvalues++;
- listnode_add(elements, data);
- goto next;
- }
-
- object->v =
- XMALLOC(MTYPE_CONFD,
- CONFD_MAX_CHILD_NODES * sizeof(confd_value_t));
-
- /*
- * ConfD 6.6 user guide, chapter 6.11 (Operational data lists
- * without keys):
- * "In the response to the get_next_object() callback, the data
- * provider is expected to provide the key values along with the
- * other leafs in an array that is populated according to the
- * data model. This must be done also for this type of list,
- * even though the key isn't actually in the data model. The
- * "pseudo" key must always be the first element in the array".
- */
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
- confd_value_t *v;
-
- snprintf(pseudo_keys[j], sizeof(pseudo_keys[j]), "%lu",
- (unsigned long)nb_next);
-
- v = &object->v[nvalues++];
- CONFD_SET_STR(v, pseudo_keys[j]);
- }
-
- /* Loop through list child nodes. */
- LY_LIST_FOR (lysc_node_child(nb_node->snode), child) {
- struct nb_node *nb_node_child = child->priv;
- char xpath_child[XPATH_MAXLEN * 2];
- confd_value_t *v;
-
- if (nvalues > CONFD_MAX_CHILD_NODES)
- break;
-
- v = &object->v[nvalues++];
-
- /* Non-presence containers, lists and leaf-lists. */
- if (!nb_node_child->cbs.get_elem) {
- CONFD_SET_NOEXISTS(v);
- continue;
- }
-
- snprintf(xpath_child, sizeof(xpath_child), "%s/%s",
- xpath, child->name);
- data = nb_callback_get_elem(nb_node_child, xpath_child,
- nb_next);
- if (data) {
- if (data->value)
- CONFD_SET_STR(v, data->value);
- else {
- /*
- * Presence containers and empty leafs.
- */
- CONFD_SET_XMLTAG(
- v, nb_node_child->confd_hash,
- confd_str2hash(
- nb_node_child->snode
- ->module->ns));
- }
- listnode_add(elements, data);
- } else
- CONFD_SET_NOEXISTS(v);
- }
- next:
- object->n = nvalues;
- nobjects++;
- }
-
- if (nobjects == 0) {
- confd_data_reply_next_object_array(tctx, NULL, 0, 0);
- list_delete(&elements);
- return CONFD_OK;
- }
-
- /* Detect end of the list. */
- if (!nb_next) {
- nobjects++;
- objects[nobjects].v = NULL;
- }
-
- /* Reply to ConfD. */
- confd_data_reply_next_object_arrays(tctx, objects, nobjects, 0);
- if (!nb_next)
- nobjects--;
-
- /* Release memory. */
- list_delete(&elements);
- for (int j = 0; j < nobjects; j++) {
- struct confd_next_object *object;
-
- object = &objects[j];
- XFREE(MTYPE_CONFD, object->v);
- }
-
- return CONFD_OK;
-}
-
-static int frr_confd_notification_send(const char *xpath,
- struct list *arguments)
-{
- struct nb_node *nb_node;
- struct yang_module *module;
- struct confd_datetime now;
- confd_tag_value_t *values;
- int nvalues;
- int i = 0;
- struct yang_data *data;
- struct listnode *node;
- int ret;
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- return -1;
- }
- module = yang_module_find(nb_node->snode->module->name);
- assert(module);
-
- nvalues = 2;
- if (arguments)
- nvalues += listcount(arguments);
-
- values = XMALLOC(MTYPE_CONFD, nvalues * sizeof(*values));
-
- CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash,
- module->confd_hash);
- for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
- struct nb_node *nb_node_arg;
-
- nb_node_arg = nb_node_find(data->xpath);
- if (!nb_node_arg) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__,
- data->xpath);
- XFREE(MTYPE_CONFD, values);
- return NB_ERR;
- }
-
- CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash,
- data->value);
- }
- CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash,
- module->confd_hash);
-
- getdatetime(&now);
- ret = confd_notification_send(live_ctx, &now, values, nvalues);
-
- /* Release memory. */
- XFREE(MTYPE_CONFD, values);
-
- /* Map ConfD return code to northbound return code. */
- switch (ret) {
- case CONFD_OK:
- return NB_OK;
- default:
- return NB_ERR;
- }
-}
-
-static int frr_confd_action_init(struct confd_user_info *uinfo)
-{
- confd_action_set_fd(uinfo, dp_worker_sock);
-
- return CONFD_OK;
-}
-
-static int frr_confd_action_execute(struct confd_user_info *uinfo,
- struct xml_tag *name, confd_hkeypath_t *kp,
- confd_tag_value_t *params, int nparams)
-{
- char xpath[XPATH_MAXLEN];
- struct nb_node *nb_node;
- struct list *input;
- struct list *output;
- struct yang_data *data;
- confd_tag_value_t *reply;
- int ret = CONFD_OK;
- char errmsg[BUFSIZ] = {0};
-
- /* Getting the XPath is tricky. */
- if (kp) {
- /* This is a YANG RPC. */
- frr_confd_get_xpath(kp, xpath, sizeof(xpath));
- strlcat(xpath, "/", sizeof(xpath));
- strlcat(xpath, confd_hash2str(name->tag), sizeof(xpath));
- } else {
- /* This is a YANG action. */
- snprintf(xpath, sizeof(xpath), "/%s:%s",
- confd_ns2prefix(name->ns), confd_hash2str(name->tag));
- }
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- return CONFD_ERR;
- }
-
- input = yang_data_list_new();
- output = yang_data_list_new();
-
- /* Process input nodes. */
- for (int i = 0; i < nparams; i++) {
- char xpath_input[XPATH_MAXLEN * 2];
- char value_str[YANG_VALUE_MAXLEN];
-
- snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath,
- confd_hash2str(params[i].tag.tag));
-
- if (frr_confd_val2str(xpath_input, &params[i].v, value_str,
- sizeof(value_str))
- != 0) {
- flog_err(
- EC_LIB_CONFD_DATA_CONVERT,
- "%s: failed to convert ConfD value to a string",
- __func__);
- ret = CONFD_ERR;
- goto exit;
- }
-
- data = yang_data_new(xpath_input, value_str);
- listnode_add(input, data);
- }
-
- /* Execute callback registered for this XPath. */
- if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
- sizeof(errmsg))
- != NB_OK) {
- flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
- __func__, xpath);
- ret = CONFD_ERR;
- goto exit;
- }
-
- /* Process output nodes. */
- if (listcount(output) > 0) {
- struct listnode *node;
- int i = 0;
-
- reply = XMALLOC(MTYPE_CONFD,
- listcount(output) * sizeof(*reply));
-
- for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
- struct nb_node *nb_node_output;
- int hash;
-
- nb_node_output = nb_node_find(data->xpath);
- if (!nb_node_output) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__,
- data->xpath);
- goto exit;
- }
-
- hash = confd_str2hash(nb_node_output->snode->name);
- CONFD_SET_TAG_STR(&reply[i++], hash, data->value);
- }
- confd_action_reply_values(uinfo, reply, listcount(output));
- XFREE(MTYPE_CONFD, reply);
- }
-
-exit:
- /* Release memory. */
- list_delete(&input);
- list_delete(&output);
-
- return ret;
-}
-
-
-static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd)
-{
- int ret;
-
- ret = confd_fd_ready(dctx, fd);
- if (ret == CONFD_EOF) {
- flog_err_confd("confd_fd_ready");
- frr_confd_finish();
- return -1;
- } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
- flog_err_confd("confd_fd_ready");
- frr_confd_finish();
- return -1;
- }
-
- return 0;
-}
-
-static void frr_confd_dp_ctl_read(struct event *thread)
-{
- struct confd_daemon_ctx *dctx = EVENT_ARG(thread);
- int fd = EVENT_FD(thread);
-
- event_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl);
-
- frr_confd_dp_read(dctx, fd);
-}
-
-static void frr_confd_dp_worker_read(struct event *thread)
-{
- struct confd_daemon_ctx *dctx = EVENT_ARG(thread);
- int fd = EVENT_FD(thread);
-
- event_add_read(master, frr_confd_dp_worker_read, dctx, fd,
- &t_dp_worker);
-
- frr_confd_dp_read(dctx, fd);
-}
-
-static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg)
-{
- struct nb_node *nb_node = snode->priv;
- struct confd_data_cbs *data_cbs = arg;
-
- if (!nb_node || !CHECK_FLAG(snode->flags, LYS_CONFIG_R))
- return YANG_ITER_CONTINUE;
- /* We only need to subscribe to the root of the state subtrees. */
- if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
- return YANG_ITER_CONTINUE;
-
- DEBUGD(&nb_dbg_client_confd,
- "%s: providing data to '%s' (callpoint %s)", __func__,
- nb_node->xpath, snode->name);
-
- strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint));
- if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK)
- flog_err_confd("confd_register_data_cb");
-
- return YANG_ITER_CONTINUE;
-}
-
-static int frr_confd_init_dp(const char *program_name)
-{
- struct confd_trans_cbs trans_cbs;
- struct confd_data_cbs data_cbs;
- struct confd_notification_stream_cbs ncbs;
- struct confd_action_cbs acbs;
-
- /* Initialize daemon context. */
- dctx = confd_init_daemon(program_name);
- if (!dctx) {
- flog_err_confd("confd_init_daemon");
- goto error;
- }
-
- /*
- * Inform we want to receive YANG values as raw strings, and that we
- * want to provide only strings in the reply functions, regardless of
- * the YANG type.
- */
- confd_set_daemon_flags(dctx, CONFD_DAEMON_FLAG_STRINGSONLY);
-
- /* Establish a control socket. */
- dp_ctl_sock = socket(PF_INET, SOCK_STREAM, 0);
- if (dp_ctl_sock < 0) {
- flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
- __func__, safe_strerror(errno));
- goto error;
- }
-
- if (confd_connect(dctx, dp_ctl_sock, CONTROL_SOCKET, &confd_addr,
- sizeof(struct sockaddr_in))
- != CONFD_OK) {
- flog_err_confd("confd_connect");
- goto error;
- }
-
- /*
- * Establish a worker socket (only one since this plugin runs on a
- * single thread).
- */
- dp_worker_sock = socket(PF_INET, SOCK_STREAM, 0);
- if (dp_worker_sock < 0) {
- flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
- __func__, safe_strerror(errno));
- goto error;
- }
- if (confd_connect(dctx, dp_worker_sock, WORKER_SOCKET, &confd_addr,
- sizeof(struct sockaddr_in))
- != CONFD_OK) {
- flog_err_confd("confd_connect");
- goto error;
- }
-
- /* Register transaction callback functions. */
- memset(&trans_cbs, 0, sizeof(trans_cbs));
- trans_cbs.init = frr_confd_transaction_init;
- confd_register_trans_cb(dctx, &trans_cbs);
-
- /* Register our read/write callbacks. */
- memset(&data_cbs, 0, sizeof(data_cbs));
- data_cbs.get_elem = frr_confd_data_get_elem;
- data_cbs.exists_optional = frr_confd_data_get_elem;
- data_cbs.get_next = frr_confd_data_get_next;
- data_cbs.get_object = frr_confd_data_get_object;
- data_cbs.get_next_object = frr_confd_data_get_next_object;
-
- /*
- * Iterate over all loaded YANG modules and subscribe to the paths
- * referent to state data.
- */
- yang_snodes_iterate(NULL, frr_confd_subscribe_state, 0, &data_cbs);
-
- /* Register notification stream. */
- memset(&ncbs, 0, sizeof(ncbs));
- ncbs.fd = dp_worker_sock;
- /*
- * RFC 5277 - Section 3.2.3:
- * A NETCONF server implementation supporting the notification
- * capability MUST support the "NETCONF" notification event
- * stream. This stream contains all NETCONF XML event notifications
- * supported by the NETCONF server.
- */
- strlcpy(ncbs.streamname, "NETCONF", sizeof(ncbs.streamname));
- if (confd_register_notification_stream(dctx, &ncbs, &live_ctx)
- != CONFD_OK) {
- flog_err_confd("confd_register_notification_stream");
- goto error;
- }
-
- /* Register the action handler callback. */
- memset(&acbs, 0, sizeof(acbs));
- strlcpy(acbs.actionpoint, "actionpoint", sizeof(acbs.actionpoint));
- acbs.init = frr_confd_action_init;
- acbs.action = frr_confd_action_execute;
- if (confd_register_action_cbs(dctx, &acbs) != CONFD_OK) {
- flog_err_confd("confd_register_action_cbs");
- goto error;
- }
-
- /* Notify we registered all callbacks we wanted. */
- if (confd_register_done(dctx) != CONFD_OK) {
- flog_err_confd("confd_register_done");
- goto error;
- }
-
- event_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock,
- &t_dp_ctl);
- event_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock,
- &t_dp_worker);
-
- return 0;
-
-error:
- frr_confd_finish_dp();
-
- return -1;
-}
-
-static void frr_confd_finish_dp(void)
-{
- if (dp_worker_sock > 0) {
- EVENT_OFF(t_dp_worker);
- close(dp_worker_sock);
- }
- if (dp_ctl_sock > 0) {
- EVENT_OFF(t_dp_ctl);
- close(dp_ctl_sock);
- }
- if (dctx != NULL)
- confd_release_daemon(dctx);
-}
-
-/* ------------ CLI ------------ */
-
-DEFUN (debug_nb_confd,
- debug_nb_confd_cmd,
- "[no] debug northbound client confd",
- NO_STR
- DEBUG_STR
- "Northbound debugging\n"
- "Client\n"
- "ConfD\n")
-{
- uint32_t mode = DEBUG_NODE2MODE(vty->node);
- bool no = strmatch(argv[0]->text, "no");
-
- DEBUG_MODE_SET(&nb_dbg_client_confd, mode, !no);
-
- return CMD_SUCCESS;
-}
-
-static int frr_confd_debug_config_write(struct vty *vty)
-{
- if (DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_CONF))
- vty_out(vty, "debug northbound client confd\n");
-
- return 0;
-}
-
-static int frr_confd_debug_set_all(uint32_t flags, bool set)
-{
- DEBUG_FLAGS_SET(&nb_dbg_client_confd, flags, set);
-
- /* If all modes have been turned off, don't preserve options. */
- if (!DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_ALL))
- DEBUG_CLEAR(&nb_dbg_client_confd);
-
- return 0;
-}
-
-static void frr_confd_cli_init(void)
-{
- hook_register(nb_client_debug_config_write,
- frr_confd_debug_config_write);
- hook_register(nb_client_debug_set_all, frr_confd_debug_set_all);
-
- install_element(ENABLE_NODE, &debug_nb_confd_cmd);
- install_element(CONFIG_NODE, &debug_nb_confd_cmd);
-}
-
-/* ------------ Main ------------ */
-
-static int frr_confd_calculate_snode_hash(const struct lysc_node *snode,
- void *arg)
-{
- struct nb_node *nb_node = snode->priv;
-
- if (nb_node)
- nb_node->confd_hash = confd_str2hash(snode->name);
-
- return YANG_ITER_CONTINUE;
-}
-
-static int frr_confd_init(const char *program_name)
-{
- struct sockaddr_in *confd_addr4 = (struct sockaddr_in *)&confd_addr;
- int debuglevel = CONFD_SILENT;
- int ret = -1;
-
- /* Initialize ConfD library. */
- confd_init(program_name, stderr, debuglevel);
-
- confd_addr4->sin_family = AF_INET;
- confd_addr4->sin_addr.s_addr = inet_addr("127.0.0.1");
- confd_addr4->sin_port = htons(CONFD_PORT);
- if (confd_load_schemas(&confd_addr, sizeof(struct sockaddr_in))
- != CONFD_OK) {
- flog_err_confd("confd_load_schemas");
- return -1;
- }
-
- ret = frr_confd_init_cdb();
- if (ret != 0)
- goto error;
-
- ret = frr_confd_init_dp(program_name);
- if (ret != 0) {
- frr_confd_finish_cdb();
- goto error;
- }
-
- yang_snodes_iterate(NULL, frr_confd_calculate_snode_hash, 0, NULL);
-
- hook_register(nb_notification_send, frr_confd_notification_send);
-
- confd_connected = true;
- return 0;
-
-error:
- confd_free_schemas();
-
- return ret;
-}
-
-static int frr_confd_finish(void)
-{
- if (!confd_connected)
- return 0;
-
- frr_confd_finish_cdb();
- frr_confd_finish_dp();
-
- confd_free_schemas();
-
- confd_connected = false;
-
- return 0;
-}
-
-static int frr_confd_module_late_init(struct event_loop *tm)
-{
- master = tm;
-
- if (frr_confd_init(frr_get_progname()) < 0) {
- flog_err(EC_LIB_CONFD_INIT,
- "failed to initialize the ConfD module");
- return -1;
- }
-
- hook_register(frr_fini, frr_confd_finish);
- frr_confd_cli_init();
-
- return 0;
-}
-
-static int frr_confd_module_init(void)
-{
- hook_register(frr_late_init, frr_confd_module_late_init);
-
- return 0;
-}
-
-FRR_MODULE_SETUP(.name = "frr_confd", .version = FRR_VERSION,
- .description = "FRR ConfD integration module",
- .init = frr_confd_module_init,
-);
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index 2394b5e865..7e7190f5a4 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -342,38 +342,6 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys)
/* ======================= */
/**
- * __xpath_pop_node() - remove the last node from xpath string
- * @xpath: an xpath string
- *
- * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop.
- */
-static int __xpath_pop_node(char *xpath)
-{
- int len = strlen(xpath);
- bool abs = xpath[0] == '/';
- char *slash;
-
- /* "//" or "/" => NULL */
- if (abs && (len == 1 || (len == 2 && xpath[1] == '/')))
- return NB_ERR_NOT_FOUND;
-
- slash = (char *)frrstr_back_to_char(xpath, '/');
- /* "/foo/bar/" or "/foo/bar//" => "/foo " */
- if (slash && slash == &xpath[len - 1]) {
- xpath[--len] = 0;
- slash = (char *)frrstr_back_to_char(xpath, '/');
- if (slash && slash == &xpath[len - 1]) {
- xpath[--len] = 0;
- slash = (char *)frrstr_back_to_char(xpath, '/');
- }
- }
- if (!slash)
- return NB_ERR_NOT_FOUND;
- *slash = 0;
- return NB_OK;
-}
-
-/**
* nb_op_xpath_to_trunk() - generate a lyd_node tree (trunk) using an xpath.
* @xpath_in: xpath query string to build trunk from.
* @dnode: resulting tree (trunk)
@@ -398,7 +366,7 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in,
if (err == LY_SUCCESS)
break;
- ret = __xpath_pop_node(xpath);
+ ret = yang_xpath_pop_node(xpath);
if (ret != NB_OK)
break;
}
diff --git a/lib/plist.c b/lib/plist.c
index 618d92b549..2cfaa7d81d 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1073,17 +1073,13 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
struct prefix_master *master;
int64_t seqnum = 0;
json_object *json = NULL;
- json_object *json_proto = NULL;
master = prefix_master_get(afi, 0);
if (master == NULL)
return CMD_WARNING;
- if (uj) {
+ if (uj)
json = json_object_new_object();
- json_proto = json_object_new_object();
- json_object_object_add(json, frr_protoname, json_proto);
- }
if (seq)
seqnum = (int64_t)atol(seq);
@@ -1096,8 +1092,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
"%% Can't find specified prefix-list\n");
return CMD_WARNING;
}
- vty_show_prefix_entry(vty, json_proto, afi, plist, master,
- dtype, seqnum);
+ vty_show_prefix_entry(vty, json, afi, plist, master, dtype,
+ seqnum);
} else {
if (dtype == detail_display || dtype == summary_display) {
if (master->recent && !uj)
@@ -1107,8 +1103,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
}
frr_each (plist, &master->str, plist)
- vty_show_prefix_entry(vty, json_proto, afi, plist,
- master, dtype, seqnum);
+ vty_show_prefix_entry(vty, json, afi, plist, master,
+ dtype, seqnum);
}
return vty_json(vty, json);
@@ -1227,7 +1223,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
#include "lib/plist_clippy.c"
-DEFPY (show_ip_prefix_list,
+DEFPY_NOSH (show_ip_prefix_list,
show_ip_prefix_list_cmd,
"show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
@@ -1239,6 +1235,7 @@ DEFPY (show_ip_prefix_list,
JSON_STR)
{
enum display_type dtype = normal_display;
+
if (dseq)
dtype = sequential_display;
@@ -1258,6 +1255,7 @@ DEFPY (show_ip_prefix_list_prefix,
"First matched prefix\n")
{
enum display_type dtype = normal_display;
+
if (dl)
dtype = longer_display;
else if (dfm)
@@ -1267,7 +1265,7 @@ DEFPY (show_ip_prefix_list_prefix,
dtype);
}
-DEFPY (show_ip_prefix_list_summary,
+DEFPY_NOSH (show_ip_prefix_list_summary,
show_ip_prefix_list_summary_cmd,
"show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]",
SHOW_STR
@@ -1281,7 +1279,7 @@ DEFPY (show_ip_prefix_list_summary,
summary_display, !!uj);
}
-DEFPY (show_ip_prefix_list_detail,
+DEFPY_NOSH (show_ip_prefix_list_detail,
show_ip_prefix_list_detail_cmd,
"show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]",
SHOW_STR
@@ -1307,7 +1305,7 @@ DEFPY (clear_ip_prefix_list,
return vty_clear_prefix_list(vty, AFI_IP, name, prefix_str);
}
-DEFPY (show_ipv6_prefix_list,
+DEFPY_NOSH(show_ipv6_prefix_list,
show_ipv6_prefix_list_cmd,
"show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
SHOW_STR
@@ -1319,6 +1317,7 @@ DEFPY (show_ipv6_prefix_list,
JSON_STR)
{
enum display_type dtype = normal_display;
+
if (dseq)
dtype = sequential_display;
@@ -1338,6 +1337,7 @@ DEFPY (show_ipv6_prefix_list_prefix,
"First matched prefix\n")
{
enum display_type dtype = normal_display;
+
if (dl)
dtype = longer_display;
else if (dfm)
@@ -1347,7 +1347,7 @@ DEFPY (show_ipv6_prefix_list_prefix,
prefix_str, dtype);
}
-DEFPY (show_ipv6_prefix_list_summary,
+DEFPY_NOSH (show_ipv6_prefix_list_summary,
show_ipv6_prefix_list_summary_cmd,
"show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]",
SHOW_STR
@@ -1361,7 +1361,7 @@ DEFPY (show_ipv6_prefix_list_summary,
summary_display, !!uj);
}
-DEFPY (show_ipv6_prefix_list_detail,
+DEFPY_NOSH (show_ipv6_prefix_list_detail,
show_ipv6_prefix_list_detail_cmd,
"show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]",
SHOW_STR
diff --git a/lib/routing_nb.h b/lib/routing_nb.h
index e805e1cd0f..26b4cf08d8 100644
--- a/lib/routing_nb.h
+++ b/lib/routing_nb.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#ifndef _FRR_ROUTING_NB_H_
#define _FRR_ROUTING_NB_H_
diff --git a/lib/sigevent.c b/lib/sigevent.c
index 06f80db4cc..3e69f280da 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -240,7 +240,17 @@ core_handler(int signo, siginfo_t *siginfo, void *context)
/* dump memory stats on core */
log_memstats(stderr, "core_handler");
- zlog_tls_buffer_fini();
+ /*
+ * This is a buffer flush because FRR is going down
+ * hard. This is especially important if the crash
+ * was caused by a memory operation and if we call
+ * zlog_tls_buffer_fini() then it has memory
+ * operations as well. This will cause the
+ * core dump to not happen. BAD MOJO
+ * So this is intentional, let's try to flush
+ * what we can and let the crash happen.
+ */
+ zlog_tls_buffer_flush();
/* give the kernel a chance to generate a coredump */
sigaddset(&sigset, signo);
diff --git a/lib/srv6.c b/lib/srv6.c
index dceb6ab48b..a82103e423 100644
--- a/lib/srv6.c
+++ b/lib/srv6.c
@@ -94,9 +94,11 @@ const char *seg6local_context2str(char *str, size_t size,
snprintf(str, size, "table %u", ctx->table);
return str;
- case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
case ZEBRA_SEG6_LOCAL_ACTION_END_B6:
case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP:
+ snprintfrr(str, size, "nh6 %pI6", &ctx->nh6);
+ return str;
+ case ZEBRA_SEG6_LOCAL_ACTION_END_DX2:
case ZEBRA_SEG6_LOCAL_ACTION_END_BM:
case ZEBRA_SEG6_LOCAL_ACTION_END_S:
case ZEBRA_SEG6_LOCAL_ACTION_END_AS:
diff --git a/lib/stream.h b/lib/stream.h
index 85eebb47be..61eaa46c95 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -105,9 +105,7 @@ struct stream_fifo {
/* number of streams in this fifo */
atomic_size_t count;
-#if defined DEV_BUILD
atomic_size_t max_count;
-#endif
struct stream *head;
struct stream *tail;
diff --git a/lib/subdir.am b/lib/subdir.am
index c621ad0658..5ec6adf4c0 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -410,18 +410,6 @@ lib_libfrrzmq_la_SOURCES = \
#end
#
-# Tail-f's ConfD support
-#
-if CONFD
-module_LTLIBRARIES += lib/confd.la
-endif
-
-lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS)
-lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS)
-lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS)
-lib_confd_la_SOURCES = lib/northbound_confd.c
-
-#
# Sysrepo support
#
if SYSREPO
diff --git a/lib/vrf.c b/lib/vrf.c
index 9f4c5cdddc..31632a80d5 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -326,6 +326,33 @@ void vrf_disable(struct vrf *vrf)
(*vrf_master.vrf_disable_hook)(vrf);
}
+void vrf_iterate(vrf_iter_func fnc)
+{
+ struct vrf *vrf, *tmp;
+
+ if (debug_vrf)
+ zlog_debug("%s: vrf subsystem iteration", __func__);
+
+ RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
+
+ fnc(vrf);
+ }
+
+ RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
+
+ fnc(vrf);
+ }
+
+ /* Finally process default VRF */
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ if (vrf)
+ fnc(vrf);
+}
+
const char *vrf_id_to_name(vrf_id_t vrf_id)
{
struct vrf *vrf;
@@ -542,32 +569,12 @@ static void vrf_terminate_single(struct vrf *vrf)
vrf_delete(vrf);
}
-/* Terminate VRF module. */
void vrf_terminate(void)
{
- struct vrf *vrf, *tmp;
-
if (debug_vrf)
zlog_debug("%s: Shutting down vrf subsystem", __func__);
- RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) {
- if (vrf->vrf_id == VRF_DEFAULT)
- continue;
-
- vrf_terminate_single(vrf);
- }
-
- RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) {
- if (vrf->vrf_id == VRF_DEFAULT)
- continue;
-
- vrf_terminate_single(vrf);
- }
-
- /* Finally terminate default VRF */
- vrf = vrf_lookup_by_id(VRF_DEFAULT);
- if (vrf)
- vrf_terminate_single(vrf);
+ vrf_iterate(vrf_terminate_single);
}
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
@@ -629,7 +636,7 @@ int vrf_configure_backend(enum vrf_backend_type backend)
}
/* vrf CLI commands */
-DEFUN_NOSH(vrf_exit,
+DEFUN_YANG_NOSH (vrf_exit,
vrf_exit_cmd,
"exit-vrf",
"Exit current mode and down to previous mode\n")
diff --git a/lib/vrf.h b/lib/vrf.h
index 4277a51bb1..3ebb6ddf53 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -202,6 +202,12 @@ extern void vrf_init(int (*create)(struct vrf *vrf),
int (*destroy)(struct vrf *vrf));
/*
+ * Iterate over custom VRFs and round up by processing the default VRF.
+ */
+typedef void (*vrf_iter_func)(struct vrf *vrf);
+extern void vrf_iterate(vrf_iter_func fnc);
+
+/*
* Call vrf_terminate when the protocol is being shutdown
*/
extern void vrf_terminate(void);
diff --git a/lib/vty.c b/lib/vty.c
index 912c893556..d69b5eebd2 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -3826,6 +3826,23 @@ static int vty_mgmt_get_tree_result_notified(
return 0;
}
+static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client,
+ uintptr_t user_data,
+ uint64_t client_id, uint64_t session_id,
+ uintptr_t session_ctx, uint64_t req_id,
+ const char *xpath)
+{
+ struct vty *vty = (struct vty *)session_ctx;
+
+ debug_fe_client("EDIT request for client 0x%" PRIx64 " req-id %" PRIu64
+ " was successful, xpath: %s",
+ client_id, req_id, xpath);
+
+ vty_mgmt_resume_response(vty, CMD_SUCCESS);
+
+ return 0;
+}
+
static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx,
@@ -3867,6 +3884,7 @@ static struct mgmt_fe_client_cbs mgmt_cbs = {
.commit_config_notify = vty_mgmt_commit_config_result_notified,
.get_data_notify = vty_mgmt_get_data_result_notified,
.get_tree_notify = vty_mgmt_get_tree_result_notified,
+ .edit_notify = vty_mgmt_edit_result_notified,
.error_notify = vty_mgmt_error_notified,
};
@@ -4122,6 +4140,28 @@ int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore,
return 0;
}
+int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
+ LYD_FORMAT request_type, uint8_t flags,
+ uint8_t operation, const char *xpath,
+ const char *data)
+{
+ vty->mgmt_req_id++;
+
+ if (mgmt_fe_send_edit_req(mgmt_fe_client, vty->mgmt_session_id,
+ vty->mgmt_req_id, datastore, request_type,
+ flags, operation, xpath, data)) {
+ zlog_err("Failed to send EDIT to MGMTD session-id: %" PRIu64
+ " req-id %" PRIu64 ".",
+ vty->mgmt_session_id, vty->mgmt_req_id);
+ vty_out(vty, "Failed to send EDIT to MGMTD!\n");
+ return -1;
+ }
+
+ vty->mgmt_req_pending_cmd = "MESSAGE_EDIT_REQ";
+
+ return 0;
+}
+
/* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging)
{
diff --git a/lib/vty.h b/lib/vty.h
index a59ac7a652..0e0c92c336 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -419,6 +419,10 @@ extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
extern int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore,
LYD_FORMAT result_type, uint8_t flags,
uint8_t defaults, const char *xpath);
+extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore,
+ LYD_FORMAT request_type, uint8_t flags,
+ uint8_t operation, const char *xpath,
+ const char *data);
extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
bool lock, bool scok);
extern void vty_mgmt_resume_response(struct vty *vty, int ret);
diff --git a/lib/yang.c b/lib/yang.c
index d71cb2f498..013a762842 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -12,6 +12,7 @@
#include "yang.h"
#include "yang_translator.h"
#include "northbound.h"
+#include "frrstr.h"
#include "lib/config_paths.h"
@@ -1122,6 +1123,32 @@ int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys)
return NB_OK;
}
+int yang_xpath_pop_node(char *xpath)
+{
+ int len = strlen(xpath);
+ bool abs = xpath[0] == '/';
+ char *slash;
+
+ /* "//" or "/" => NULL */
+ if (abs && (len == 1 || (len == 2 && xpath[1] == '/')))
+ return NB_ERR_NOT_FOUND;
+
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ /* "/foo/bar/" or "/foo/bar//" => "/foo " */
+ if (slash && slash == &xpath[len - 1]) {
+ xpath[--len] = 0;
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ if (slash && slash == &xpath[len - 1]) {
+ xpath[--len] = 0;
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ }
+ }
+ if (!slash)
+ return NB_ERR_NOT_FOUND;
+ *slash = 0;
+ return NB_OK;
+}
+
/*
* ------------------------
* Libyang Future Functions
@@ -1275,6 +1302,42 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
#endif
}
+/* Can be replaced by `lyd_parse_data` with libyang >= 2.1.156 */
+LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent,
+ struct ly_in *in, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options,
+ struct lyd_node **tree)
+{
+ struct lyd_node *child;
+ LY_ERR err;
+
+ err = lyd_parse_data(ctx, parent, in, format, parse_options,
+ validate_options, tree);
+ if (err)
+ return err;
+
+ if (!parent || !(parse_options & LYD_PARSE_ONLY))
+ return LY_SUCCESS;
+
+ /*
+ * Versions prior to 2.1.156 don't return `tree` if `parent` is not NULL
+ * and validation is disabled (`LYD_PARSE_ONLY`). To work around this,
+ * go through the children and find the one with `LYD_NEW` flag set.
+ */
+ *tree = NULL;
+
+ LY_LIST_FOR (lyd_child_no_keys(parent), child) {
+ if (child->flags & LYD_NEW) {
+ *tree = child;
+ break;
+ }
+ }
+
+ assert(tree);
+
+ return LY_SUCCESS;
+}
+
/*
* Safe to remove after libyang v2.1.128 is required
*/
diff --git a/lib/yang.h b/lib/yang.h
index 65f6a73e0b..25703b1879 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -45,9 +45,6 @@ struct yang_module {
RB_ENTRY(yang_module) entry;
const char *name;
const struct lys_module *info;
-#ifdef HAVE_CONFD
- int confd_hash;
-#endif
#ifdef HAVE_SYSREPO
sr_subscription_ctx_t *sr_subscription;
struct event *sr_thread;
@@ -771,6 +768,14 @@ extern int yang_get_key_preds(char *s, const struct lysc_node *snode,
extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys);
/**
+ * yang_xpath_pop_node() - remove the last node from xpath string
+ * @xpath: an xpath string
+ *
+ * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop.
+ */
+extern int yang_xpath_pop_node(char *xpath);
+
+/**
* yang_resolve_snodes() - Resolve an XPath to matching schema nodes.
* @ly_ctx: libyang context to operate on.
* @xpath: the path or XPath to resolve.
@@ -800,6 +805,11 @@ extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
const struct yang_list_keys *keys,
struct lyd_node **nodes);
extern LY_ERR yang_lyd_trim_xpath(struct lyd_node **rootp, const char *xpath);
+extern LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx,
+ struct lyd_node *parent, struct ly_in *in,
+ LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options,
+ struct lyd_node **tree);
#ifdef __cplusplus
}
diff --git a/lib/zclient.c b/lib/zclient.c
index 51ebb56275..4cbd04c116 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -282,6 +282,7 @@ static void zclient_flush_data(struct event *thread)
zclient->sock, &zclient->t_write);
break;
case BUFFER_EMPTY:
+ /* Currently only Sharpd and Bgpd has callbacks defined */
if (zclient->zebra_buffer_write_ready)
(*zclient->zebra_buffer_write_ready)();
break;
diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c
index 62d1a0109a..ab0da64d8f 100644
--- a/mgmtd/mgmt_fe_adapter.c
+++ b/mgmtd/mgmt_fe_adapter.c
@@ -898,11 +898,13 @@ static int mgmt_fe_session_handle_commit_config_req_msg(
/*
* Create COMMITConfig request under the transaction
*/
- if (mgmt_txn_send_commit_config_req(
- session->cfg_txn_id, commcfg_req->req_id,
- commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id,
- dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort,
- false) != 0) {
+ if (mgmt_txn_send_commit_config_req(session->cfg_txn_id,
+ commcfg_req->req_id,
+ commcfg_req->src_ds_id, src_ds_ctx,
+ commcfg_req->dst_ds_id, dst_ds_ctx,
+ commcfg_req->validate_only,
+ commcfg_req->abort, false,
+ NULL) != 0) {
fe_adapter_send_commit_cfg_reply(
session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
@@ -1099,6 +1101,33 @@ done:
return ret;
}
+static int fe_adapter_send_edit_reply(struct mgmt_fe_session_ctx *session,
+ uint64_t req_id, const char *xpath)
+{
+ struct mgmt_msg_edit_reply *msg;
+ int ret;
+
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit_reply, 0,
+ MTYPE_MSG_NATIVE_EDIT_REPLY);
+ msg->refer_id = session->session_id;
+ msg->req_id = req_id;
+ msg->code = MGMT_MSG_CODE_EDIT_REPLY;
+
+ mgmt_msg_native_xpath_encode(msg, xpath);
+
+ __dbg("Sending edit-reply from adapter %s to session-id %" PRIu64
+ " req-id %" PRIu64 " len %u",
+ session->adapter->name, session->session_id, req_id,
+ mgmt_msg_native_get_msg_len(msg));
+
+ ret = fe_adapter_send_native_msg(session->adapter, msg,
+ mgmt_msg_native_get_msg_len(msg),
+ false);
+ mgmt_msg_native_free_msg(msg);
+
+ return ret;
+}
+
/**
* fe_adapter_handle_get_data() - Handle a get-tree message from a FE client.
* @session: the client session.
@@ -1224,6 +1253,112 @@ done:
darr_free(xpath_resolved);
}
+static void fe_adapter_handle_edit(struct mgmt_fe_session_ctx *session,
+ void *__msg, size_t msg_len)
+{
+ struct mgmt_msg_edit *msg = __msg;
+ Mgmtd__DatastoreId ds_id, rds_id;
+ struct mgmt_ds_ctx *ds_ctx, *rds_ctx;
+ const char *xpath, *data;
+ bool lock, commit;
+ int ret;
+
+ if (msg->datastore != MGMT_MSG_DATASTORE_CANDIDATE) {
+ fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
+ "Unsupported datastore");
+ return;
+ }
+
+ xpath = mgmt_msg_native_xpath_data_decode(msg, msg_len, data);
+ if (!xpath || !data) {
+ fe_adapter_send_error(session, msg->req_id, false, -EINVAL,
+ "Invalid message");
+ return;
+ }
+
+ ds_id = MGMTD_DS_CANDIDATE;
+ ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id);
+ assert(ds_ctx);
+
+ rds_id = MGMTD_DS_RUNNING;
+ rds_ctx = mgmt_ds_get_ctx_by_id(mm, rds_id);
+ assert(rds_ctx);
+
+ lock = CHECK_FLAG(msg->flags, EDIT_FLAG_IMPLICIT_LOCK);
+ commit = CHECK_FLAG(msg->flags, EDIT_FLAG_IMPLICIT_COMMIT);
+
+ if (lock) {
+ if (mgmt_fe_session_write_lock_ds(ds_id, ds_ctx, session)) {
+ fe_adapter_send_error(session, msg->req_id, false,
+ -EBUSY,
+ "Candidate DS is locked by another session");
+ return;
+ }
+
+ if (commit) {
+ if (mgmt_fe_session_write_lock_ds(rds_id, rds_ctx,
+ session)) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx,
+ session);
+ fe_adapter_send_error(
+ session, msg->req_id, false, -EBUSY,
+ "Running DS is locked by another session");
+ return;
+ }
+ }
+ } else {
+ if (!session->ds_locked[ds_id]) {
+ fe_adapter_send_error(session, msg->req_id, false,
+ -EBUSY,
+ "Candidate DS is not locked");
+ return;
+ }
+
+ if (commit) {
+ if (!session->ds_locked[rds_id]) {
+ fe_adapter_send_error(session, msg->req_id,
+ false, -EBUSY,
+ "Running DS is not locked");
+ return;
+ }
+ }
+ }
+
+ session->cfg_txn_id = mgmt_create_txn(session->session_id,
+ MGMTD_TXN_TYPE_CONFIG);
+ if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
+ if (lock) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+ if (commit)
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx,
+ session);
+ }
+ fe_adapter_send_error(session, msg->req_id, false, -EBUSY,
+ "Failed to create a configuration transaction");
+ return;
+ }
+
+ __dbg("Created new config txn-id: %" PRIu64 " for session-id: %" PRIu64,
+ session->cfg_txn_id, session->session_id);
+
+ ret = mgmt_txn_send_edit(session->cfg_txn_id, msg->req_id, ds_id,
+ ds_ctx, rds_id, rds_ctx, lock, commit,
+ msg->request_type, msg->flags, msg->operation,
+ xpath, data);
+ if (ret) {
+ /* destroy the just created txn */
+ mgmt_destroy_txn(&session->cfg_txn_id);
+ if (lock) {
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+ if (commit)
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx,
+ session);
+ }
+ fe_adapter_send_error(session, msg->req_id, false, -EBUSY,
+ "Failed to create a configuration transaction");
+ }
+}
+
/**
* Handle a native encoded message from the FE client.
*/
@@ -1245,6 +1380,9 @@ static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
case MGMT_MSG_CODE_GET_DATA:
fe_adapter_handle_get_data(session, msg, msg_len);
break;
+ case MGMT_MSG_CODE_EDIT:
+ fe_adapter_handle_edit(session, msg, msg_len);
+ break;
default:
__log_err("unknown native message session-id %" PRIu64
" req-id %" PRIu64 " code %u to FE adapter %s",
@@ -1484,6 +1622,52 @@ int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
return ret;
}
+int mgmt_fe_adapter_send_edit_reply(uint64_t session_id, uint64_t txn_id,
+ uint64_t req_id, bool unlock, bool commit,
+ const char *xpath, int16_t error,
+ const char *errstr)
+{
+ struct mgmt_fe_session_ctx *session;
+ Mgmtd__DatastoreId ds_id, rds_id;
+ struct mgmt_ds_ctx *ds_ctx, *rds_ctx;
+ int ret;
+
+ session = mgmt_session_id2ctx(session_id);
+ if (!session || session->cfg_txn_id != txn_id)
+ return -1;
+
+ if (session->cfg_txn_id != MGMTD_TXN_ID_NONE && commit)
+ mgmt_fe_session_register_event(session,
+ MGMTD_FE_SESSION_CFG_TXN_CLNUP);
+
+ if (unlock) {
+ ds_id = MGMTD_DS_CANDIDATE;
+ ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id);
+ assert(ds_ctx);
+
+ mgmt_fe_session_unlock_ds(ds_id, ds_ctx, session);
+
+ if (commit) {
+ rds_id = MGMTD_DS_RUNNING;
+ rds_ctx = mgmt_ds_get_ctx_by_id(mm, rds_id);
+ assert(rds_ctx);
+
+ mgmt_fe_session_unlock_ds(rds_id, rds_ctx, session);
+ }
+ }
+
+ if (error)
+ ret = fe_adapter_send_error(session, req_id, false, error, "%s",
+ errstr);
+ else
+ ret = fe_adapter_send_edit_reply(session, req_id, xpath);
+
+ if (session->cfg_txn_id != MGMTD_TXN_ID_NONE && !commit)
+ mgmt_destroy_txn(&session->cfg_txn_id);
+
+ return ret;
+}
+
/**
* Send an error back to the FE client and cleanup any in-progress txn.
*/
diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h
index e768a3cca0..8d61ffe910 100644
--- a/mgmtd/mgmt_fe_adapter.h
+++ b/mgmtd/mgmt_fe_adapter.h
@@ -163,6 +163,26 @@ mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
int partial_error, bool short_circuit_ok);
/**
+ * Send edit reply back to client. If error is not 0, a native error is sent.
+ *
+ * This also cleans up and frees the transaction.
+ *
+ * Args:
+ * session_id: the session.
+ * txn_id: the txn_id this data pertains to
+ * req_id: the req id for the edit message
+ * unlock: implicit-lock flag was set in the request
+ * commit: implicit-commit flag was set in the request
+ * xpath: the xpath of the data node that was created
+ * error: the error code, zero for successful request
+ * errstr: the error string, if error is non-zero
+ */
+extern int mgmt_fe_adapter_send_edit_reply(uint64_t session_id, uint64_t txn_id,
+ uint64_t req_id, bool unlock,
+ bool commit, const char *xpath,
+ int16_t error, const char *errstr);
+
+/**
* Send an error back to the FE client using native messaging.
*
* This also cleans up and frees the transaction.
diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c
index 7e3ded8209..a33a55efca 100644
--- a/mgmtd/mgmt_testc.c
+++ b/mgmtd/mgmt_testc.c
@@ -133,8 +133,10 @@ static void sigusr1(void)
static void quit(int exit_code)
{
EVENT_OFF(event_timeout);
- frr_fini();
darr_free(__client_cbs.notif_xpaths);
+
+ frr_fini();
+
exit(exit_code);
}
diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c
index a4e0fa2169..901163c6e6 100644
--- a/mgmtd/mgmt_txn.c
+++ b/mgmtd/mgmt_txn.c
@@ -30,7 +30,6 @@ enum mgmt_txn_event {
MGMTD_TXN_PROC_GETCFG,
MGMTD_TXN_PROC_GETTREE,
MGMTD_TXN_COMMITCFG_TIMEOUT,
- MGMTD_TXN_GETTREE_TIMEOUT,
};
PREDECL_LIST(mgmt_txn_reqs);
@@ -92,6 +91,11 @@ DECLARE_LIST(mgmt_txn_batches, struct mgmt_txn_be_cfg_batch, list_linkage);
#define FOREACH_TXN_CFG_BATCH_IN_LIST(list, batch) \
frr_each_safe (mgmt_txn_batches, list, batch)
+struct mgmt_edit_req {
+ char xpath_created[XPATH_MAXLEN];
+ bool unlock;
+};
+
struct mgmt_commit_cfg_req {
Mgmtd__DatastoreId src_ds_id;
struct mgmt_ds_ctx *src_ds_ctx;
@@ -110,6 +114,12 @@ struct mgmt_commit_cfg_req {
enum mgmt_commit_phase be_phase[MGMTD_BE_CLIENT_ID_MAX];
/*
+ * Additional information when the commit is triggered by native edit
+ * request.
+ */
+ struct mgmt_edit_req *edit;
+
+ /*
* Set of config changes to commit. This is used only
* when changes are NOT to be determined by comparing
* candidate and running DSs. This is typically used
@@ -407,7 +417,6 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
txn_req->req_id, txn->txn_id, txn->session_id);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
- case MGMTD_TXN_GETTREE_TIMEOUT:
break;
}
@@ -446,6 +455,8 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
cleanup = (ccreq->phase >= MGMTD_COMMIT_PHASE_TXN_CREATE &&
ccreq->phase < MGMTD_COMMIT_PHASE_TXN_DELETE);
+ XFREE(MTYPE_MGMTD_TXN_REQ, ccreq->edit);
+
FOREACH_MGMTD_BE_CLIENT_ID (id) {
/*
* Send TXN_DELETE to cleanup state for this
@@ -496,7 +507,6 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
- case MGMTD_TXN_GETTREE_TIMEOUT:
break;
}
@@ -607,7 +617,8 @@ static void mgmt_txn_process_set_cfg(struct event *thread)
->dst_ds_id,
txn_req->req.set_cfg
->dst_ds_ctx,
- false, false, true);
+ false, false, true,
+ NULL);
if (mm->perf_stats_en)
gettimeofday(&cmt_stats->last_start, NULL);
@@ -658,7 +669,8 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
* b/c right now that is special cased.. that special casing should be
* removed; however...
*/
- if (!txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
+ if (!txn->commit_cfg_req->req.commit_cfg.edit &&
+ !txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
!txn->commit_cfg_req->req.commit_cfg.rollback &&
mgmt_fe_send_commit_cfg_reply(txn->session_id, txn->txn_id,
txn->commit_cfg_req->req.commit_cfg
@@ -674,7 +686,8 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
txn->txn_id, txn->session_id);
}
- if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
+ if (!txn->commit_cfg_req->req.commit_cfg.edit &&
+ txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id &&
!txn->commit_cfg_req->req.commit_cfg.rollback &&
mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id,
txn->commit_cfg_req->req.commit_cfg
@@ -688,6 +701,21 @@ static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn,
txn->txn_id, txn->session_id);
}
+ if (txn->commit_cfg_req->req.commit_cfg.edit &&
+ mgmt_fe_adapter_send_edit_reply(txn->session_id, txn->txn_id,
+ txn->commit_cfg_req->req_id,
+ txn->commit_cfg_req->req.commit_cfg
+ .edit->unlock,
+ true,
+ txn->commit_cfg_req->req.commit_cfg
+ .edit->xpath_created,
+ success ? 0 : -1,
+ error_if_any) != 0) {
+ __log_err("Failed to send EDIT-REPLY txn-id: %" PRIu64
+ " session-id: %" PRIu64,
+ txn->txn_id, txn->session_id);
+ }
+
if (success) {
/* Stop the commit-timeout timer */
/* XXX why only on success? */
@@ -1488,7 +1516,6 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
case MGMTD_TXN_PROC_SETCFG:
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETTREE:
- case MGMTD_TXN_GETTREE_TIMEOUT:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
__log_err("Invalid Txn-Req-Event %u", txn_req->req_event);
break;
@@ -1862,11 +1889,6 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC,
&txn->comm_cfg_timeout);
break;
- case MGMTD_TXN_GETTREE_TIMEOUT:
- event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn,
- MGMTD_TXN_GET_TREE_MAX_DELAY_SEC,
- &txn->get_tree_timeout);
- break;
case MGMTD_TXN_PROC_GETTREE:
assert(!"code bug do not register this event");
break;
@@ -2020,7 +2042,7 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId dst_ds_id,
struct mgmt_ds_ctx *dst_ds_ctx,
bool validate_only, bool abort,
- bool implicit)
+ bool implicit, struct mgmt_edit_req *edit)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
@@ -2044,6 +2066,7 @@ int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
txn_req->req.commit_cfg.validate_only = validate_only;
txn_req->req.commit_cfg.abort = abort;
txn_req->req.commit_cfg.implicit = implicit;
+ txn_req->req.commit_cfg.edit = edit;
txn_req->req.commit_cfg.cmt_stats =
mgmt_fe_get_session_commit_stats(txn->session_id);
@@ -2427,6 +2450,52 @@ state:
return 0;
}
+int mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id,
+ Mgmtd__DatastoreId ds_id, struct mgmt_ds_ctx *ds_ctx,
+ Mgmtd__DatastoreId commit_ds_id,
+ struct mgmt_ds_ctx *commit_ds_ctx, bool unlock,
+ bool commit, LYD_FORMAT request_type, uint8_t flags,
+ uint8_t operation, const char *xpath, const char *data)
+{
+ struct mgmt_txn_ctx *txn;
+ struct mgmt_edit_req *edit;
+ struct nb_config *nb_config;
+ char errstr[BUFSIZ];
+ int ret;
+
+ txn = mgmt_txn_id2ctx(txn_id);
+ if (!txn)
+ return -1;
+
+ edit = XCALLOC(MTYPE_MGMTD_TXN_REQ, sizeof(struct mgmt_edit_req));
+
+ nb_config = mgmt_ds_get_nb_config(ds_ctx);
+ assert(nb_config);
+
+ ret = nb_candidate_edit_tree(nb_config, operation, request_type, xpath,
+ data, edit->xpath_created, errstr,
+ sizeof(errstr));
+ if (ret)
+ goto reply;
+
+ if (commit) {
+ edit->unlock = unlock;
+
+ mgmt_txn_send_commit_config_req(txn_id, req_id, ds_id, ds_ctx,
+ commit_ds_id, commit_ds_ctx,
+ false, false, true, edit);
+ return 0;
+ }
+reply:
+ mgmt_fe_adapter_send_edit_reply(txn->session_id, txn->txn_id, req_id,
+ unlock, commit, edit->xpath_created,
+ ret ? -1 : 0, errstr);
+
+ XFREE(MTYPE_MGMTD_TXN_REQ, edit);
+
+ return 0;
+}
+
/*
* Error reply from the backend client.
*/
@@ -2475,7 +2544,6 @@ int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETCFG:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
- case MGMTD_TXN_GETTREE_TIMEOUT:
default:
assert(!"non-native req event in native erorr path");
return -1;
diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h
index b7198326da..aeb74469f1 100644
--- a/mgmtd/mgmt_txn.h
+++ b/mgmtd/mgmt_txn.h
@@ -43,6 +43,7 @@
PREDECL_LIST(mgmt_txns);
struct mgmt_master;
+struct mgmt_edit_req;
enum mgmt_txn_type {
MGMTD_TXN_TYPE_NONE = 0,
@@ -171,16 +172,17 @@ extern int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id,
* implicit
* TRUE if the commit is implicit, FALSE otherwise.
*
+ * edit
+ * Additional info when triggered from native edit request.
+ *
* Returns:
* 0 on success, -1 on failures.
*/
-extern int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
- Mgmtd__DatastoreId src_ds_id,
- struct mgmt_ds_ctx *dst_ds_ctx,
- Mgmtd__DatastoreId dst_ds_id,
- struct mgmt_ds_ctx *src_ds_ctx,
- bool validate_only, bool abort,
- bool implicit);
+extern int mgmt_txn_send_commit_config_req(
+ uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
+ struct mgmt_ds_ctx *dst_ds_ctx, Mgmtd__DatastoreId dst_ds_id,
+ struct mgmt_ds_ctx *src_ds_ctx, bool validate_only, bool abort,
+ bool implicit, struct mgmt_edit_req *edit);
/*
* Send get-{cfg,data} request to be processed later in transaction.
@@ -219,6 +221,31 @@ extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
uint32_t wd_options, bool simple_xpath,
const char *xpath);
+/**
+ * Send edit request.
+ *
+ * Args:
+ * txn_id: Transaction identifier.
+ * req_id: FE client request identifier.
+ * ds_id: Datastore ID.
+ * ds_ctx: Datastore context.
+ * commit_ds_id: Commit datastore ID.
+ * commit_ds_ctx: Commit datastore context.
+ * unlock: Unlock datastores after the edit.
+ * commit: Commit the candidate datastore after the edit.
+ * request_type: LYD_FORMAT request type.
+ * flags: option flags for the request.
+ * operation: The operation to perform.
+ * xpath: The xpath of data node to edit.
+ * data: The data tree.
+ */
+extern int
+mgmt_txn_send_edit(uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId ds_id,
+ struct mgmt_ds_ctx *ds_ctx, Mgmtd__DatastoreId commit_ds_id,
+ struct mgmt_ds_ctx *commit_ds_ctx, bool unlock, bool commit,
+ LYD_FORMAT request_type, uint8_t flags, uint8_t operation,
+ const char *xpath, const char *data);
+
/*
* Notifiy backend adapter on connection.
*/
diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c
index 2cd24719bc..61d0760e05 100644
--- a/mgmtd/mgmt_vty.c
+++ b/mgmtd/mgmt_vty.c
@@ -238,6 +238,64 @@ DEFPY(mgmt_replace_config_data, mgmt_replace_config_data_cmd,
return CMD_SUCCESS;
}
+DEFPY(mgmt_edit, mgmt_edit_cmd,
+ "mgmt edit {create|delete|merge|replace|remove}$op XPATH [json|xml]$fmt [lock$lock] [commit$commit] [DATA]",
+ MGMTD_STR
+ "Edit configuration data\n"
+ "Create data\n"
+ "Delete data\n"
+ "Merge data\n"
+ "Replace data\n"
+ "Remove data\n"
+ "XPath expression specifying the YANG data path\n"
+ "JSON input format (default)\n"
+ "XML input format\n"
+ "Lock the datastores automatically\n"
+ "Commit the changes automatically\n"
+ "Data tree\n")
+{
+ LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON;
+ uint8_t operation;
+ uint8_t flags = 0;
+
+ switch (op[2]) {
+ case 'e':
+ operation = NB_OP_CREATE_EXCL;
+ break;
+ case 'l':
+ operation = NB_OP_DELETE;
+ break;
+ case 'r':
+ operation = NB_OP_MODIFY;
+ break;
+ case 'p':
+ operation = NB_OP_REPLACE;
+ break;
+ case 'm':
+ operation = NB_OP_DESTROY;
+ break;
+ default:
+ vty_out(vty, "Invalid operation!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!data && (operation == NB_OP_CREATE_EXCL ||
+ operation == NB_OP_MODIFY || operation == NB_OP_REPLACE)) {
+ vty_out(vty, "Data tree is missing!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (lock)
+ flags |= EDIT_FLAG_IMPLICIT_LOCK;
+
+ if (commit)
+ flags |= EDIT_FLAG_IMPLICIT_COMMIT;
+
+ vty_mgmt_send_edit_req(vty, MGMT_MSG_DATASTORE_CANDIDATE, format, flags,
+ operation, xpath, data);
+ return CMD_SUCCESS;
+}
+
DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
"show mgmt get-config [candidate|operational|running]$dsname WORD$path",
SHOW_STR MGMTD_STR
@@ -643,6 +701,7 @@ void mgmt_vty_init(void)
install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_remove_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_replace_config_data_cmd);
+ install_element(CONFIG_NODE, &mgmt_edit_cmd);
install_element(CONFIG_NODE, &mgmt_load_config_cmd);
install_element(CONFIG_NODE, &mgmt_save_config_cmd);
install_element(CONFIG_NODE, &mgmt_rollback_cmd);
diff --git a/nhrpd/debug.h b/nhrpd/debug.h
index f2c7022ad4..d5c00ada8e 100644
--- a/nhrpd/debug.h
+++ b/nhrpd/debug.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#include "log.h"
#define NHRP_DEBUG_COMMON (1 << 0)
diff --git a/nhrpd/os.h b/nhrpd/os.h
index 2b9e07fa6e..3f3b0de912 100644
--- a/nhrpd/os.h
+++ b/nhrpd/os.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
int os_socket(void);
int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr,
diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c
index 738e935ec2..c2f5d24b69 100644
--- a/nhrpd/reqid.c
+++ b/nhrpd/reqid.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#include "zebra.h"
#include "hash.h"
#include "nhrpd.h"
diff --git a/nhrpd/vici.h b/nhrpd/vici.h
index f2ad3a9fe3..8a8011f510 100644
--- a/nhrpd/vici.h
+++ b/nhrpd/vici.h
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
enum vici_type_t {
VICI_START = 0,
diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c
index be1042f260..229e0a4db8 100644
--- a/ospf6d/ospf6_gr_helper.c
+++ b/ospf6d/ospf6_gr_helper.c
@@ -938,15 +938,6 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6,
? "Enabled"
: "Disabled");
-#if CONFDATE > 20240401
- CPP_NOTICE("Remove deprecated json key: restartSupoort")
-#endif
- json_object_string_add(
- json, "restartSupoort",
- (ospf6->ospf6_helper_cfg.only_planned_restart)
- ? "Planned Restart only"
- : "Planned and Unplanned Restarts");
-
json_object_string_add(
json, "restartSupport",
(ospf6->ospf6_helper_cfg.only_planned_restart)
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index 0e8504c529..abd4320335 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -110,9 +110,10 @@ static void __attribute__((noreturn)) ospf6_exit(int status)
ospf6_master_delete();
+ keychain_terminate();
+
frr_fini();
- keychain_terminate();
exit(status);
}
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 610b5fc08e..9e26a2ac2d 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -480,7 +480,7 @@ static int ospf_ase_route_match_same(struct route_table *rt,
assert(or);
- if (or->path_type != newor->path_type)
+ if (or->changed || (or->path_type != newor->path_type))
return 0;
switch (or->path_type) {
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 95a593ad4d..e15871ac81 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -765,8 +765,9 @@ int ospf_flood_through_interface(struct ospf_interface *oi,
packets must be sent, as unicasts, to each adjacent neighbor
(i.e., those in state Exchange or greater). The destination
IP addresses for these packets are the neighbors' IP
- addresses. */
- if (oi->type == OSPF_IFTYPE_NBMA) {
+ addresses. This behavior is extended to P2MP networks which
+ don't support broadcast. */
+ if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 0969ae15bd..319db1efe2 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -147,17 +147,11 @@ void ospf_if_reset(struct interface *ifp)
}
}
-void ospf_if_reset_variables(struct ospf_interface *oi)
+static void ospf_if_default_variables(struct ospf_interface *oi)
{
/* Set default values. */
- /* don't clear this flag. oi->flag = OSPF_IF_DISABLE; */
- if (oi->vl_data)
- oi->type = OSPF_IFTYPE_VIRTUALLINK;
- else
- /* preserve network-type */
- if (oi->type != OSPF_IFTYPE_NBMA)
- oi->type = OSPF_IFTYPE_BROADCAST;
+ oi->type = OSPF_IFTYPE_BROADCAST;
oi->state = ISM_Down;
@@ -254,7 +248,7 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp,
oi->ls_ack_direct.ls_ack = list_new();
/* Set default values. */
- ospf_if_reset_variables(oi);
+ ospf_if_default_variables(oi);
/* Set pseudo neighbor to Null */
oi->nbr_self = NULL;
@@ -821,14 +815,41 @@ int ospf_if_up(struct ospf_interface *oi)
return 1;
}
-int ospf_if_down(struct ospf_interface *oi)
+/* This function will mark routes with next-hops matching the down
+ * OSPF interface as changed. It is used to assure routes that get
+ * removed from the zebra RIB when an interface goes down are
+ * reinstalled if the interface comes back up prior to an intervening
+ * SPF calculation.
+ */
+static void ospf_if_down_mark_routes_changed(struct route_table *table,
+ struct ospf_interface *oi)
{
- struct ospf *ospf;
struct route_node *rn;
struct ospf_route *or;
struct listnode *nh;
struct ospf_path *op;
+ for (rn = route_top(table); rn; rn = route_next(rn)) {
+ or = rn->info;
+
+ if (or == NULL)
+ continue;
+
+ for (nh = listhead(or->paths); nh;
+ nh = listnextnode_unchecked(nh)) {
+ op = listgetdata(nh);
+ if (op->ifindex == oi->ifp->ifindex) {
+ or->changed = true;
+ break;
+ }
+ }
+ }
+}
+
+int ospf_if_down(struct ospf_interface *oi)
+{
+ struct ospf *ospf;
+
if (oi == NULL)
return 0;
@@ -864,23 +885,11 @@ int ospf_if_down(struct ospf_interface *oi)
/* Shutdown packet reception and sending */
ospf_if_stream_unset(oi);
- if (!ospf->new_table)
- return 1;
- for (rn = route_top(ospf->new_table); rn; rn = route_next(rn)) {
- or = rn->info;
-
- if (!or)
- continue;
+ if (ospf->new_table)
+ ospf_if_down_mark_routes_changed(ospf->new_table, oi);
- for (nh = listhead(or->paths); nh;
- nh = listnextnode_unchecked(nh)) {
- op = listgetdata(nh);
- if (op->ifindex == oi->ifp->ifindex) {
- or->changed = true;
- break;
- }
- }
- }
+ if (ospf->new_external_route)
+ ospf_if_down_mark_routes_changed(ospf->new_external_route, oi);
return 1;
}
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index 08a2b11273..721ab1a9d7 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -119,6 +119,9 @@ struct ospf_if_params {
/* point-to-multipoint delayed reflooding configuration */
bool p2mp_delay_reflood;
+ /* point-to-multipoint doesn't support broadcast */
+ bool p2mp_non_broadcast;
+
/* Opaque LSA capability at interface level (see RFC5250) */
DECLARE_IF_PARAM(bool, opaque_capable);
};
@@ -185,6 +188,10 @@ struct ospf_interface {
/* OSPF Network Type. */
uint8_t type;
+#define OSPF_IF_NON_BROADCAST(O) \
+ (((O)->type == OSPF_IFTYPE_NBMA) || \
+ ((((O)->type == OSPF_IFTYPE_POINTOMULTIPOINT) && \
+ (O)->p2mp_non_broadcast)))
/* point-to-point DMVPN configuration */
uint8_t ptp_dmvpn;
@@ -192,6 +199,9 @@ struct ospf_interface {
/* point-to-multipoint delayed reflooding */
bool p2mp_delay_reflood;
+ /* point-to-multipoint doesn't support broadcast */
+ bool p2mp_non_broadcast;
+
/* State of Interface State Machine. */
uint8_t state;
@@ -326,7 +336,6 @@ extern void ospf_if_update_params(struct interface *ifp, struct in_addr addr);
extern int ospf_if_new_hook(struct interface *ifp);
extern void ospf_if_init(void);
extern void ospf_if_stream_unset(struct ospf_interface *oi);
-extern void ospf_if_reset_variables(struct ospf_interface *oi);
extern int ospf_if_is_enable(struct ospf_interface *oi);
extern int ospf_if_get_output_cost(struct ospf_interface *oi);
extern void ospf_if_recalculate_output_cost(struct interface *ifp);
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c
index 2516fa75db..878ab725bd 100644
--- a/ospfd/ospf_ism.c
+++ b/ospfd/ospf_ism.c
@@ -367,7 +367,7 @@ static int ism_interface_up(struct ospf_interface *oi)
/* Otherwise, the state transitions to Waiting. */
next_state = ISM_Waiting;
- if (oi->type == OSPF_IFTYPE_NBMA)
+ if (OSPF_IF_NON_BROADCAST(oi))
ospf_nbr_nbma_if_update(oi->ospf, oi);
/* ospf_ism_event (t); */
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index c238f051df..d47d581605 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -431,7 +431,7 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi,
memcpy(&nbr->address, p, sizeof(struct prefix));
nbr->nbr_nbma = NULL;
- if (oi->type == OSPF_IFTYPE_NBMA) {
+ if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_nbr_nbma *nbr_nbma;
struct listnode *node;
@@ -485,7 +485,7 @@ struct ospf_neighbor *ospf_nbr_get(struct ospf_interface *oi,
route_unlock_node(rn);
nbr = rn->info;
- if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt) {
+ if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->state == NSM_Attempt) {
nbr->src = iph->ip_src;
memcpy(&nbr->address, p, sizeof(struct prefix));
}
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index 08aa103686..c466ddcc6f 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -166,7 +166,7 @@ static int nsm_hello_received(struct ospf_neighbor *nbr)
OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
nbr->v_inactivity);
- if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma)
+ if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->nbr_nbma != NULL)
EVENT_OFF(nbr->nbr_nbma->t_poll);
/* Send proactive ARP requests */
@@ -377,7 +377,7 @@ static int nsm_kill_nbr(struct ospf_neighbor *nbr)
return 0;
}
- if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) {
+ if (OSPF_IF_NON_BROADCAST(nbr->oi) && nbr->nbr_nbma != NULL) {
struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma;
nbr_nbma->nbr = NULL;
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 4bf4ae9597..60479ddcd1 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -1486,12 +1486,8 @@ static void ospf_ls_req(struct ip *iph, struct ospf_header *ospfh,
/* Packet overflows MTU size, send immediately. */
if (length + ntohs(find->data->length) > ospf_packet_max(oi)) {
- if (oi->type == OSPF_IFTYPE_NBMA)
- ospf_ls_upd_send(nbr, ls_upd,
- OSPF_SEND_PACKET_DIRECT, 0);
- else
- ospf_ls_upd_send(nbr, ls_upd,
- OSPF_SEND_PACKET_INDIRECT, 0);
+ ospf_ls_upd_send(nbr, ls_upd,
+ OSPF_SEND_PACKET_DIRECT, 0);
/* Only remove list contents. Keep ls_upd. */
list_delete_all_node(ls_upd);
@@ -1508,12 +1504,7 @@ static void ospf_ls_req(struct ip *iph, struct ospf_header *ospfh,
/* Send rest of Link State Update. */
if (listcount(ls_upd) > 0) {
- if (oi->type == OSPF_IFTYPE_NBMA)
- ospf_ls_upd_send(nbr, ls_upd, OSPF_SEND_PACKET_DIRECT,
- 0);
- else
- ospf_ls_upd_send(nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT,
- 0);
+ ospf_ls_upd_send(nbr, ls_upd, OSPF_SEND_PACKET_DIRECT, 0);
list_delete(&ls_upd);
} else
@@ -3404,17 +3395,19 @@ static void ospf_poll_send(struct ospf_nbr_nbma *nbr_nbma)
if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE)
return;
- if (oi->type != OSPF_IFTYPE_NBMA)
- return;
-
if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down)
return;
- if (PRIORITY(oi) == 0)
- return;
+ if (oi->type == OSPF_IFTYPE_NBMA) {
+ if (PRIORITY(oi) == 0)
+ return;
+
+ if (nbr_nbma->priority == 0 && oi->state != ISM_DR &&
+ oi->state != ISM_Backup)
+ return;
- if (nbr_nbma->priority == 0 && oi->state != ISM_DR
- && oi->state != ISM_Backup)
+ } else if (oi->type != OSPF_IFTYPE_POINTOMULTIPOINT ||
+ !oi->p2mp_non_broadcast)
return;
ospf_hello_send_sub(oi, nbr_nbma->addr.s_addr);
@@ -3460,7 +3453,7 @@ void ospf_hello_send(struct ospf_interface *oi)
if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE)
return;
- if (oi->type == OSPF_IFTYPE_NBMA) {
+ if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
struct route_node *rn;
@@ -3476,31 +3469,44 @@ void ospf_hello_send(struct ospf_interface *oi)
continue;
/*
- * RFC 2328 Section 9.5.1
- * If the router is not eligible to become Designated
- * Router, it must periodically send Hello Packets to
- * both the Designated Router and the Backup
- * Designated Router (if they exist).
+ * Always send to all neighbors on Point-to-Multipoint
+ * non-braodcast networks.
*/
- if (PRIORITY(oi) == 0 &&
- IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) &&
- IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4))
- continue;
+ if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+ ospf_hello_send_sub(oi, nbr->address.u.prefix4
+ .s_addr);
+ else {
+ /*
+ * RFC 2328 Section 9.5.1
+ * If the router is not eligible to become Designated
+ * Router, it must periodically send Hello Packets to
+ * both the Designated Router and the Backup
+ * Designated Router (if they exist).
+ */
+ if (PRIORITY(oi) == 0 &&
+ IPV4_ADDR_CMP(&DR(oi),
+ &nbr->address.u.prefix4) &&
+ IPV4_ADDR_CMP(&BDR(oi),
+ &nbr->address.u.prefix4))
+ continue;
- /*
- * If the router is eligible to become Designated
- * Router, it must periodically send Hello Packets to
- * all neighbors that are also eligible. In addition,
- * if the router is itself the Designated Router or
- * Backup Designated Router, it must also send periodic
- * Hello Packets to all other neighbors.
- */
- if (nbr->priority == 0 && oi->state == ISM_DROther)
- continue;
+ /*
+ * If the router is eligible to become Designated
+ * Router, it must periodically send Hello Packets to
+ * all neighbors that are also eligible. In addition,
+ * if the router is itself the Designated Router or
+ * Backup Designated Router, it must also send periodic
+ * Hello Packets to all other neighbors.
+ */
+ if (nbr->priority == 0 &&
+ oi->state == ISM_DROther)
+ continue;
- /* if oi->state == Waiting, send
- * hello to all neighbors */
- ospf_hello_send_sub(oi, nbr->address.u.prefix4.s_addr);
+ /* if oi->state == Waiting, send
+ * hello to all neighbors */
+ ospf_hello_send_sub(oi, nbr->address.u.prefix4
+ .s_addr);
+ }
}
} else {
/* Decide destination address. */
@@ -3857,11 +3863,10 @@ void ospf_ls_upd_send(struct ospf_neighbor *nbr, struct list *update, int flag,
else
p.prefix.s_addr = htonl(OSPF_ALLDROUTERS);
- if (oi->type == OSPF_IFTYPE_NBMA) {
+ if (OSPF_IF_NON_BROADCAST(oi)) {
if (flag == OSPF_SEND_PACKET_INDIRECT)
- flog_warn(
- EC_OSPF_PACKET,
- "* LS-Update is directly sent on NBMA network.");
+ flog_warn(EC_OSPF_PACKET,
+ "* LS-Update is directly sent on non-broadcast network.");
if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix))
flog_warn(EC_OSPF_PACKET,
"* LS-Update is sent to myself.");
@@ -3919,7 +3924,8 @@ static void ospf_ls_ack_send_list(struct ospf_interface *oi, struct list *ack,
/* Decide destination address. */
if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
- oi->type == OSPF_IFTYPE_POINTOMULTIPOINT)
+ (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT &&
+ !oi->p2mp_non_broadcast))
op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS);
else
op->dst.s_addr = dst.s_addr;
@@ -3971,7 +3977,7 @@ void ospf_ls_ack_send_delayed(struct ospf_interface *oi)
networks, delayed Link State Acknowledgment packets must be
unicast separately over each adjacency (i.e., neighbor whose
state is >= Exchange). */
- if (oi->type == OSPF_IFTYPE_NBMA) {
+ if (OSPF_IF_NON_BROADCAST(oi)) {
struct ospf_neighbor *nbr;
struct route_node *rn;
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index e26fe6f53a..198309c1ef 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -2740,9 +2740,9 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
if (srn->algo[i] == SR_ALGORITHM_UNSET)
continue;
json_obj = json_object_new_object();
- char tmp[2];
+ char tmp[12];
- snprintf(tmp, sizeof(tmp), "%u", i);
+ snprintf(tmp, sizeof(tmp), "%d", i);
json_object_string_add(json_obj, tmp,
srn->algo[i] == SR_ALGORITHM_SPF
? "SPF"
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 93dd12ce49..4980cd3eca 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -2406,130 +2406,30 @@ DEFUN (no_ospf_timers_lsa_min_arrival,
return CMD_SUCCESS;
}
-DEFUN (ospf_neighbor,
- ospf_neighbor_cmd,
- "neighbor A.B.C.D [priority (0-255) [poll-interval (1-65535)]]",
- NEIGHBOR_STR
- "Neighbor IP address\n"
- "Neighbor Priority\n"
- "Priority\n"
- "Dead Neighbor Polling interval\n"
- "Seconds\n")
-{
- VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- int idx_ipv4 = 1;
- int idx_pri = 3;
- int idx_poll = 5;
- struct in_addr nbr_addr;
- unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT;
- unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT;
-
- if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
- vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (argc > 2)
- priority = strtoul(argv[idx_pri]->arg, NULL, 10);
-
- if (argc > 4)
- interval = strtoul(argv[idx_poll]->arg, NULL, 10);
-
- ospf_nbr_nbma_set(ospf, nbr_addr);
-
- if (argc > 2)
- ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority);
-
- if (argc > 4)
- ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (ospf_neighbor_poll_interval,
- ospf_neighbor_poll_interval_cmd,
- "neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]",
- NEIGHBOR_STR
- "Neighbor IP address\n"
- "Dead Neighbor Polling interval\n"
- "Seconds\n"
- "OSPF priority of non-broadcast neighbor\n"
- "Priority\n")
-{
- VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- int idx_ipv4 = 1;
- int idx_poll = 3;
- int idx_pri = 5;
- struct in_addr nbr_addr;
- unsigned int priority;
- unsigned int interval;
-
- if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
- vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- interval = strtoul(argv[idx_poll]->arg, NULL, 10);
-
- priority = argc > 4 ? strtoul(argv[idx_pri]->arg, NULL, 10)
- : OSPF_NEIGHBOR_PRIORITY_DEFAULT;
-
- ospf_nbr_nbma_set(ospf, nbr_addr);
- ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval);
-
- if (argc > 4)
- ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ospf_neighbor,
- no_ospf_neighbor_cmd,
- "no neighbor A.B.C.D [priority (0-255) [poll-interval (1-65525)]]",
- NO_STR
- NEIGHBOR_STR
- "Neighbor IP address\n"
- "Neighbor Priority\n"
- "Priority\n"
- "Dead Neighbor Polling interval\n"
- "Seconds\n")
+DEFPY(ospf_neighbor, ospf_neighbor_cmd,
+ "[no] neighbor A.B.C.D$nbr_address [{priority (0-255)$priority | poll-interval (1-65535)$interval}]",
+ NO_STR
+ NEIGHBOR_STR
+ "Neighbor IP address\n"
+ "Neighbor Priority\n"
+ "Priority\n"
+ "Dead Neighbor Polling interval\n"
+ "Seconds\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- int idx_ipv4 = 2;
- struct in_addr nbr_addr;
- if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
- vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- (void)ospf_nbr_nbma_unset(ospf, nbr_addr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ospf_neighbor_poll,
- no_ospf_neighbor_poll_cmd,
- "no neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]",
- NO_STR
- NEIGHBOR_STR
- "Neighbor IP address\n"
- "Dead Neighbor Polling interval\n"
- "Seconds\n"
- "Neighbor Priority\n"
- "Priority\n")
-{
- VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- int idx_ipv4 = 2;
- struct in_addr nbr_addr;
+ if (no)
+ ospf_nbr_nbma_unset(ospf, nbr_address);
+ else {
+ ospf_nbr_nbma_set(ospf, nbr_address);
+ if (priority_str)
+ ospf_nbr_nbma_priority_set(ospf, nbr_address, priority);
- if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) {
- vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n");
- return CMD_WARNING_CONFIG_FAILED;
+ if (interval_str)
+ ospf_nbr_nbma_poll_interval_set(ospf, nbr_address,
+ interval);
}
- (void)ospf_nbr_nbma_unset(ospf, nbr_addr);
-
return CMD_SUCCESS;
}
@@ -2680,7 +2580,7 @@ DEFUN (no_ospf_write_multiplier,
}
ALIAS(no_ospf_write_multiplier, no_write_multiplier_cmd,
- "no write-multiplier (1-100)", NO_STR
+ "no write-multiplier [(1-100)]", NO_STR
"Write multiplier\n"
"Maximum number of interface serviced per write\n")
@@ -2754,9 +2654,10 @@ DEFUN (ospf_max_multipath,
DEFUN (no_ospf_max_multipath,
no_ospf_max_multipath_cmd,
- "no maximum-paths",
+ "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM)"]",
NO_STR
- "Max no of multiple paths for ECMP support\n")
+ "Max no of multiple paths for ECMP support\n"
+ "Number of paths\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
uint16_t maxpaths = MULTIPATH_NUM;
@@ -4148,6 +4049,8 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
ospf_interface_auth_show(vty, oi, json_interface_sub, use_json);
ospf_interface_auth_show(vty, oi, json_oi, use_json);
+
+ /* Point-to-Multipoint Interface options. */
if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
if (use_json) {
json_object_boolean_add(json_interface_sub,
@@ -4162,6 +4065,19 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
" %sDelay reflooding LSAs received on P2MP interface\n",
oi->p2mp_delay_reflood ? "" : "Don't ");
}
+ if (use_json) {
+ json_object_boolean_add(json_interface_sub,
+ "p2mpNonBroadcast",
+ oi->p2mp_non_broadcast);
+
+ json_object_boolean_add(json_oi,
+ "p2mpNonBroadcast",
+ oi->p2mp_non_broadcast);
+ } else {
+ vty_out(vty,
+ " P2MP interface does %ssupport broadcast\n",
+ oi->p2mp_non_broadcast ? "not " : "");
+ }
}
/* Add ospf_interface object to main json blob using SIP as key
@@ -5970,7 +5886,7 @@ static int show_ip_ospf_neighbor_detail_all_common(struct vty *vty,
prev_nbr = nbr;
}
- if (oi->type != OSPF_IFTYPE_NBMA)
+ if (!OSPF_IF_NON_BROADCAST(oi))
continue;
struct listnode *nd;
@@ -8548,7 +8464,7 @@ DEFUN_HIDDEN (no_ospf_hello_interval,
DEFUN(ip_ospf_network, ip_ospf_network_cmd,
"ip ospf network <broadcast|"
"non-broadcast|"
- "point-to-multipoint [delay-reflood]|"
+ "point-to-multipoint [delay-reflood|non-broadcast]|"
"point-to-point [dmvpn]>",
"IP Information\n"
"OSPF interface commands\n"
@@ -8557,6 +8473,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
"Specify OSPF NBMA network\n"
"Specify OSPF point-to-multipoint network\n"
"Specify OSPF delayed reflooding of LSAs received on P2MP interface\n"
+ "Specify OSPF point-to-multipoint network doesn't support broadcast\n"
"Specify OSPF point-to-point network\n"
"Specify OSPF point-to-point DMVPN network\n")
{
@@ -8565,6 +8482,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
int old_type = IF_DEF_PARAMS(ifp)->type;
uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
uint8_t old_p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood;
+ uint8_t old_p2mp_non_broadcast = IF_DEF_PARAMS(ifp)->p2mp_non_broadcast;
struct route_node *rn;
if (old_type == OSPF_IFTYPE_LOOPBACK) {
@@ -8576,16 +8494,19 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood =
OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
+ IF_DEF_PARAMS(ifp)->p2mp_non_broadcast = OSPF_P2MP_NON_BROADCAST_DEFAULT;
if (argv_find(argv, argc, "broadcast", &idx))
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST;
- else if (argv_find(argv, argc, "non-broadcast", &idx))
- IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
else if (argv_find(argv, argc, "point-to-multipoint", &idx)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT;
if (argv_find(argv, argc, "delay-reflood", &idx))
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = true;
- } else if (argv_find(argv, argc, "point-to-point", &idx)) {
+ if (argv_find(argv, argc, "non-broadcast", &idx))
+ IF_DEF_PARAMS(ifp)->p2mp_non_broadcast = true;
+ } else if (argv_find(argv, argc, "non-broadcast", &idx))
+ IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA;
+ else if (argv_find(argv, argc, "point-to-point", &idx)) {
IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT;
if (argv_find(argv, argc, "dmvpn", &idx))
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1;
@@ -8593,7 +8514,8 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
if (IF_DEF_PARAMS(ifp)->type == old_type &&
IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn &&
- IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood)
+ IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood &&
+ IF_DEF_PARAMS(ifp)->p2mp_non_broadcast == old_p2mp_non_broadcast)
return CMD_SUCCESS;
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
@@ -8607,13 +8529,16 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd,
oi->type = IF_DEF_PARAMS(ifp)->type;
oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn;
oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood;
+ oi->p2mp_non_broadcast = IF_DEF_PARAMS(ifp)->p2mp_non_broadcast;
/*
* The OSPF interface only needs to be flapped if the network
* type or DMVPN parameter changes.
*/
if (IF_DEF_PARAMS(ifp)->type != old_type ||
- IF_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn) {
+ IF_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn ||
+ IF_DEF_PARAMS(ifp)->p2mp_non_broadcast !=
+ old_p2mp_non_broadcast) {
if (oi->state > ISM_Down) {
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown);
OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp);
@@ -8657,6 +8582,7 @@ DEFUN (no_ip_ospf_network,
IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0;
IF_DEF_PARAMS(ifp)->p2mp_delay_reflood =
OSPF_P2MP_DELAY_REFLOOD_DEFAULT;
+ IF_DEF_PARAMS(ifp)->p2mp_non_broadcast = OSPF_P2MP_NON_BROADCAST_DEFAULT;
if (IF_DEF_PARAMS(ifp)->type == old_type)
return CMD_SUCCESS;
@@ -10521,15 +10447,6 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf,
json_object_string_add(json_vrf, "strictLsaCheck",
(ospf->strict_lsa_check) ? "Enabled"
: "Disabled");
-#if CONFDATE > 20240401
- CPP_NOTICE("Remove deprecated json key: restartSupoort")
-#endif
- json_object_string_add(
- json_vrf, "restartSupoort",
- (ospf->only_planned_restart)
- ? "Planned Restart only"
- : "Planned and Unplanned Restarts");
-
json_object_string_add(
json_vrf, "restartSupport",
(ospf->only_planned_restart)
@@ -12225,6 +12142,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
OSPF_IFTYPE_POINTOMULTIPOINT &&
params->p2mp_delay_reflood)
vty_out(vty, " delay-reflood");
+ if (params->type ==
+ OSPF_IFTYPE_POINTOMULTIPOINT &&
+ params->p2mp_non_broadcast)
+ vty_out(vty, " non-broadcast");
if (params != IF_DEF_PARAMS(ifp) && rn)
vty_out(vty, " %pI4",
&rn->p.u.prefix4);
@@ -13753,11 +13674,8 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &ospf_auto_cost_reference_bandwidth_cmd);
install_element(OSPF_NODE, &no_ospf_auto_cost_reference_bandwidth_cmd);
- /* "neighbor" commands. */
+ /* "neighbor" command. */
install_element(OSPF_NODE, &ospf_neighbor_cmd);
- install_element(OSPF_NODE, &ospf_neighbor_poll_interval_cmd);
- install_element(OSPF_NODE, &no_ospf_neighbor_cmd);
- install_element(OSPF_NODE, &no_ospf_neighbor_poll_cmd);
/* write multiplier commands */
install_element(OSPF_NODE, &ospf_write_multiplier_cmd);
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 4c4666db52..1d013b260e 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -1096,6 +1096,7 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
oi->type = IF_DEF_PARAMS(co->ifp)->type;
oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn;
oi->p2mp_delay_reflood = IF_DEF_PARAMS(co->ifp)->p2mp_delay_reflood;
+ oi->p2mp_non_broadcast = IF_DEF_PARAMS(co->ifp)->p2mp_non_broadcast;
/* Add pseudo neighbor. */
ospf_nbr_self_reset(oi, oi->ospf->router_id);
@@ -1989,7 +1990,7 @@ static void ospf_nbr_nbma_add(struct ospf_nbr_nbma *nbr_nbma,
struct route_node *rn;
struct prefix p;
- if (oi->type != OSPF_IFTYPE_NBMA)
+ if (!OSPF_IF_NON_BROADCAST(oi))
return;
if (nbr_nbma->nbr != NULL)
@@ -2036,7 +2037,7 @@ void ospf_nbr_nbma_if_update(struct ospf *ospf, struct ospf_interface *oi)
struct route_node *rn;
struct prefix_ipv4 p;
- if (oi->type != OSPF_IFTYPE_NBMA)
+ if (!OSPF_IF_NON_BROADCAST(oi))
return;
for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn))
@@ -2095,7 +2096,7 @@ int ospf_nbr_nbma_set(struct ospf *ospf, struct in_addr nbr_addr)
rn->info = nbr_nbma;
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
- if (oi->type == OSPF_IFTYPE_NBMA)
+ if (OSPF_IF_NON_BROADCAST(oi))
if (prefix_match(oi->address, (struct prefix *)&p)) {
ospf_nbr_nbma_add(nbr_nbma, oi);
break;
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index be36a07687..15d3904c37 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1941,12 +1941,15 @@ DEFUN (show_ip_pim_mlag_summary,
json_object *json_stat = NULL;
json = json_object_new_object();
- if (router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)
- json_object_boolean_true_add(json, "mlagConnUp");
- if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)
- json_object_boolean_true_add(json, "mlagPeerConnUp");
- if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)
- json_object_boolean_true_add(json, "mlagPeerZebraUp");
+ json_object_boolean_add(json, "mlagConnUp",
+ CHECK_FLAG(router->mlag_flags,
+ PIM_MLAGF_LOCAL_CONN_UP));
+ json_object_boolean_add(json, "mlagPeerConnUp",
+ CHECK_FLAG(router->mlag_flags,
+ PIM_MLAGF_PEER_CONN_UP));
+ json_object_boolean_add(json, "mlagPeerZebraUp",
+ CHECK_FLAG(router->mlag_flags,
+ PIM_MLAGF_PEER_ZEBRA_UP));
json_object_string_add(json, "mlagRole",
mlag_role2str(router->mlag_role,
role_buf, sizeof(role_buf)));
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index 5d7132c09a..dcb6116012 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -1677,7 +1677,9 @@ static int pim_ifp_up(struct interface *ifp)
__func__, vrf->name);
return 0;
}
+
pim_zebra_interface_set_master(master, ifp);
+ break;
}
}
}
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 433aeacebb..f42079cd50 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -187,7 +187,7 @@ BuildRequires: make
BuildRequires: ncurses-devel
BuildRequires: readline-devel
BuildRequires: texinfo
-BuildRequires: libyang-devel >= 2.1.80
+BuildRequires: libyang-devel >= 2.1.128
%if 0%{?rhel} && 0%{?rhel} < 7
#python27-devel is available from ius community repo for RedHat/CentOS 6
BuildRequires: python27-devel
@@ -805,7 +805,21 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
%changelog
-* Tue Oct 10 2023 Donatas Abraitis <donatas@opensourcerouting.org> - %{version}
+* Mon Mar 25 2024 Jafar Al-Gharaibeh <jafar@atcorp.com> - %{version}
+
+* Mon Mar 25 2024 Jafar Al-Gharaibeh <jafar@atcorp.com> - 10.0
+- Major highlights:
+- Introduce local host routes
+- Require libyang 2.1.128
+- Add suport to configire a log file per daemon
+- BGP BMP Loc-RIB (RFC9069) support
+- eBGP-OAD (One Administrative Domain) support
+- BGP RPKI VRF support
+- BGP SNMP traps for BGP4-MIBV2
+- Management (mgmtd) daemon "replace" operation support
+- BGP dynamic capabilities for addpath, fqdn, orf capabilities
+- SRv6 encapsulation source address feature
+- OSPFv3 Point-To-Multipoint mode
* Mon Oct 09 2023 Donatas Abraitis <donatas@opensourcerouting.org> - 9.1
- Major highlights:
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index cef02c3972..d90236f7a2 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -302,7 +302,7 @@ parts:
- libpcre2-8-0
source: https://github.com/CESNET/libyang.git
source-type: git
- source-tag: v2.1.80
+ source-tag: v2.1.128
plugin: cmake
configflags:
- -DCMAKE_INSTALL_PREFIX:PATH=/usr
diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am
index 82314ccc04..94df3626d6 100644
--- a/tests/lib/subdir.am
+++ b/tests/lib/subdir.am
@@ -25,7 +25,7 @@ copy_script: tests/lib/script1.lua
$(INSTALL_SCRIPT) $< tests/lib/script1.lua
##############################################################################
-GRPC_TESTS_LDADD = mgmtd/libmgmt_be_nb.la staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
+GRPC_TESTS_LDADD = mgmtd/libmgmt_be_nb.la staticd/libstatic.a grpc/libfrrgrpc_pb.la $(GRPC_LIBS) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
if GRPC
check_PROGRAMS += tests/lib/test_grpc
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref
index b2e8de5ce1..eb4e51a074 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref
@@ -1,9 +1,9 @@
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
- Network Next Hop Metric LocPrf Weight Path
- *> 192.168.0.0 0.0.0.0 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> 192.168.0.0 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref
index 7bee704182..8fe3ea41a6 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref
@@ -1,9 +1,9 @@
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
- Network Next Hop Metric LocPrf Weight Path
- *> 192.168.0.0/24 0.0.0.0 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> 192.168.0.0/24 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref
index 31071e760d..67b907131c 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref
@@ -1,10 +1,10 @@
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
Default local pref 100, local AS 100
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
- Network Next Hop Metric LocPrf Weight Path
- *> 192.168.0.0/24 0.0.0.0 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> 192.168.0.0/24 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref
index 53c4793bf4..4f21a5711b 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref
@@ -1,7 +1,7 @@
BGP table version is 1, local router ID is 192.168.0.1
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
- Network Next Hop Metric LocPrf Weight Path
- *> 192.168.0.0 0.0.0.0 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> 192.168.0.0 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref
index fe3f0720d8..69e44e77ed 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref
@@ -1,9 +1,9 @@
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
- Network Next Hop Metric LocPrf Weight Path
- *> fc00::/64 :: 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref
index 363b4f5349..77aab38b0d 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref
@@ -1,7 +1,7 @@
BGP table version is 1, local router ID is 192.168.0.1
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete
- Network Next Hop Metric LocPrf Weight Path
- *> fc00::/64 :: 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref
index 8c3229b45d..a99400a6b0 100644
--- a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref
@@ -1,10 +1,10 @@
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
Default local pref 100, local AS 100
-Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+Status codes: s suppressed, d damped, h history, u unsorted, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
- Network Next Hop Metric LocPrf Weight Path
- *> fc00::/64 :: 0 32768 i
+ Network Next Hop Metric LocPrf Weight Path
+ *> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json
index 5cba71ef20..61be1df92a 100644
--- a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json
@@ -4,51 +4,6 @@
"localAS": 101,
"routes":
{
- "2001:db8:6::/64": [
- {
- "stale": true,
- "valid": true,
- "bestpath": true,
- "pathFrom": "external",
- "prefix": "2001:db8:6::",
- "prefixLen": 64,
- "network": "2001:db8:6::\/64",
- "metric": 0,
- "weight": 0,
- "peerId": "2001:db8:4::1",
- "origin": "IGP",
- "nexthops": [
- { "ip": "2001:db8:4::1",
- "afi": "ipv6",
- "scope": "global",
- "used": true
- }
- ]
- }
- ],
- "2001:db8:7::/64": [
- {
- "stale": true,
- "valid": true,
- "bestpath": true,
- "pathFrom": "external",
- "prefix": "2001:db8:7::",
- "prefixLen": 64, "network":
- "2001:db8:7::\/64",
- "metric": 0,
- "weight": 0,
- "peerId": "2001:db8:4::1",
- "origin": "IGP",
- "nexthops": [
- {
- "ip": "2001:db8:4::1",
- "afi": "ipv6",
- "scope": "global",
- "used": true
- }
- ]
- }
- ],
"2001:db8:8::/64": [
{
"valid": true,
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json
index 8eea183285..36cdcc307d 100644
--- a/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json
@@ -32,49 +32,5 @@
}
]
}
- ],
- "2001:db8:6::/64": [{
- "distance": 20,
- "protocol": "bgp",
- "metric": 0,
- "selected": true,
- "destSelected": true,
- "prefix": "2001:db8:6::/64",
- "nexthops": [{
- "ip":"2001:db8:4::1",
- "active": true,
- "afi": "ipv6",
- "recursive":true
- },
- {
- "fib":true,
- "ip":"2001:db8:1::2",
- "afi": "ipv6",
- "interfaceName": "r1-eth0"
- }
- ]
- }
- ],
- "2001:db8:7::/64": [{
- "distance": 20,
- "protocol": "bgp",
- "metric": 0,
- "selected": true,
- "destSelected": true,
- "prefix": "2001:db8:7::/64",
- "nexthops": [{
- "ip":"2001:db8:4::1",
- "active": true,
- "afi": "ipv6",
- "recursive": true
- },
- {
- "fib":true,
- "ip":"2001:db8:1::2",
- "afi": "ipv6",
- "interfaceName":"r1-eth0"
- }
- ]
- }
]
}
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
index 906687d1cd..705937ef93 100644
--- a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
+++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
@@ -122,8 +122,8 @@ def test_bfd_connection():
def test_bfd_loss_intermediate():
"""
- Assert that BFD notices the bfd link down failure.
- but BGP entries should still be present
+ Assert that BGP notices the BFD link down failure.
+ The BGP entries should be flushed as the C-bit is set in both directions.
"""
tgen = get_topogen()
if tgen.routers_have_failure():
@@ -160,7 +160,7 @@ def test_bfd_loss_intermediate():
assertmsg = '"{}" JSON output mismatches'.format(router.name)
assert result is None, assertmsg
- logger.info("waiting for BGP entries to become stale")
+ logger.info("waiting for BGP entries to be removed")
for router in tgen.routers().values():
if router.name == "r2":
continue
diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.py b/tests/topotests/bfd_topo3/test_bfd_topo3.py
index f767b0e7b9..d7b2542f9f 100644
--- a/tests/topotests/bfd_topo3/test_bfd_topo3.py
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py
@@ -189,7 +189,7 @@ def test_wait_bfd_convergence():
"show bfd peers json",
bfd_config,
)
- _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=1)
assertmsg = '"{}" BFD configuration failure'.format(router)
assert result is None, assertmsg
diff --git a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json
index e3703bf953..39ed61f718 100644
--- a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json
+++ b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json
@@ -1,7 +1,6 @@
{
"vrfId": 0,
"vrfName": "default",
- "tableVersion": 3,
"routerId": "192.168.255.1",
"defaultLocPrf": 100,
"localAS": "1.1",
diff --git a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json
index 1af4ff7e3d..30133175e2 100644
--- a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json
+++ b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json
@@ -1,7 +1,6 @@
{
"vrfId": 0,
"vrfName": "default",
- "tableVersion": 3,
"routerId": "192.168.255.2",
"defaultLocPrf": 100,
"localAS": 65538,
diff --git a/tests/topotests/bgp_dynamic_capability/r1/frr.conf b/tests/topotests/bgp_dynamic_capability/r1/frr.conf
index aa5c3db9a6..c9594626f5 100644
--- a/tests/topotests/bgp_dynamic_capability/r1/frr.conf
+++ b/tests/topotests/bgp_dynamic_capability/r1/frr.conf
@@ -15,6 +15,7 @@ router bgp 65001
!
address-family ipv4 unicast
neighbor 192.168.1.2 addpath-tx-all-paths
+ neighbor 192.168.1.2 addpath-rx-paths-limit 10
exit-address-family
!
ip prefix-list r2 seq 5 permit 10.10.10.10/32
diff --git a/tests/topotests/bgp_dynamic_capability/r2/frr.conf b/tests/topotests/bgp_dynamic_capability/r2/frr.conf
index 7f25665ed5..3cc1f1fc39 100644
--- a/tests/topotests/bgp_dynamic_capability/r2/frr.conf
+++ b/tests/topotests/bgp_dynamic_capability/r2/frr.conf
@@ -16,6 +16,7 @@ router bgp 65002
neighbor 192.168.1.1 timers 1 3
neighbor 192.168.1.1 timers connect 1
neighbor 192.168.1.1 capability dynamic
+ neighbor 192.168.1.1 addpath-rx-paths-limit 20
!
address-family ipv4 unicast
redistribute connected
diff --git a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py
index 5202f51e28..7511d57e3e 100644
--- a/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py
+++ b/tests/topotests/bgp_dynamic_capability/test_bgp_dynamic_capability_addpath.py
@@ -6,7 +6,11 @@
#
"""
-Test if Addpath capability is adjusted dynamically.
+Test if Addpath/Paths-Limit capabilities are adjusted dynamically.
+T1: Enable Addpath/Paths-Limit capabilities and check if they are exchanged dynamically
+T2: Disable paths limit and check if it's exchanged dynamically
+T3: Disable Addpath capability RX and check if it's exchanged dynamically
+T4: Disable Addpath capability and check if it's exchanged dynamically
"""
import os
@@ -24,7 +28,6 @@ sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
-from lib.common_config import step
pytestmark = [pytest.mark.bgpd]
@@ -47,7 +50,7 @@ def teardown_module(mod):
tgen.stop_topology()
-def test_bgp_dynamic_capability_addpath():
+def test_bgp_addpath_paths_limit():
tgen = get_topogen()
if tgen.routers_have_failure():
@@ -56,7 +59,7 @@ def test_bgp_dynamic_capability_addpath():
r1 = tgen.gears["r1"]
r2 = tgen.gears["r2"]
- def _bgp_converge():
+ def _converge():
output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
expected = {
"192.168.1.2": {
@@ -65,8 +68,19 @@ def test_bgp_dynamic_capability_addpath():
"dynamic": "advertisedAndReceived",
"addPath": {
"ipv4Unicast": {
+ "txAdvertisedAndReceived": False,
"txAdvertised": True,
+ "txReceived": False,
"rxAdvertisedAndReceived": True,
+ "rxAdvertised": True,
+ "rxReceived": True,
+ }
+ },
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 10,
+ "receivedPathsLimit": 20,
}
},
},
@@ -80,26 +94,26 @@ def test_bgp_dynamic_capability_addpath():
return topotest.json_cmp(output, expected)
test_func = functools.partial(
- _bgp_converge,
+ _converge,
)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
assert result is None, "Can't converge"
- step("Enable Addpath capability and check if it's exchanged dynamically")
-
- # Clear message stats to check if we receive a notification or not after we
- # change the settings fo LLGR.
+ ####
+ # T1: Enable Addpath/Paths-Limit capabilities and check if they are exchanged dynamically
+ ####
r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
r2.vtysh_cmd(
"""
configure terminal
- router bgp
- address-family ipv4 unicast
- neighbor 192.168.1.1 addpath-tx-all-paths
+ router bgp
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 addpath-tx-all-paths
+ neighbor 192.168.1.1 addpath-rx-paths-limit 21
"""
)
- def _bgp_check_if_addpath_rx_tx_and_session_not_reset():
+ def _enable_addpath_paths_limit():
output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
expected = {
"192.168.1.2": {
@@ -109,7 +123,18 @@ def test_bgp_dynamic_capability_addpath():
"addPath": {
"ipv4Unicast": {
"txAdvertisedAndReceived": True,
+ "txAdvertised": True,
+ "txReceived": True,
"rxAdvertisedAndReceived": True,
+ "rxAdvertised": True,
+ "rxReceived": True,
+ }
+ },
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 10,
+ "receivedPathsLimit": 21,
}
},
},
@@ -120,23 +145,76 @@ def test_bgp_dynamic_capability_addpath():
},
"messageStats": {
"notificationsRecv": 0,
- "capabilityRecv": 1,
+ "notificationsSent": 0,
+ "capabilityRecv": 2,
},
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(
- _bgp_check_if_addpath_rx_tx_and_session_not_reset,
+ _enable_addpath_paths_limit,
)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
- assert result is None, "Session was reset after enabling Addpath capability"
+ assert (
+ result is None
+ ), "Something went wrong when enabling Addpath/Paths-Limit capabilities"
- step("Disable Addpath capability RX and check if it's exchanged dynamically")
+ ###
+ # T2: Disable paths limit and check if it's exchanged dynamically
+ ###
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ address-family ipv4 unicast
+ no neighbor 192.168.1.1 addpath-rx-paths-limit
+ """
+ )
- # Clear message stats to check if we receive a notification or not after we
- # disable addpath-rx.
- r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ def _disable_paths_limit():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "dynamic": "advertisedAndReceived",
+ "addPath": {
+ "ipv4Unicast": {
+ "txAdvertisedAndReceived": True,
+ "txAdvertised": True,
+ "txReceived": True,
+ "rxAdvertisedAndReceived": True,
+ "rxAdvertised": True,
+ "rxReceived": True,
+ }
+ },
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 10,
+ "receivedPathsLimit": 0,
+ }
+ },
+ },
+ "messageStats": {
+ "notificationsRecv": 0,
+ "notificationsSent": 0,
+ "capabilityRecv": 3,
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _disable_paths_limit,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Something went wrong after disabling paths limit"
+
+ ###
+ # T3: Disable Addpath capability RX and check if it's exchanged dynamically
+ ###
r2.vtysh_cmd(
"""
configure terminal
@@ -146,7 +224,7 @@ def test_bgp_dynamic_capability_addpath():
"""
)
- def _bgp_check_if_addpath_tx_and_session_not_reset():
+ def _disable_addpath_rx():
output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
expected = {
"192.168.1.2": {
@@ -156,27 +234,39 @@ def test_bgp_dynamic_capability_addpath():
"addPath": {
"ipv4Unicast": {
"txAdvertisedAndReceived": True,
+ "txAdvertised": True,
+ "txReceived": True,
+ "rxAdvertisedAndReceived": False,
"rxAdvertised": True,
+ "rxReceived": False,
+ }
+ },
+ "pathsLimit": {
+ "ipv4Unicast": {
+ "advertisedAndReceived": True,
+ "advertisedPathsLimit": 10,
+ "receivedPathsLimit": 0,
}
},
},
"messageStats": {
"notificationsRecv": 0,
- "capabilityRecv": 1,
+ "notificationsSent": 0,
+ "capabilityRecv": 4,
},
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(
- _bgp_check_if_addpath_tx_and_session_not_reset,
+ _disable_addpath_rx,
)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
- assert result is None, "Session was reset after disabling Addpath RX flags"
+ assert result is None, "Something went wrong after disabling Addpath RX flags"
- # Clear message stats to check if we receive a notification or not after we
- # disable Addpath capability.
- r1.vtysh_cmd("clear bgp 192.168.1.2 message-stats")
+ ###
+ # T4: Disable Addpath capability and check if it's exchanged dynamically
+ ###
r1.vtysh_cmd(
"""
configure terminal
@@ -186,7 +276,7 @@ def test_bgp_dynamic_capability_addpath():
"""
)
- def _bgp_check_if_addpath_capability_is_absent():
+ def _disable_addpath():
output = json.loads(r1.vtysh_cmd("show bgp neighbor json"))
expected = {
"192.168.1.2": {
@@ -195,24 +285,30 @@ def test_bgp_dynamic_capability_addpath():
"dynamic": "advertisedAndReceived",
"addPath": {
"ipv4Unicast": {
- "txAdvertisedAndReceived": None,
- "txAdvertised": None,
+ "txAdvertisedAndReceived": False,
+ "txAdvertised": False,
+ "txReceived": True,
+ "rxAdvertisedAndReceived": False,
"rxAdvertised": True,
+ "rxReceived": False,
}
},
},
"messageStats": {
"notificationsRecv": 0,
+ "notificationsSent": 0,
+ "capabilitySent": 1,
+ "capabilityRecv": 4,
},
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(
- _bgp_check_if_addpath_capability_is_absent,
+ _disable_addpath,
)
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
- assert result is None, "Failed to disable Addpath capability"
+ assert result is None, "Something went wrong when disabling Addpath capability"
if __name__ == "__main__":
diff --git a/eigrpd/eigrp_pkt_tlv1.c b/tests/topotests/bgp_evpn_maximum_prefix/__init__.py
index e69de29bb2..e69de29bb2 100644
--- a/eigrpd/eigrp_pkt_tlv1.c
+++ b/tests/topotests/bgp_evpn_maximum_prefix/__init__.py
diff --git a/tests/topotests/bgp_evpn_maximum_prefix/c1/frr.conf b/tests/topotests/bgp_evpn_maximum_prefix/c1/frr.conf
new file mode 100644
index 0000000000..7476a3723d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_maximum_prefix/c1/frr.conf
@@ -0,0 +1,4 @@
+!
+int c1-eth0
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bgp_evpn_maximum_prefix/c2/frr.conf b/tests/topotests/bgp_evpn_maximum_prefix/c2/frr.conf
new file mode 100644
index 0000000000..a203daae05
--- /dev/null
+++ b/tests/topotests/bgp_evpn_maximum_prefix/c2/frr.conf
@@ -0,0 +1,4 @@
+!
+int c2-eth0
+ ip address 192.168.0.2/24
+!
diff --git a/tests/topotests/bgp_evpn_maximum_prefix/r1/frr.conf b/tests/topotests/bgp_evpn_maximum_prefix/r1/frr.conf
new file mode 100644
index 0000000000..0534518cfb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_maximum_prefix/r1/frr.conf
@@ -0,0 +1,30 @@
+!
+!debug bgp neighbor
+!debug route-map detail
+!
+vni 10
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r1-eth1
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ network 10.10.10.10/32
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor 192.168.1.2 activate
+ advertise-all-vni
+ advertise ipv4 unicast
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_evpn_maximum_prefix/r2/frr.conf b/tests/topotests/bgp_evpn_maximum_prefix/r2/frr.conf
new file mode 100644
index 0000000000..353302b9e7
--- /dev/null
+++ b/tests/topotests/bgp_evpn_maximum_prefix/r2/frr.conf
@@ -0,0 +1,25 @@
+!
+!debug bgp neighbor
+!
+int lo
+ ip address 10.10.10.2/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor 192.168.1.1 activate
+ neighbor 192.168.1.1 maximum-prefix 2
+ advertise-all-vni
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_evpn_maximum_prefix/test_bgp_evpn_maximum_prefix.py b/tests/topotests/bgp_evpn_maximum_prefix/test_bgp_evpn_maximum_prefix.py
new file mode 100644
index 0000000000..5469eff144
--- /dev/null
+++ b/tests/topotests/bgp_evpn_maximum_prefix/test_bgp_evpn_maximum_prefix.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2024 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+import os
+import re
+import sys
+import json
+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
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("c1", "r1"), "s2": ("r1", "r2"), "s3": ("r2", "c2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ tgen.net["r1"].cmd(
+ """
+ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.1 nolearning
+ip link add name br10 type bridge
+ip link set dev vxlan10 master br10
+ip link set dev r1-eth0 master br10
+ip link set up dev br10
+ip link set up dev vxlan10"""
+ )
+
+ tgen.net["r2"].cmd(
+ """
+ip link add vxlan10 type vxlan id 10 dstport 4789 local 10.10.10.2 nolearning
+ip link add name br10 type bridge
+ip link set dev vxlan10 master br10
+ip link set dev r2-eth1 master br10
+ip link set up dev br10
+ip link set up dev vxlan10"""
+ )
+
+ router_list = tgen.routers()
+
+ for _, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_evpn_maximum_prefix():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show bgp l2vpn evpn summary failed json"))
+ expected = {
+ "peers": {
+ "192.168.1.1": {
+ "lastNotificationReason": "Cease/Maximum Number of Prefixes Reached",
+ "lastResetDueTo": "BGP Notification send",
+ }
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(
+ _bgp_converge,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Can't limit maximum-prefixes for EVPN routes"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf
index 33b6d08aba..d246517898 100644
--- a/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/leaf1/evpn.conf
@@ -1,6 +1,7 @@
frr defaults datacenter
!
router bgp 65101
+ timers bgp 3 10
bgp router-id 192.168.100.13
no bgp ebgp-requires-policy
neighbor 192.168.50.1 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf
index 428998b0fe..6855a436d4 100644
--- a/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/leaf2/evpn.conf
@@ -1,6 +1,7 @@
frr defaults datacenter
!
router bgp 65101
+ timers bgp 3 10
bgp router-id 192.168.100.14
no bgp ebgp-requires-policy
neighbor 192.168.61.1 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/spine1/evpn.conf b/tests/topotests/bgp_evpn_mh/spine1/evpn.conf
index b9fce46ea4..7d6fef699d 100644
--- a/tests/topotests/bgp_evpn_mh/spine1/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/spine1/evpn.conf
@@ -1,6 +1,7 @@
frr defaults datacenter
!
router bgp 65001
+ timers bgp 3 10
bgp router-id 192.168.100.13
no bgp ebgp-requires-policy
neighbor 192.168.50.2 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/spine2/evpn.conf b/tests/topotests/bgp_evpn_mh/spine2/evpn.conf
index 1430e10b68..c651ada686 100644
--- a/tests/topotests/bgp_evpn_mh/spine2/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/spine2/evpn.conf
@@ -1,6 +1,7 @@
frr defaults datacenter
!
router bgp 65001
+ timers bgp 3 10
bgp router-id 192.168.100.14
no bgp ebgp-requires-policy
neighbor 192.168.60.2 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/torm11/evpn.conf b/tests/topotests/bgp_evpn_mh/torm11/evpn.conf
index 2c1c695a18..62b7ec5855 100644
--- a/tests/topotests/bgp_evpn_mh/torm11/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/torm11/evpn.conf
@@ -7,6 +7,7 @@ frr defaults datacenter
!
!
router bgp 65002
+ timers bgp 3 10
bgp router-id 192.168.100.15
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/torm12/evpn.conf b/tests/topotests/bgp_evpn_mh/torm12/evpn.conf
index 8b0ce1d98f..3ceb974c47 100644
--- a/tests/topotests/bgp_evpn_mh/torm12/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/torm12/evpn.conf
@@ -7,6 +7,7 @@ frr defaults datacenter
!
!
router bgp 65003
+ timers bgp 3 10
bgp router-id 192.168.100.16
no bgp ebgp-requires-policy
neighbor 192.168.2.1 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/torm21/evpn.conf b/tests/topotests/bgp_evpn_mh/torm21/evpn.conf
index 5247dc1ebd..ecaf85ddb7 100644
--- a/tests/topotests/bgp_evpn_mh/torm21/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/torm21/evpn.conf
@@ -7,6 +7,7 @@ frr defaults datacenter
!
!
router bgp 65004
+ timers bgp 3 10
bgp router-id 192.168.100.17
no bgp ebgp-requires-policy
neighbor 192.168.3.1 remote-as external
diff --git a/tests/topotests/bgp_evpn_mh/torm22/evpn.conf b/tests/topotests/bgp_evpn_mh/torm22/evpn.conf
index ec56360176..c7e152498c 100644
--- a/tests/topotests/bgp_evpn_mh/torm22/evpn.conf
+++ b/tests/topotests/bgp_evpn_mh/torm22/evpn.conf
@@ -6,6 +6,7 @@ frr defaults datacenter
! debug bgp zebra
!
router bgp 65005
+ timers bgp 3 10
bgp router-id 192.168.100.18
no bgp ebgp-requires-policy
neighbor 192.168.4.1 remote-as external
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py
index 0deb181f3e..489c59fa40 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py
@@ -1,17 +1,17 @@
from lib.lutil import luCommand
luCommand(
- "r1", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+ "r1", 'vtysh -c "show bgp nexthop"', "99.0.0.. valid", "wait", "See CE static NH"
)
luCommand(
- "r3", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+ "r3", 'vtysh -c "show bgp nexthop"', "99.0.0.. valid", "wait", "See CE static NH"
)
luCommand(
- "r4", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+ "r4", 'vtysh -c "show bgp nexthop"', "99.0.0.. valid", "wait", "See CE static NH"
)
-luCommand("r1", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
-luCommand("r3", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
-luCommand("r4", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
+luCommand("r1", 'vtysh -c "show bgp ipv4 uni"', "i 5.*i 5", "wait", "See CE routes")
+luCommand("r3", 'vtysh -c "show bgp ipv4 uni"', "i 5.*i 5", "wait", "See CE routes")
+luCommand("r4", 'vtysh -c "show bgp ipv4 uni"', "i 5.*i 5", "wait", "See CE routes")
luCommand("ce1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
luCommand("r1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
luCommand("ce2", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
@@ -39,22 +39,22 @@ luCommand(
luCommand(
"r3",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.1/32",
+ "i 99.0.0.1/32",
"wait",
"See R1s static address",
)
luCommand(
"r4",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.1/32",
+ "i 99.0.0.1/32",
"wait",
"See R1s static address",
)
luCommand(
- "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports"
+ "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i 5.*i 5", "wait", "See R1s imports"
)
luCommand(
- "r4", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports"
+ "r4", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i 5.*i 5", "wait", "See R1s imports"
)
luCommand(
@@ -86,14 +86,14 @@ if have2ndImports:
luCommand(
"r1",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.2/32",
+ "i 99.0.0.2/32",
"wait",
"See R3s static address",
)
luCommand(
"r4",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.2/32",
+ "i 99.0.0.2/32",
"wait",
"See R3s static address",
)
@@ -101,14 +101,14 @@ if have2ndImports:
luCommand(
"r1",
'vtysh -c "show bgp ipv4 vpn rd 10:3"',
- "i5.*i5",
+ "i 5.*i 5",
"none",
"See R3s imports",
)
luCommand(
"r4",
'vtysh -c "show bgp ipv4 vpn rd 10:3"',
- "i5.*i5",
+ "i 5.*i 5",
"none",
"See R3s imports",
)
@@ -133,22 +133,22 @@ luCommand(
luCommand(
"r1",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.3/32",
+ "i 99.0.0.3/32",
"wait",
"See R4s static address",
)
luCommand(
"r3",
'vtysh -c "show bgp ipv4 vpn"',
- "i99.0.0.3/32",
+ "i 99.0.0.3/32",
"wait",
"See R4s static address",
)
luCommand(
- "r1", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports"
+ "r1", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i 5.*i 5", "wait", "See R4s imports"
)
luCommand(
- "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports"
+ "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i 5.*i 5", "wait", "See R4s imports"
)
diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
index af6976b5a6..fd67b2ecdd 100644
--- a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
+++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
@@ -53,7 +53,39 @@ anycast IP (VIP) addresses via BGP.
def build_topo(tgen):
- "Build function"
+ """
+ Build function
+
+ +------+
+ | |
+ /| r7 |---
+ / | 65351|
+ / +------+
+ /
+ +------+ / +------+
+ | |/ | |
+ /| r4 | | r8 |---
+ / | 65301|------| 65352|
+ / +------+ +------+
+ /
+ +------+ / +------+ +------+
+ | |/ | | | |
+ | r2 | | r5 | | r9 |---
+ | 65201|------| 65302|------| 65353|
+ +------+ +------+ +------+
+ |
+ +------+ |
+ | |----------
+ | r1 |
+ | 65101|----------
+ +------+ |
+ |
+ +------+ +------+ +------+
+ | | | | | |
+ | r3 |------| r6 |------| r10 |---
+ | 65202| | 65303| | 65354|
+ +------+ +------+ +------+
+ """
# Create 10 routers - 1 super-spine, 2 spines, 3 leafs
# and 4 servers
diff --git a/tests/topotests/bgp_peer_group/r1/bgpd.conf b/tests/topotests/bgp_peer_group/r1/bgpd.conf
index 19b490a359..68d8e61a59 100644
--- a/tests/topotests/bgp_peer_group/r1/bgpd.conf
+++ b/tests/topotests/bgp_peer_group/r1/bgpd.conf
@@ -5,4 +5,8 @@ router bgp 65001
neighbor PG timers 3 10
neighbor 192.168.255.3 peer-group PG
neighbor r1-eth0 interface peer-group PG
+ neighbor PG1 peer-group
+ neighbor PG1 remote-as external
+ neighbor PG1 timers 3 20
+ neighbor 192.168.251.2 peer-group PG1
!
diff --git a/tests/topotests/bgp_peer_group/r1/zebra.conf b/tests/topotests/bgp_peer_group/r1/zebra.conf
index e2c399e536..16fd8c538c 100644
--- a/tests/topotests/bgp_peer_group/r1/zebra.conf
+++ b/tests/topotests/bgp_peer_group/r1/zebra.conf
@@ -2,5 +2,8 @@
interface r1-eth0
ip address 192.168.255.1/24
!
+interface r1-eth1
+ ip address 192.168.251.1/30
+!
ip forwarding
!
diff --git a/tests/topotests/bgp_peer_group/r2/bgpd.conf b/tests/topotests/bgp_peer_group/r2/bgpd.conf
index 0880ee9fae..d0e8f017d1 100644
--- a/tests/topotests/bgp_peer_group/r2/bgpd.conf
+++ b/tests/topotests/bgp_peer_group/r2/bgpd.conf
@@ -4,4 +4,8 @@ router bgp 65002
neighbor PG remote-as external
neighbor PG timers 3 10
neighbor r2-eth0 interface peer-group PG
+ neighbor PG1 peer-group
+ neighbor PG1 remote-as external
+ neighbor PG1 timers 3 20
+ neighbor 192.168.251.1 peer-group PG1
!
diff --git a/tests/topotests/bgp_peer_group/r2/zebra.conf b/tests/topotests/bgp_peer_group/r2/zebra.conf
index 606c17bec9..c2ad956c9c 100644
--- a/tests/topotests/bgp_peer_group/r2/zebra.conf
+++ b/tests/topotests/bgp_peer_group/r2/zebra.conf
@@ -2,5 +2,8 @@
interface r2-eth0
ip address 192.168.255.2/24
!
+interface r2-eth1
+ ip address 192.168.251.2/30
+!
ip forwarding
!
diff --git a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
index a91fade049..5cbcd19be9 100644
--- a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
+++ b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
@@ -22,7 +22,7 @@ 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
pytestmark = [pytest.mark.bgpd]
@@ -36,6 +36,10 @@ def build_topo(tgen):
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
@@ -70,6 +74,7 @@ def test_bgp_peer_group():
expected = {
"r1-eth0": {"peerGroup": "PG", "bgpState": "Established"},
"192.168.255.3": {"peerGroup": "PG", "bgpState": "Established"},
+ "192.168.251.2": {"peerGroup": "PG1", "bgpState": "Established"},
}
return topotest.json_cmp(output, expected)
@@ -96,6 +101,48 @@ def test_bgp_peer_group():
assert result is None, "Failed checking advertised routes from r3"
+def test_bgp_peer_group_remote_as_del_readd():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ logger.info("Remove bgp peer-group PG1 remote-as neighbor should be retained")
+ r1.cmd(
+ 'vtysh -c "config t" -c "router bgp 65001" '
+ + ' -c "no neighbor PG1 remote-as external" '
+ )
+
+ def _bgp_peer_group_remoteas_del():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.251.2": {"peerGroup": "PG1", "bgpState": "Active"},
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_peer_group_remoteas_del)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed bgp convergence in r1"
+
+ logger.info("Re-add bgp peer-group PG1 remote-as neighbor should be established")
+ r1.cmd(
+ 'vtysh -c "config t" -c "router bgp 65001" '
+ + ' -c "neighbor PG1 remote-as external" '
+ )
+
+ def _bgp_peer_group_remoteas_add():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp neighbor json"))
+ expected = {
+ "192.168.251.2": {"peerGroup": "PG1", "bgpState": "Established"},
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_peer_group_remoteas_add)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed bgp convergence in r1"
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
index 3886bc1772..db6dbc61d2 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
@@ -129,6 +129,83 @@ def test_bgp_sender_as_path_loop_detection():
assert result is None, "Routes should not be sent to r1 from r2"
+def test_remove_loop_detection_on_one_peer():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_reset_route_to_r1():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 3}
+ return topotest.json_cmp(output, expected)
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ no neighbor 192.168.255.2 sender-as-path-loop-detection
+ """
+ )
+
+ r2.vtysh_cmd(
+ """
+ clear bgp *
+ """
+ )
+ test_func = functools.partial(_bgp_reset_route_to_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed bgp to reset route"
+
+
+def test_loop_detection_on_peer_group():
+ tgen = get_topogen()
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_suppress_route_to_r1():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 0}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_suppress_route_to_r3():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 2}
+ return topotest.json_cmp(output, expected)
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ neighbor loop_group peer-group
+ neighbor 192.168.255.2 peer-group loop_group
+ neighbor loop_group sender-as-path-loop-detection
+ """
+ )
+
+ r2.vtysh_cmd(
+ """
+ clear bgp *
+ """
+ )
+
+ test_func = functools.partial(_bgp_suppress_route_to_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Route 172.16.255.253/32 should not be sent to r3 from r2"
+
+ test_func = functools.partial(_bgp_suppress_route_to_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Routes should not be sent to r1 from r2"
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
index 6fc43e194d..f054fab48f 100644
--- a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
@@ -1,6 +1,5 @@
{
"vrfName": "default",
- "tableVersion": 2,
"routerId": "192.0.2.1",
"defaultLocPrf": 100,
"localAS": 1,
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
index 538e8955ef..60bcb7565f 100644
--- a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
@@ -1,6 +1,5 @@
{
"vrfName": "default",
- "tableVersion": 2,
"routerId": "192.0.2.2",
"defaultLocPrf": 100,
"localAS": 2,
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json
index 25b7a8616f..0fdd3d6dc0 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json
@@ -1,7 +1,6 @@
{
"vrfId": 0,
"vrfName": "default",
- "tableVersion": 2,
"routerId": "1.1.1.1",
"defaultLocPrf": 100,
"localAS": 1,
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json
index 2cd47b9ce5..03bbcc008d 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json
@@ -1,7 +1,6 @@
{
"vrfId": 0,
"vrfName": "default",
- "tableVersion": 2,
"routerId": "2.2.2.2",
"defaultLocPrf": 100,
"localAS": 2,
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index 23eab68db4..c6f038b7f6 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -68,6 +68,10 @@ def log_handler(basename, logpath):
topolog.logfinish(basename, logpath)
+def is_main_runner():
+ return "PYTEST_XDIST_WORKER" not in os.environ
+
+
def pytest_addoption(parser):
"""
Add topology-only option to the topology tester. This option makes pytest
@@ -86,6 +90,17 @@ def pytest_addoption(parser):
)
parser.addoption(
+ "--cov-topotest",
+ action="store_true",
+ help="Enable reporting of coverage",
+ )
+
+ parser.addoption(
+ "--cov-frr-build-dir",
+ help="Dir of coverage-enable build being run, default is the source dir",
+ )
+
+ parser.addoption(
"--gdb-breakpoints",
metavar="SYMBOL[,SYMBOL...]",
help="Comma-separated list of functions to set gdb breakpoints on",
@@ -456,6 +471,37 @@ def pytest_assertrepr_compare(op, left, right):
return json_result.gen_report()
+def setup_coverage(config):
+ commander = Commander("pytest")
+ if config.option.cov_frr_build_dir:
+ bdir = Path(config.option.cov_frr_build_dir).resolve()
+ output = commander.cmd_raises(f"find {bdir} -name zebra_nb.gcno").strip()
+ else:
+ # Support build sub-directory of main source dir
+ bdir = Path(__file__).resolve().parent.parent.parent
+ output = commander.cmd_raises(f"find {bdir} -name zebra_nb.gcno").strip()
+ m = re.match(f"({bdir}.*)/zebra/zebra_nb.gcno", output)
+ if not m:
+ logger.warning(
+ "No coverage data files (*.gcno) found, try specifying --cov-frr-build-dir"
+ )
+ return
+
+ bdir = Path(m.group(1))
+ # Save so we can get later from g_pytest_config
+ rundir = Path(config.option.rundir).resolve()
+ gcdadir = rundir / "gcda"
+ os.environ["FRR_BUILD_DIR"] = str(bdir)
+ os.environ["GCOV_PREFIX_STRIP"] = str(len(bdir.parts) - 1)
+ os.environ["GCOV_PREFIX"] = str(gcdadir)
+
+ if is_main_runner():
+ commander.cmd_raises(f"find {bdir} -name '*.gc??' -exec chmod o+rw {{}} +")
+ commander.cmd_raises(f"mkdir -p {gcdadir}")
+ commander.cmd_raises(f"chown -R root:frr {gcdadir}")
+ commander.cmd_raises(f"chmod 2775 {gcdadir}")
+
+
def pytest_configure(config):
"""
Assert that the environment is correctly configured, and get extra config.
@@ -556,8 +602,6 @@ def pytest_configure(config):
if config.option.topology_only and is_xdist:
pytest.exit("Cannot use --topology-only with distributed test mode")
- pytest.exit("Cannot use --topology-only with distributed test mode")
-
# Check environment now that we have config
if not diagnose_env(rundir):
pytest.exit("environment has errors, please read the logs in %s" % rundir)
@@ -572,27 +616,25 @@ def pytest_configure(config):
if "TOPOTESTS_CHECK_STDERR" in os.environ:
del os.environ["TOPOTESTS_CHECK_STDERR"]
+ if config.option.cov_topotest:
+ setup_coverage(config)
+
@pytest.fixture(autouse=True, scope="session")
-def setup_session_auto():
+def session_autouse():
# Aligns logs nicely
logging.addLevelName(logging.WARNING, " WARN")
logging.addLevelName(logging.INFO, " INFO")
- if "PYTEST_TOPOTEST_WORKER" not in os.environ:
- is_worker = False
- elif not os.environ["PYTEST_TOPOTEST_WORKER"]:
- is_worker = False
- else:
- is_worker = True
+ is_main = is_main_runner()
- logger.debug("Before the run (is_worker: %s)", is_worker)
- if not is_worker:
+ logger.debug("Before the run (is_main: %s)", is_main)
+ if is_main:
cleanup_previous()
yield
- if not is_worker:
+ if is_main:
cleanup_current()
- logger.debug("After the run (is_worker: %s)", is_worker)
+ logger.debug("After the run (is_main: %s)", is_main)
def pytest_runtest_setup(item):
@@ -719,6 +761,42 @@ def pytest_runtest_makereport(item, call):
pause_test()
+def coverage_finish(terminalreporter, config):
+ commander = Commander("pytest")
+ rundir = Path(config.option.rundir).resolve()
+ bdir = Path(os.environ["FRR_BUILD_DIR"])
+ gcdadir = Path(os.environ["GCOV_PREFIX"])
+
+ # Get the data files into the build directory
+ logger.info("Copying gcda files from '%s' to '%s'", gcdadir, bdir)
+ user = os.environ.get("SUDO_USER", os.environ["USER"])
+ commander.cmd_raises(f"chmod -R ugo+r {gcdadir}")
+ commander.cmd_raises(
+ f"tar -C {gcdadir} -cf - . | su {user} -c 'tar -C {bdir} -xf -'"
+ )
+
+ # Get the results into a summary file
+ data_file = rundir / "coverage.info"
+ logger.info("Gathering coverage data into: %s", data_file)
+ commander.cmd_raises(f"lcov --directory {bdir} --capture --output-file {data_file}")
+
+ # Get coverage info filtered to a specific set of files
+ report_file = rundir / "coverage.info"
+ logger.debug("Generating coverage summary from: %s\n%s", report_file)
+ output = commander.cmd_raises(f"lcov --summary {data_file}")
+ logger.info("\nCOVERAGE-SUMMARY-START\n%s\nCOVERAGE-SUMMARY-END", output)
+ terminalreporter.write(
+ f"\nCOVERAGE-SUMMARY-START\n{output}\nCOVERAGE-SUMMARY-END\n"
+ )
+
+
+def pytest_terminal_summary(terminalreporter, exitstatus, config):
+ # Only run if we are the top level test runner
+ is_xdist_worker = "PYTEST_XDIST_WORKER" in os.environ
+ if config.option.cov_topotest and not is_xdist_worker:
+ coverage_finish(terminalreporter, config)
+
+
#
# Add common fixtures available to all tests as parameters
#
diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
index 7f37cedb2b..b5b03e86b4 100644
--- a/tests/topotests/evpn_pim_1/spine/bgp.summ.json
+++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
@@ -2,7 +2,6 @@
"routerId":"192.168.100.1",
"as":65001,
"vrfName":"default",
- "tableVersion":7,
"peerCount":2,
"peers":{
"192.168.1.2":{
diff --git a/tests/topotests/grpc_basic/test_basic_grpc.py b/tests/topotests/grpc_basic/test_basic_grpc.py
index bb57705a70..1ded663179 100644
--- a/tests/topotests/grpc_basic/test_basic_grpc.py
+++ b/tests/topotests/grpc_basic/test_basic_grpc.py
@@ -142,7 +142,7 @@ def test_shutdown_checks(tgen):
time.sleep(1)
try:
for r in tgen.routers().values():
- r.net.stopRouter()
+ r.net.stopRouter(False)
r.net.checkRouterCores()
finally:
if p:
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 598db84e63..7787b6f74b 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -936,14 +936,26 @@ def generate_support_bundle():
"""
tgen = get_topogen()
+ if tgen is None:
+ logger.warn(
+ "Support bundle attempted to be generated, but topogen is not being used"
+ )
+ return True
+
router_list = tgen.routers()
test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0]
bundle_procs = {}
for rname, rnode in router_list.items():
logger.info("Spawn collection of support bundle for %s", rname)
- dst_bundle = "{}/{}/support_bundles/{}".format(tgen.logdir, rname, test_name)
- rnode.run("mkdir -p " + dst_bundle)
+ try:
+ dst_bundle = "{}/{}/support_bundles/{}".format(
+ tgen.logdir, rname, test_name
+ )
+ rnode.run("mkdir -p " + dst_bundle)
+ except Exception as err:
+ logger.error("Generation of Support bundle failed {}".format(err))
+ return True
gen_sup_cmd = [
"/usr/lib/frr/generate_support_bundle.py",
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 0a5be970b8..985ba536dd 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -27,6 +27,7 @@ import time
import logging
from collections.abc import Mapping
from copy import deepcopy
+from pathlib import Path
import lib.topolog as topolog
from lib.micronet_compat import Node
@@ -1262,8 +1263,8 @@ def rlimit_atleast(rname, min_value, raises=False):
def fix_netns_limits(ns):
# Maximum read and write socket buffer sizes
- sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20])
- sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20])
+ sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20])
+ sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20])
sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0)
sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0)
@@ -1295,6 +1296,8 @@ def fix_netns_limits(ns):
sysctl_assure(ns, "net.ipv4.conf.all.ignore_routes_with_linkdown", 1)
sysctl_assure(ns, "net.ipv6.conf.all.ignore_routes_with_linkdown", 1)
+ sysctl_assure(ns, "net.ipv4.conf.default.ignore_routes_with_linkdown", 1)
+ sysctl_assure(ns, "net.ipv6.conf.default.ignore_routes_with_linkdown", 1)
# igmp
sysctl_atleast(ns, "net.ipv4.igmp_max_memberships", 1000)
@@ -1322,8 +1325,8 @@ def fix_host_limits():
sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024)
# Maximum read and write socket buffer sizes
- sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20)
- sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20)
+ sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20)
+ sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20)
# Garbage Collection Settings for ARP and Neighbors
sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024)
@@ -1523,7 +1526,7 @@ class Router(Node):
pass
return ret
- def stopRouter(self, assertOnError=True, minErrorVersion="5.1"):
+ def stopRouter(self, assertOnError=True):
# Stop Running FRR Daemons
running = self.listDaemons()
if not running:
@@ -1570,9 +1573,6 @@ class Router(Node):
)
errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
if assertOnError and (errors is not None) and len(errors) > 0:
assert "Errors found - details follow:" == 0, errors
return errors
@@ -1803,6 +1803,8 @@ class Router(Node):
"Starts FRR daemons for this router."
asan_abort = bool(g_pytest_config.option.asan_abort)
+ cov_option = bool(g_pytest_config.option.cov_topotest)
+ cov_dir = Path(g_pytest_config.option.rundir) / "gcda"
gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
@@ -1836,13 +1838,6 @@ class Router(Node):
# Re-enable to allow for report per run
self.reportCores = True
- # XXX: glue code forward ported from removed function.
- if self.version is None:
- self.version = self.cmd(
- os.path.join(self.daemondir, "bgpd") + " -v"
- ).split()[2]
- logger.info("{}: running version: {}".format(self.name, self.version))
-
perfds = {}
perf_options = g_pytest_config.get_option("--perf-options", "-g")
for perf in g_pytest_config.get_option("--perf", []):
@@ -1928,6 +1923,10 @@ class Router(Node):
self.logdir, self.name, daemon
)
+ if cov_option:
+ scount = os.environ["GCOV_PREFIX_STRIP"]
+ cmdenv += f"GCOV_PREFIX_STRIP={scount} GCOV_PREFIX={cov_dir}"
+
if valgrind_memleaks:
this_dir = os.path.dirname(
os.path.abspath(os.path.realpath(__file__))
@@ -2277,9 +2276,7 @@ class Router(Node):
rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False)
return rc == 0 or "No such process" not in e
- def killRouterDaemons(
- self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1"
- ):
+ def killRouterDaemons(self, daemons, wait=True, assertOnError=True):
# Kill Running FRR
# Daemons(user specified daemon only) using SIGKILL
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
@@ -2339,9 +2336,6 @@ class Router(Node):
self.cmd("rm -- {}".format(daemonpidfile))
if wait:
errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion("<", minErrorVersion):
- # ignore errors in old versions
- errors = ""
if assertOnError and len(errors) > 0:
assert "Errors found - details follow:" == 0, errors
else:
diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py
index bf4e95b275..605c14285f 100644
--- a/tests/topotests/mgmt_tests/test_yang_mgmt.py
+++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py
@@ -36,6 +36,7 @@ import time
import os
import pytest
import platform
+import json
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -45,7 +46,7 @@ sys.path.append(os.path.join(CWD, "../lib/"))
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib.topogen import Topogen, get_topogen
-from lib.topotest import version_cmp
+from lib.topotest import version_cmp, router_json_cmp
# Import topoJson from lib, to create topology and initial configuration
from lib.common_config import (
@@ -401,6 +402,242 @@ def test_mgmt_delete_config(request):
write_test_footer(tc_name)
+def test_mgmt_edit_config(request):
+ """
+ Verify mgmt edit config.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ r1 = tgen.gears["r1"]
+
+ # check "create" operation
+ data = {"frr-interface:interface": [{"name": "eth0", "description": "eth0-desc"}]}
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit create /frr-interface:lib lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = data
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib/interface[name='eth0'] only-config exact",
+ data_out,
+ exact=True,
+ )
+ == None
+ )
+
+ # check error on "create" for an existing object
+ data = {"frr-interface:interface": [{"name": "eth0", "description": "eth0-desc"}]}
+ ret = r1.vtysh_cmd(
+ f"conf\nmgmt edit create /frr-interface:lib lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ assert "Data already exists" in ret
+
+ # check adding a leaf to an existing object using "merge"
+ data = {
+ "frr-interface:interface": [
+ {"name": "eth0", "frr-zebra:zebra": {"bandwidth": 100}}
+ ]
+ }
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit merge /frr-interface:lib/interface[name='eth0'] lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = {
+ "frr-interface:interface": [
+ {
+ "name": "eth0",
+ "description": "eth0-desc",
+ "frr-zebra:zebra": {"bandwidth": 100},
+ }
+ ]
+ }
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib/interface[name='eth0'] only-config exact",
+ data_out,
+ exact=True,
+ )
+ == None
+ )
+
+ # check replacing an existing object using "replace"
+ data = {
+ "frr-interface:interface": [{"name": "eth0", "description": "eth0-desc-new"}]
+ }
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit replace /frr-interface:lib/interface[name='eth0'] lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = data
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib/interface[name='eth0'] only-config exact",
+ data_out,
+ exact=True,
+ )
+ == None
+ )
+
+ # check error on "replace" when keys in xpath and data are different
+ data = {
+ "frr-interface:interface": [{"name": "eth1", "description": "eth0-desc-new"}]
+ }
+ ret = r1.vtysh_cmd(
+ f"conf\nmgmt edit replace /frr-interface:lib/interface[name='eth0'] lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ assert "List keys in xpath and data tree are different" in ret
+
+ # check deleting an existing object using "delete"
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit delete /frr-interface:lib/interface[name='eth0'] lock commit"
+ )
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib/interface[name='eth0'] only-config exact",
+ {},
+ exact=True,
+ )
+ == None
+ )
+
+ # check error on "delete" for a non-existing object
+ ret = r1.vtysh_cmd(
+ f"conf\nmgmt edit delete /frr-interface:lib/interface[name='eth0'] lock commit"
+ )
+ assert "Data missing" in ret
+
+ # check no error on "remove" for a non-existing object
+ ret = r1.vtysh_cmd(
+ f"conf\nmgmt edit remove /frr-interface:lib/interface[name='eth0'] lock commit"
+ )
+ assert "Data missing" not in ret
+
+ # check "remove" for an existing object
+ data = {"frr-interface:interface": [{"name": "eth0", "description": "eth0-desc"}]}
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit create /frr-interface:lib lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit remove /frr-interface:lib/interface[name='eth0'] lock commit"
+ )
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib/interface[name='eth0'] only-config exact",
+ {},
+ exact=True,
+ )
+ == None
+ )
+
+ # check "create" of a top-level node
+ data = {"frr-vrf:lib": {"vrf": [{"name": "vrf1"}]}}
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit create / lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = data
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-vrf:lib only-config exact",
+ data_out,
+ exact=True,
+ )
+ == None
+ )
+
+ # check "replace" of a top-level node
+ data = {"frr-vrf:lib": {"vrf": [{"name": "vrf2"}]}}
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit replace /frr-vrf:lib lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = data
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-vrf:lib only-config exact",
+ data_out,
+ exact=True,
+ )
+ == None
+ )
+
+ # check "delete" of a top-level node
+ r1.vtysh_cmd(f"conf\nmgmt edit delete /frr-vrf:lib lock commit")
+ assert (
+ router_json_cmp(
+ r1, "show mgmt get-data /frr-vrf:lib only-config exact", {}, exact=True
+ )
+ == None
+ )
+
+ # check replace of the whole config
+ # this test won't work at the moment, because we don't allow to delete
+ # interfaces from the config if they are present in the system
+ # another problem is that we don't allow "/" as an xpath in get-data command
+ # therefore, commenting it out for now
+ # data = {
+ # "frr-interface:lib": {
+ # "interface": [{"name": "eth1", "description": "eth1-desc"}]
+ # },
+ # "frr-vrf:lib": {"vrf": [{"name": "vrf3"}]},
+ # }
+ # r1.vtysh_cmd(
+ # f"conf\nmgmt edit replace / lock commit {json.dumps(data, separators=(',', ':'))}"
+ # )
+ # data_out = data
+ # assert (
+ # router_json_cmp(
+ # r1,
+ # "show mgmt get-data / only-config exact",
+ # data_out,
+ # exact=True,
+ # )
+ # == None
+ # )
+
+ # check "merge" of the whole config
+ data = {
+ "frr-interface:lib": {
+ "interface": [{"name": "eth2", "description": "eth2-desc"}]
+ },
+ "frr-vrf:lib": {"vrf": [{"name": "vrf4"}]},
+ }
+ r1.vtysh_cmd(
+ f"conf\nmgmt edit merge / lock commit {json.dumps(data, separators=(',', ':'))}"
+ )
+ data_out = data
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-interface:lib only-config exact",
+ {
+ "frr-interface:lib": {
+ "interface": [{"name": "eth2", "description": "eth2-desc"}]
+ }
+ },
+ )
+ == None
+ )
+ assert (
+ router_json_cmp(
+ r1,
+ "show mgmt get-data /frr-vrf:lib only-config exact",
+ {"frr-vrf:lib": {"vrf": [{"name": "vrf4"}]}},
+ )
+ == None
+ )
+
+
def test_mgmt_chaos_stop_start_frr(request):
"""
Kill mgmtd - verify that watch frr restarts.
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
index f0950a2db3..45c1325917 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
@@ -49,7 +49,7 @@ from lib.ospf import (
verify_ospf_interface,
)
-pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.staticd]
# Global variables
diff --git a/tests/topotests/ospf_p2mp/r1/frr-p2mp-non-broadcast.conf b/tests/topotests/ospf_p2mp/r1/frr-p2mp-non-broadcast.conf
new file mode 100644
index 0000000000..ca84349cdc
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r1/frr-p2mp-non-broadcast.conf
@@ -0,0 +1,26 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r1-eth0
+ ip address 10.1.0.1/24
+ ip ospf network point-to-multipoint non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth1
+ ip address 10.1.1.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 1.1.1.1
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.1.0/24 area 0
+ neighbor 10.1.0.2 poll-interval 5
+ neighbor 10.1.0.3 poll-interval 5
+ neighbor 10.1.0.4 poll-interval 5
+!
diff --git a/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf
new file mode 100644
index 0000000000..cb4538c0e3
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r1/frr-p2mp.conf
@@ -0,0 +1,23 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r1-eth0
+ ip address 10.1.0.1/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth1
+ ip address 10.1.1.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+ ospf router-id 1.1.1.1
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.1.0/24 area 0
+!
diff --git a/tests/topotests/ospf_p2mp/r2/frr-p2mp-non-broadcast.conf b/tests/topotests/ospf_p2mp/r2/frr-p2mp-non-broadcast.conf
new file mode 100644
index 0000000000..6e26897c49
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r2/frr-p2mp-non-broadcast.conf
@@ -0,0 +1,29 @@
+!
+hostname r2
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r2-eth0
+ ip address 10.1.0.2/24
+ ip ospf network point-to-multipoint non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth1
+ ip address 10.1.2.2/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+!
+!
+router ospf
+ ospf router-id 2.2.2.2
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.2.0/24 area 0
+ neighbor 10.1.0.1 poll-interval 5
+ neighbor 10.1.0.3 poll-interval 5
+ neighbor 10.1.0.4 poll-interval 5
+!
diff --git a/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf
new file mode 100644
index 0000000000..0ca8aec3bf
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r2/frr-p2mp.conf
@@ -0,0 +1,26 @@
+!
+hostname r2
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r2-eth0
+ ip address 10.1.0.2/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth1
+ ip address 10.1.2.2/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+!
+!
+router ospf
+ ospf router-id 2.2.2.2
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.2.0/24 area 0
+!
diff --git a/tests/topotests/ospf_p2mp/r3/frr-p2mp-non-broadcast.conf b/tests/topotests/ospf_p2mp/r3/frr-p2mp-non-broadcast.conf
new file mode 100644
index 0000000000..a69e0557be
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r3/frr-p2mp-non-broadcast.conf
@@ -0,0 +1,28 @@
+!
+hostname r3
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r3-eth0
+ ip address 10.1.0.3/24
+ ip ospf network point-to-multipoint non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r3-eth1
+ ip address 10.1.3.3/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 3.3.3.3
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.3.0/24 area 0
+ neighbor 10.1.0.1 poll-interval 5
+ neighbor 10.1.0.2 poll-interval 5
+ neighbor 10.1.0.4 poll-interval 5
diff --git a/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf
new file mode 100644
index 0000000000..41ea70d443
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r3/frr-p2mp.conf
@@ -0,0 +1,25 @@
+!
+hostname r3
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r3-eth0
+ ip address 10.1.0.3/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r3-eth1
+ ip address 10.1.3.3/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 3.3.3.3
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.3.0/24 area 0
diff --git a/tests/topotests/ospf_p2mp/r4/frr-p2mp-non-broadcast.conf b/tests/topotests/ospf_p2mp/r4/frr-p2mp-non-broadcast.conf
new file mode 100644
index 0000000000..1b8388584b
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r4/frr-p2mp-non-broadcast.conf
@@ -0,0 +1,28 @@
+!
+hostname r4
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r4-eth0
+ ip address 10.1.0.4/24
+ ip ospf network point-to-multipoint non-broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r4-eth1
+ ip address 10.1.4.4/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 4.4.4.4
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.4.0/24 area 0
+ neighbor 10.1.0.1 poll-interval 5
+ neighbor 10.1.0.2 poll-interval 5
+ neighbor 10.1.0.3 poll-interval 5
diff --git a/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf b/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf
new file mode 100644
index 0000000000..21fa9c72f9
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/r4/frr-p2mp.conf
@@ -0,0 +1,25 @@
+!
+hostname r4
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r4-eth0
+ ip address 10.1.0.4/24
+ ip ospf network point-to-multipoint
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r4-eth1
+ ip address 10.1.4.4/24
+ ip ospf network broadcast
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+ ospf router-id 4.4.4.4
+ distance 20
+ network 10.1.0.0/24 area 0
+ network 10.1.4.0/24 area 0
diff --git a/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py b/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py
new file mode 100644
index 0000000000..352180b7ce
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/test_ospf_p2mp_broadcast.py
@@ -0,0 +1,346 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_ospf_prefix_p2mp_broadcast.py
+#
+# Copyright (c) 2024 LabN Consulting
+# Acee Lindem
+#
+
+import os
+import sys
+import json
+from time import sleep
+from functools import partial
+import pytest
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.common_config import (
+ run_frr_cmd,
+ shutdown_bringup_interface,
+ start_router_daemons,
+ step,
+)
+
+
+"""
+test_ospf_p2mp_broadcast.py: Test OSPF Point-to-multipoint
+"""
+
+TOPOLOGY = """
+ +-----+ +-----+
+10.1.1.0/24 | r1 | | r2 | 10.1.2.0/24
+ -----------+ | | +----------
+ +--+--+ +--+--+
+ | 10.1.0.0/24 |
+ | +-------+ |
+ +---- | |-----+
+ | P2MP |
+ +---- | |-----+
+ | +-------+ |
+ | |
+ | |
+ +--+--+ +-+---+
+10.1.3.0/24 | r3 | | r4 | 10.1.4.0/24
+ -----------+ | | +----------
+ +-----+ +-----+
+
+
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+ tgen.add_router("r4")
+
+ # Interconect them all to the P2MP network
+ switch = tgen.add_switch("s0-p2mp")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # Add standalone network to router 1
+ switch = tgen.add_switch("s-r1-1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Add standalone network to router 2
+ switch = tgen.add_switch("s-r2-1")
+ switch.add_link(tgen.gears["r2"])
+
+ # Add standalone network to router 3
+ switch = tgen.add_switch("s-r3-1")
+ switch.add_link(tgen.gears["r3"])
+
+ # Add standalone network to router 4
+ switch = tgen.add_switch("s-r4-1")
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Point-to-MultiPoint:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(os.path.join(CWD, "{}/frr-p2mp.conf".format(rname)))
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def verify_p2mp_interface(tgen):
+ "Verify the P2MP Configuration and interface settings"
+
+ r1 = tgen.gears["r1"]
+
+ step("Test running configuration for P2MP configuration")
+ rc = 0
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep 'ip ospf network point-to-multipoint'", warn=False
+ )
+ assertmsg = "'ip ospf network point-to-multipoint' applied, but not present in r1 configuration"
+ assert rc, assertmsg
+
+ step("Test OSPF interface for P2MP settings")
+ input_dict = {
+ "interfaces": {
+ "r1-eth0": {
+ "ospfEnabled": True,
+ "interfaceIp": {
+ "10.1.0.1": {
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "routerId": "1.1.1.1",
+ "networkType": "POINTOMULTIPOINT",
+ "cost": 10,
+ "state": "Point-To-Point",
+ "nbrCount": 3,
+ "nbrAdjacentCount": 3,
+ "prefixSuppression": False,
+ "p2mpDelayReflood": False,
+ "p2mpNonBroadcast": False,
+ }
+ },
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "area": "0.0.0.0",
+ "routerId": "1.1.1.1",
+ "networkType": "POINTOMULTIPOINT",
+ "cost": 10,
+ "state": "Point-To-Point",
+ "opaqueCapable": True,
+ "nbrCount": 3,
+ "nbrAdjacentCount": 3,
+ "prefixSuppression": False,
+ "p2mpDelayReflood": False,
+ "p2mpNonBroadcast": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface r1-eth0 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Interface Mismatch on router r1"
+ assert result is None, assertmsg
+
+
+def verify_non_p2mp_interface(tgen):
+ "Verify the removal of P2MP Configuration and interface settings"
+ r1 = tgen.gears["r1"]
+
+ step("Test running configuration for removal of P2MP configuration")
+ rc = 0
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf network point-to-multipoint'", warn=False
+ )
+ assertmsg = "'ip ospf network point-to-multipoint' not applied, but present in r1 configuration"
+ assert rc, assertmsg
+
+ step("Test OSPF interface for default settings")
+ input_dict = {
+ "interfaces": {
+ "r1-eth0": {
+ "ospfEnabled": True,
+ "interfaceIp": {
+ "10.1.0.1": {
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "routerId": "1.1.1.1",
+ "networkType": "BROADCAST",
+ "cost": 10,
+ "prefixSuppression": False,
+ }
+ },
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "area": "0.0.0.0",
+ "routerId": "1.1.1.1",
+ "networkType": "BROADCAST",
+ "cost": 10,
+ "opaqueCapable": True,
+ "prefixSuppression": False,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf interface r1-eth0 json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Interface Mismatch on router r1"
+ assert result is None, assertmsg
+
+
+def verify_p2mp_neighbor(tgen, router, neighbor, state, intf_addr, interface):
+ topo_router = tgen.gears[router]
+
+ step("Verify neighbor " + neighbor + " in " + state + " state")
+ input_dict = {
+ "default": {
+ neighbor: [
+ {
+ "nbrState": state,
+ "ifaceAddress": intf_addr,
+ "ifaceName": interface,
+ }
+ ],
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ topo_router,
+ "show ip ospf neighbor " + neighbor + " json",
+ input_dict,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Neighbor " + neighbor + " not in " + state
+ assert result is None, assertmsg
+
+
+def verify_p2mp_route(tgen, router, prefix, prefix_len, nexthop, interface):
+ topo_router = tgen.gears[router]
+
+ step("Verify router " + router + " p2mp route " + prefix + " installed")
+ input_dict = {
+ prefix: [
+ {
+ "prefix": prefix,
+ "prefixLen": prefix_len,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": nexthop,
+ "interfaceName": interface,
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ topo_router,
+ "show ip route " + prefix + " json",
+ input_dict,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = prefix + " not installed on router " + router
+ assert result is None, assertmsg
+
+
+def test_p2mp_broadcast_interface():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Verify router r1 interface r1-eth0 p2mp configuration")
+ verify_p2mp_interface(tgen)
+
+ step("Verify router r1 p2mp interface r1-eth0 neighbors")
+ verify_p2mp_neighbor(
+ tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
+ )
+
+ step("Verify router r1 p2mp routes installed")
+ verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
+
+ step("Verify router r1 interface r1-eth0 p2mp configuration removal")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nno ip ospf network point-to-multipoint")
+ verify_non_p2mp_interface(tgen)
+
+ step("Verify router r1 interface r1-eth0 p2mp configuration application")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint")
+ verify_p2mp_interface(tgen)
+
+ step("Verify restablishment of r1-eth0 p2mp neighbors")
+ verify_p2mp_neighbor(
+ tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
+ )
+
+ step("Verify router r1 p2mp routes reinstalled")
+ verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_p2mp/test_ospf_p2mp_non_broadcast.py b/tests/topotests/ospf_p2mp/test_ospf_p2mp_non_broadcast.py
new file mode 100644
index 0000000000..175dca74e7
--- /dev/null
+++ b/tests/topotests/ospf_p2mp/test_ospf_p2mp_non_broadcast.py
@@ -0,0 +1,467 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_prefix_p2mp_non_broadcast.py
+#
+# Copyright (c) 2024 LabN Consulting
+# Acee Lindem
+#
+
+import os
+import sys
+import json
+from time import sleep
+from functools import partial
+import pytest
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.common_config import (
+ run_frr_cmd,
+ shutdown_bringup_interface,
+ start_router_daemons,
+ step,
+)
+
+
+"""
+test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
+ Full Mesh
+"""
+
+TOPOLOGY = """
+ +-----+ +-----+
+10.1.1.0/24 | r1 | | r2 | 10.1.2.0/24
+ -----------+ | | +----------
+ +--+--+ +--+--+
+ | 10.1.0.0/24 |
+ | +-------+ |
+ +---- | |-----+
+ | P2MP |
+ +---- | |-----+
+ | +-------+ |
+ | |
+ | |
+ +--+--+ +-+---+
+10.1.3.0/24 | r3 | | r4 | 10.1.4.0/24
+ -----------+ | | +----------
+ +-----+ +-----+
+
+
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+ tgen.add_router("r4")
+
+ # Interconect them all to the P2MP network
+ switch = tgen.add_switch("s0-p2mp")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # Add standalone network to router 1
+ switch = tgen.add_switch("s-r1-1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Add standalone network to router 2
+ switch = tgen.add_switch("s-r2-1")
+ switch.add_link(tgen.gears["r2"])
+
+ # Add standalone network to router 3
+ switch = tgen.add_switch("s-r3-1")
+ switch.add_link(tgen.gears["r3"])
+
+ # Add standalone network to router 4
+ switch = tgen.add_switch("s-r4-1")
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(
+ os.path.join(CWD, "{}/frr-p2mp-non-broadcast.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def verify_p2mp_interface(tgen, router, nbr_cnt, nbr_adj_cnt, non_broadcast):
+ "Verify the P2MP Configuration and interface settings"
+
+ topo_router = tgen.gears[router]
+
+ step("Test running configuration for P2MP configuration")
+ rc = 0
+ rc, _, _ = tgen.net[router].cmd_status(
+ "show running ospfd | grep 'ip ospf network point-to-multipoint'", warn=False
+ )
+ assertmsg = (
+ "'ip ospf network point-to-multipoint' applied, but not present in "
+ + router
+ + "configuration"
+ )
+ assert rc, assertmsg
+
+ step("Test OSPF interface for P2MP settings")
+ input_dict = {
+ "interfaces": {
+ "r1-eth0": {
+ "ospfEnabled": True,
+ "interfaceIp": {
+ "10.1.0.1": {
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "routerId": "1.1.1.1",
+ "networkType": "POINTOMULTIPOINT",
+ "cost": 10,
+ "state": "Point-To-Point",
+ "nbrCount": nbr_cnt,
+ "nbrAdjacentCount": nbr_adj_cnt,
+ "prefixSuppression": False,
+ "p2mpDelayReflood": False,
+ "p2mpNonBroadcast": non_broadcast,
+ }
+ },
+ "ipAddress": "10.1.0.1",
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "area": "0.0.0.0",
+ "routerId": "1.1.1.1",
+ "networkType": "POINTOMULTIPOINT",
+ "cost": 10,
+ "state": "Point-To-Point",
+ "opaqueCapable": True,
+ "nbrCount": nbr_cnt,
+ "nbrAdjacentCount": nbr_adj_cnt,
+ "prefixSuppression": False,
+ "p2mpDelayReflood": False,
+ "p2mpNonBroadcast": non_broadcast,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ topo_router,
+ "show ip ospf interface r1-eth0 json",
+ input_dict,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Interface Mismatch on router r1"
+ assert result is None, assertmsg
+
+
+def verify_p2mp_neighbor(tgen, router, neighbor, state, intf_addr, interface):
+ topo_router = tgen.gears[router]
+
+ step("Verify neighbor " + neighbor + " in " + state + " state")
+ input_dict = {
+ "default": {
+ neighbor: [
+ {
+ "nbrState": state,
+ "ifaceAddress": intf_addr,
+ "ifaceName": interface,
+ }
+ ],
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ topo_router,
+ "show ip ospf neighbor " + neighbor + " json",
+ input_dict,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Neighbor " + neighbor + " not in " + state
+ assert result is None, assertmsg
+
+
+def verify_p2mp_route(tgen, router, prefix, prefix_len, nexthop, interface):
+ topo_router = tgen.gears[router]
+
+ step("Verify router " + router + " p2mp route " + prefix + " installed")
+ input_dict = {
+ prefix: [
+ {
+ "prefix": prefix,
+ "prefixLen": prefix_len,
+ "protocol": "ospf",
+ "nexthops": [
+ {
+ "ip": nexthop,
+ "interfaceName": interface,
+ }
+ ],
+ }
+ ]
+ }
+ test_func = partial(
+ topotest.router_json_cmp,
+ topo_router,
+ "show ip route " + prefix + " json",
+ input_dict,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = prefix + " not installed on router " + router
+ assert result is None, assertmsg
+
+
+def test_p2mp_non_broadcast_connectivity():
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
+ verify_p2mp_interface(tgen, "r1", 3, 3, True)
+
+ step("Verify router r1 interface r1-eth0 p2mp non-broadcast configuration")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf network point-to-multipoint non-broadcast'",
+ warn=False,
+ )
+ assertmsg = "'ip ospf network point-to-multipoint non-broadcast' applied, but not present in R1 configuration"
+ assert rc, assertmsg
+
+ step("Verify router r1 OSPF point-to-multipoint neighbors")
+ verify_p2mp_neighbor(
+ tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
+ )
+
+ step("Verify router r1 OSPF point-to-multipoint routes are installed")
+ verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.3", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
+
+ step("Remove r1 interface r1-eth0 p2mp non-broadcast configuration")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nip ospf network point-to-multipoint")
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep -q 'ip ospf network point-to-multipoint non-broadcast'",
+ warn=False,
+ )
+ assertmsg = "'ip ospf network point-to-multipoint non-broadcast' not applied, but present in r1 configuration"
+ assert rc, assertmsg
+
+ step("Verify router r1 interface OSPF point-to-multipoint broadcast interface")
+ verify_p2mp_interface(tgen, "r1", 3, 3, False)
+
+ step("Add r1 interface r1-eth0 p2mp non-broadcast configuration back")
+ r1.vtysh_cmd(
+ "conf t\ninterface r1-eth0\nip ospf network point-to-multipoint non-broadcast"
+ )
+ rc, _, _ = tgen.net["r1"].cmd_status(
+ "show running ospfd | grep 'ip ospf network point-to-multipoint non-broadcast'",
+ warn=False,
+ )
+ assertmsg = "'ip ospf netrwork point-to-multipoint non-broadcast' applied, but not present in R1 configuration"
+ assert rc, assertmsg
+
+ step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
+ verify_p2mp_interface(tgen, "r1", 3, 3, True)
+
+ step(
+ "Verify router r1 OSPF point-to-multipoint neighbors adjacencies restablished."
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "3.3.3.3", "Full/DROther", "10.1.0.3", "r1-eth0:10.1.0.1"
+ )
+ verify_p2mp_neighbor(
+ tgen, "r1", "4.4.4.4", "Full/DROther", "10.1.0.4", "r1-eth0:10.1.0.1"
+ )
+
+
+def test_p2mp_non_broadcast_partial_mesh_connectivity():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of router(s) failure")
+
+ """
+ test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
+ Partial Mesh
+ """
+
+ TOPOLOGY = """
+ +-----+ +------+
+ 10.1.1.0/24 | r1 | | r4 | 10.1.4.0/24
+ -----------+ | | +----------
+ +-+---+ +--+-+-+
+ | P2MP |
+ | Non-Broadcast |
+ | 10.1.0.0/24 |
+ +-+---+ +-+---+
+ 10.1.2.0/24 | r2 | | r3 | 10.1.3.0/24
+ -----------+ +--------------+ +----------
+ +-----+ +-----+
+
+
+ """
+ logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
+
+ step("Change configuration to a partial mesh")
+ step("Delete neighbors in full mesh")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.3")
+ r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.4")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.4")
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
+ r4 = tgen.gears["r4"]
+ r4.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
+ r4.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.2")
+
+ step("Flap interfaces on P2MP network to avoid transients")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nshut")
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nshut")
+ r3.vtysh_cmd("conf t\ninterface r3-eth0\nshut")
+ r4.vtysh_cmd("conf t\ninterface r4-eth0\nshut")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nno shut")
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nno shut")
+ r3.vtysh_cmd("conf t\ninterface r3-eth0\nno shut")
+ r4.vtysh_cmd("conf t\ninterface r4-eth0\nno shut")
+
+ step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
+ verify_p2mp_interface(tgen, "r1", 1, 1, True)
+
+ step("Verify router r1 interface r1-eth0 p2mp neighbor")
+ verify_p2mp_neighbor(
+ tgen, "r1", "2.2.2.2", "Full/DROther", "10.1.0.2", "r1-eth0:10.1.0.1"
+ )
+
+ step("Verify router r1 p2mp routes are installed")
+ verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.2", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.2", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.2", "r1-eth0")
+
+ """
+ test_ospf_p2mp_non_broadcast.py: Test OSPF Point-to-multipoint Non-Broadcast
+ Modified Partial Mesh
+ """
+
+ TOPOLOGY = """
+ +-----+ +------+
+ 10.1.1.0/24 | r1 | | r4 | 10.1.4.0/24
+ -----------+ +-------------+ +----------
+ +-----+ +--+-+-+
+ P2MP |
+ Non-Broadcast |
+ 10.1.0.0/24 |
+ +-+---+ +-+---+
+ 10.1.2.0/24 | r2 | | r3 | 10.1.3.0/24
+ -----------+ +--------------+ +----------
+ +-----+ +-----+
+
+
+ """
+ logger.info("OSPF Point-to-MultiPoint Non-Broadcast:\n {}".format(TOPOLOGY))
+
+ step("Change configuration to a partial mesh")
+ step("Modify neighbors in partial mesh")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.2")
+ r1.vtysh_cmd("conf t\nrouter ospf\nneighbor 10.1.0.4 poll-interval 5")
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf t\nrouter ospf\nno neighbor 10.1.0.1")
+ r4 = tgen.gears["r4"]
+ r4.vtysh_cmd("conf t\nrouter ospf\nneighbor 10.1.0.1")
+
+ step("Flap interfaces on P2MP network to avoid transients")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nshut")
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nshut")
+ r3.vtysh_cmd("conf t\ninterface r3-eth0\nshut")
+ r4.vtysh_cmd("conf t\ninterface r4-eth0\nshut")
+ r1.vtysh_cmd("conf t\ninterface r1-eth0\nno shut")
+ r2.vtysh_cmd("conf t\ninterface r2-eth0\nno shut")
+ r3.vtysh_cmd("conf t\ninterface r3-eth0\nno shut")
+ r4.vtysh_cmd("conf t\ninterface r4-eth0\nno shut")
+
+ step("Verify router r1 interface r1-eth0")
+ step("Verify router r1 interface OSPF point-to-multipoint non-broadcast interface")
+ verify_p2mp_interface(tgen, "r1", 1, 1, True)
+
+ step("Verify router r1 interface r1-eth0 p2mp neighbor")
+ input_dict = {
+ "neighbors": {
+ "4.4.4.4": [
+ {
+ "nbrState": "Full/DROther",
+ "ifaceAddress": "10.1.0.4",
+ "ifaceName": "r1-eth0:10.1.0.1",
+ }
+ ],
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf neighbor json", input_dict
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = "P2MP Non-Broadcast Neighbors not adjacent on router r1"
+ assert result is None, assertmsg
+
+ step("Verify router r1 interface r1-eth0 p2mp routes are installed")
+ verify_p2mp_route(tgen, "r1", "10.1.2.0/24", 24, "10.1.0.4", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.3.0/24", 24, "10.1.0.4", "r1-eth0")
+ verify_p2mp_route(tgen, "r1", "10.1.4.0/24", 24, "10.1.0.4", "r1-eth0")
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/cocci.h b/tools/cocci.h
index 7d6bb4cd7f..99076b5d3f 100644
--- a/tools/cocci.h
+++ b/tools/cocci.h
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/* some of this stuff doesn't seem to parse properly in coccinelle
*/
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index ef92e8b59f..b6e67fc7d2 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -220,6 +220,23 @@ def get_normalized_mac_ip_line(line):
return line
+def get_normalized_interface_vrf(line):
+ """
+ If 'interface <int_name> vrf <vrf_name>' is present in file,
+ we need to remove the explicit "vrf <vrf_name>"
+ so that the context information is created
+ correctly and configurations are matched appropriately.
+ """
+
+ intf_vrf = re.search("interface (\S+) vrf (\S+)", line)
+ if intf_vrf:
+ old_line = "vrf %s" % intf_vrf.group(2)
+ new_line = line.replace(old_line, "").strip()
+ return new_line
+
+ return line
+
+
# This dictionary contains a tree of all commands that we know start a
# new multi-line context. All other commands are treated either as
# commands inside a multi-line context or as single-line contexts. This
@@ -295,6 +312,10 @@ class Config(object):
# Compress duplicate whitespaces
line = " ".join(line.split())
+ # Remove 'vrf <vrf_name>' from 'interface <x> vrf <vrf_name>'
+ if line.startswith("interface ") and "vrf" in line:
+ line = get_normalized_interface_vrf(line)
+
if ":" in line:
line = get_normalized_mac_ip_line(line)
diff --git a/tools/indent.py b/tools/indent.py
index fe9eb7c252..dac7d3f04b 100755
--- a/tools/indent.py
+++ b/tools/indent.py
@@ -34,13 +34,13 @@ def wrap_file(fn):
ci = subprocess.Popen(
["clang-format"], stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
- stdout, ign = ci.communicate(text)
+ stdout, ign = ci.communicate(text.encode("utf-8"))
ci.wait()
if ci.returncode != 0:
raise IOError("clang-format returned %d" % (ci.returncode))
# remove the bits we inserted above
- final = clean_re.sub("", stdout)
+ final = clean_re.sub("", stdout.decode("utf-8"))
tmpname = fn + ".indent"
with open(tmpname, "w") as ofd:
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 5cab10c60d..e03d1464d3 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -3423,7 +3423,7 @@ static void show_route_map_send(const char *route_map, bool json)
bool first = true;
char command_line[128];
- snprintf(command_line, sizeof(command_line), "show route-map ");
+ snprintf(command_line, sizeof(command_line), "do show route-map ");
if (route_map)
strlcat(command_line, route_map, sizeof(command_line));
if (json)
@@ -3474,6 +3474,161 @@ DEFPY (show_route_map,
return CMD_SUCCESS;
}
+static void show_prefix_list_send(afi_t afi, const char *prefix_list,
+ const char *seq, enum display_type dtype,
+ bool json)
+{
+ unsigned int i;
+ bool first = true;
+ char command_line[128];
+
+ if (afi == AFI_IP)
+ snprintf(command_line, sizeof(command_line),
+ "do show ip prefix-list ");
+ else if (afi == AFI_IP6)
+ snprintf(command_line, sizeof(command_line),
+ "do show ipv6 prefix-list ");
+ if (dtype == detail_display)
+ strlcat(command_line, "detail ", sizeof(command_line));
+ else if (dtype == summary_display)
+ strlcat(command_line, "summary ", sizeof(command_line));
+ if (prefix_list)
+ strlcat(command_line, prefix_list, sizeof(command_line));
+ if (dtype == sequential_display) {
+ strlcat(command_line, " seq ", sizeof(command_line));
+ strlcat(command_line, seq, sizeof(command_line));
+ }
+ if (json)
+ strlcat(command_line, " json", sizeof(command_line));
+
+ if (json)
+ vty_out(vty, "{");
+
+ for (i = 0; i < array_size(vtysh_client); i++) {
+ const struct vtysh_client *client = &vtysh_client[i];
+ bool is_connected = true;
+
+ if (!CHECK_FLAG(client->flag, VTYSH_PREFIX_LIST_SHOW))
+ continue;
+
+ for (; client; client = client->next)
+ if (client->fd < 0)
+ is_connected = false;
+
+ if (!is_connected)
+ continue;
+
+ if (json && !first)
+ vty_out(vty, ",");
+ else
+ first = false;
+
+ if (json)
+ vty_out(vty, "\"%s\":", vtysh_client[i].name);
+
+ vtysh_client_execute_name(vtysh_client[i].name, command_line);
+ }
+
+ if (json)
+ vty_out(vty, "}\n");
+}
+
+DEFPY (show_ip_prefix_list,
+ show_ip_prefix_list_cmd,
+ "show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
+ SHOW_STR
+ IP_STR
+ PREFIX_LIST_STR
+ "Name of a prefix list\n"
+ "sequence number of an entry\n"
+ "Sequence number\n"
+ JSON_STR)
+{
+ enum display_type dtype = normal_display;
+
+ if (dseq)
+ dtype = sequential_display;
+
+ show_prefix_list_send(AFI_IP, name, arg_str, dtype, !!uj);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ip_prefix_list_summary,
+ show_ip_prefix_list_summary_cmd,
+ "show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]",
+ SHOW_STR
+ IP_STR
+ PREFIX_LIST_STR
+ "Summary of prefix lists\n"
+ "Name of a prefix list\n"
+ JSON_STR)
+{
+ show_prefix_list_send(AFI_IP, name, NULL, summary_display, !!uj);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ip_prefix_list_detail,
+ show_ip_prefix_list_detail_cmd,
+ "show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]",
+ SHOW_STR
+ IP_STR
+ PREFIX_LIST_STR
+ "Detail of prefix lists\n"
+ "Name of a prefix list\n"
+ JSON_STR)
+{
+ show_prefix_list_send(AFI_IP, name, NULL, detail_display, !!uj);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ipv6_prefix_list,
+ show_ipv6_prefix_list_cmd,
+ "show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]",
+ SHOW_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ "Name of a prefix list\n"
+ "sequence number of an entry\n"
+ "Sequence number\n"
+ JSON_STR)
+{
+ enum display_type dtype = normal_display;
+
+ if (dseq)
+ dtype = sequential_display;
+
+ show_prefix_list_send(AFI_IP6, name, arg_str, dtype, !!uj);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ipv6_prefix_list_summary,
+ show_ipv6_prefix_list_summary_cmd,
+ "show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]",
+ SHOW_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ "Summary of prefix lists\n"
+ "Name of a prefix list\n"
+ JSON_STR)
+{
+ show_prefix_list_send(AFI_IP6, name, NULL, summary_display, !!uj);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ipv6_prefix_list_detail,
+ show_ipv6_prefix_list_detail_cmd,
+ "show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]",
+ SHOW_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ "Detail of prefix lists\n"
+ "Name of a prefix list\n"
+ JSON_STR)
+{
+ show_prefix_list_send(AFI_IP6, name, NULL, detail_display, !!uj);
+ return CMD_SUCCESS;
+}
+
DEFUN (vtysh_integrated_config,
vtysh_integrated_config_cmd,
"service integrated-vtysh-config",
@@ -3498,8 +3653,8 @@ DEFUN (no_vtysh_integrated_config,
static void backup_config_file(const char *fbackup)
{
char *integrate_sav = NULL;
-
size_t integrate_sav_sz = strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1;
+
integrate_sav = malloc(integrate_sav_sz);
strlcpy(integrate_sav, fbackup, integrate_sav_sz);
strlcat(integrate_sav, CONF_BACKUP_EXT, integrate_sav_sz);
@@ -4999,11 +5154,13 @@ void vtysh_init_vty(void)
install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
install_element(VRF_NODE, &vtysh_quit_vrf_cmd);
+#ifdef HAVE_BGPD
install_node(&rpki_vrf_node);
install_element(VRF_NODE, &rpki_cmd);
install_element(RPKI_VRF_NODE, &rpki_exit_cmd);
install_element(RPKI_VRF_NODE, &rpki_quit_cmd);
install_element(RPKI_VRF_NODE, &vtysh_end_all_cmd);
+#endif
install_element(CONFIG_NODE, &vtysh_affinity_map_cmd);
install_element(CONFIG_NODE, &vtysh_no_affinity_map_cmd);
@@ -5072,6 +5229,12 @@ void vtysh_init_vty(void)
install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd);
install_element(ENABLE_NODE, &show_route_map_cmd);
+ install_element(ENABLE_NODE, &show_ip_prefix_list_cmd);
+ install_element(ENABLE_NODE, &show_ip_prefix_list_summary_cmd);
+ install_element(ENABLE_NODE, &show_ip_prefix_list_detail_cmd);
+ install_element(ENABLE_NODE, &show_ipv6_prefix_list_cmd);
+ install_element(ENABLE_NODE, &show_ipv6_prefix_list_summary_cmd);
+ install_element(ENABLE_NODE, &show_ipv6_prefix_list_detail_cmd);
/* "write terminal" command. */
install_element(ENABLE_NODE, &vtysh_write_terminal_cmd);
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index b35d56672a..131fbef8ba 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -68,6 +68,10 @@ extern struct event_loop *master;
VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \
VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \
VTYSH_FABRICD
+#define VTYSH_PREFIX_LIST_SHOW \
+ VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \
+ VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | VTYSH_EIGRPD | \
+ VTYSH_FABRICD
#define VTYSH_INTERFACE_SUBSET \
VTYSH_OSPFD | VTYSH_OSPF6D | \
VTYSH_ISISD | VTYSH_PIMD | VTYSH_PIM6D | VTYSH_NHRPD | \
@@ -90,7 +94,17 @@ enum vtysh_write_integrated {
WRITE_INTEGRATED_YES
};
+enum display_type {
+ normal_display,
+ summary_display,
+ detail_display,
+ sequential_display,
+ longer_display,
+ first_match_display
+};
+
extern enum vtysh_write_integrated vtysh_write_integrated;
+extern enum display_type display_type;
extern char frr_config[];
extern char vtydir[];
diff --git a/yang/confd/confd.frr-ripd.yang b/yang/confd/confd.frr-ripd.yang
deleted file mode 100644
index 7bbe54cca9..0000000000
--- a/yang/confd/confd.frr-ripd.yang
+++ /dev/null
@@ -1,24 +0,0 @@
-module confd.frr-ripd {
- namespace "urn:dummy";
- prefix "dummy";
-
- import tailf-common {
- prefix tailf;
- }
- import frr-ripd {
- prefix frr-ripd;
- }
-
- tailf:annotate-module "frr-ripd" {
- tailf:annotate-statement "container[name='ripd']" {
- tailf:annotate-statement "list[name='instance']" {
- tailf:annotate-statement "container[name='state']" {
- tailf:callpoint "state";
- }
- }
- }
- tailf:annotate-statement "rpc[name='clear-rip-route']" {
- tailf:actionpoint "actionpoint";
- }
- }
-}
diff --git a/yang/confd/confd.frr-ripngd.yang b/yang/confd/confd.frr-ripngd.yang
deleted file mode 100644
index 83383fb454..0000000000
--- a/yang/confd/confd.frr-ripngd.yang
+++ /dev/null
@@ -1,24 +0,0 @@
-module confd.frr-ripngd {
- namespace "urn:dummy";
- prefix "dummy";
-
- import tailf-common {
- prefix tailf;
- }
- import frr-ripngd {
- prefix frr-ripngd;
- }
-
- tailf:annotate-module "frr-ripngd" {
- tailf:annotate-statement "container[name='ripngd']" {
- tailf:annotate-statement "list[name='instance']" {
- tailf:annotate-statement "container[name='state']" {
- tailf:callpoint "state";
- }
- }
- }
- tailf:annotate-statement "rpc[name='clear-ripng-route']" {
- tailf:actionpoint "actionpoint";
- }
- }
-}
diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang
index f1a69068c3..79c524a40a 100644
--- a/yang/frr-zebra.yang
+++ b/yang/frr-zebra.yang
@@ -151,6 +151,12 @@ module frr-zebra {
"Zebra interface type macvlan.";
}
+ identity zif-gre {
+ base zebra-interface-type;
+ description
+ "Zebra interface type gre.";
+ }
+
/*
* Multicast RPF mode configurable type
*/
diff --git a/yang/subdir.am b/yang/subdir.am
index 6745d53712..71aa040878 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -106,31 +106,4 @@ endif
CLEANFILES += \
yang/*.c \
yang/ietf/*.c \
- yang/confd/*.c \
#
-
-if CONFD
-
-SUBMODULES = $(shell cd $(top_srcdir); grep -l belongs-to $(dist_yangmodels_DATA))
-EXCLUDED_MODULES = $(SUBMODULES) yang/frr-module-translator.yang
-YANG_MODULES = $(filter-out $(EXCLUDED_MODULES),$(dist_yangmodels_DATA))
-
-fxsdir = $(sysconfdir)/confd
-fxs_DATA = $(YANG_MODULES:.yang=.fxs)
-
-SUFFIXES += .fxs
-CLEANFILES += $(fxs_DATA)
-
-AM_V_CONFDC = $(AM_V_CONFDC_@AM_V@)
-AM_V_CONFDC_ = $(AM_V_CONFDC_@AM_DEFAULT_V@)
-AM_V_CONFDC_0 = @echo " CONFDC " $@;
-
-CONFDC_FLAGS = --yangpath $(srcdir)/yang --yangpath $(srcdir)/yang/ietf
-
-yang/%.fxs: yang/%.yang yang/confd/confd.%.yang
- $(AM_V_CONFDC)$(CONFDC) $(CONFDC_FLAGS) -c -o $@ -a $(srcdir)/yang/confd/confd.$*.yang -- $<
-
-yang/%.fxs: yang/%.yang
- $(AM_V_CONFDC)$(CONFDC) $(CONFDC_FLAGS) -c -o $@ -- $<
-
-endif
diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c
index d50e40e9d8..5ffc0561bb 100644
--- a/zebra/fpm_listener.c
+++ b/zebra/fpm_listener.c
@@ -32,12 +32,14 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#include "rt_netlink.h"
#include "fpm/fpm.h"
#include "lib/libfrr.h"
struct glob {
int server_sock;
int sock;
+ bool reflect;
};
struct glob glob_space;
@@ -211,6 +213,12 @@ netlink_msg_type_to_s(uint16_t type)
case RTM_DELROUTE:
return "Del route";
+ case RTM_NEWNEXTHOP:
+ return "New Nexthop Group";
+
+ case RTM_DELNEXTHOP:
+ return "Del Nexthop Group";
+
default:
return "Unknown";
}
@@ -239,6 +247,39 @@ netlink_prot_to_s(unsigned char prot)
case RTPROT_DHCP:
return "Dhcp";
+ case RTPROT_BGP:
+ return "BGP";
+
+ case RTPROT_ISIS:
+ return "ISIS";
+
+ case RTPROT_OSPF:
+ return "OSPF";
+
+ case RTPROT_RIP:
+ return "RIP";
+
+ case RTPROT_RIPNG:
+ return "RIPNG";
+
+ case RTPROT_BABEL:
+ return "BABEL";
+
+ case RTPROT_NHRP:
+ return "NHRP";
+
+ case RTPROT_EIGRP:
+ return "EIGRP";
+
+ case RTPROT_SHARP:
+ return "SHARP";
+
+ case RTPROT_PBR:
+ return "PBR";
+
+ case RTPROT_ZSTATIC:
+ return "Static";
+
default:
return "Unknown";
}
@@ -269,6 +310,7 @@ struct netlink_msg_ctx {
struct rtattr *dest;
struct rtattr *src;
int *metric;
+ unsigned int *nhgid;
const char *err_msg;
};
@@ -432,6 +474,10 @@ static int parse_route_msg(struct netlink_msg_ctx *ctx)
if (rtattr)
ctx->metric = (int *)RTA_DATA(rtattr);
+ rtattr = rtattrs[RTA_NH_ID];
+ if (rtattr)
+ ctx->nhgid = (unsigned int *)RTA_DATA(rtattr);
+
gateway = rtattrs[RTA_GATEWAY];
oif = rtattrs[RTA_OIF];
if (gateway || oif) {
@@ -481,15 +527,18 @@ static int netlink_msg_ctx_snprint(struct netlink_msg_ctx *ctx, char *buf,
cur = buf;
end = buf + buf_len;
- cur += snprintf(cur, end - cur, "%s %s/%d, Prot: %s",
+ cur += snprintf(cur, end - cur, "%s %s/%d, Prot: %s(%u)",
netlink_msg_type_to_s(hdr->nlmsg_type),
addr_to_s(rtmsg->rtm_family, RTA_DATA(ctx->dest)),
rtmsg->rtm_dst_len,
- netlink_prot_to_s(rtmsg->rtm_protocol));
+ netlink_prot_to_s(rtmsg->rtm_protocol),
+ rtmsg->rtm_protocol);
if (ctx->metric)
cur += snprintf(cur, end - cur, ", Metric: %d", *ctx->metric);
+ if (ctx->nhgid)
+ cur += snprintf(cur, end - cur, ", nhgid: %u", *ctx->nhgid);
for (i = 0; i < ctx->num_nhs; i++) {
cur += snprintf(cur, end - cur, "\n ");
nh = &ctx->nhs[i];
@@ -523,8 +572,7 @@ static void print_netlink_msg_ctx(struct netlink_msg_ctx *ctx)
/*
* parse_netlink_msg
*/
-static void
-parse_netlink_msg(char *buf, size_t buf_len)
+static void parse_netlink_msg(char *buf, size_t buf_len, fpm_msg_hdr_t *fpm)
{
struct netlink_msg_ctx ctx_space, *ctx;
struct nlmsghdr *hdr;
@@ -552,11 +600,23 @@ parse_netlink_msg(char *buf, size_t buf_len)
}
print_netlink_msg_ctx(ctx);
+
+ if (glob->reflect && hdr->nlmsg_type == RTM_NEWROUTE &&
+ ctx->rtmsg->rtm_protocol > RTPROT_STATIC) {
+ printf(" Route %s(%u) reflecting back\n",
+ netlink_prot_to_s(
+ ctx->rtmsg->rtm_protocol),
+ ctx->rtmsg->rtm_protocol);
+ ctx->rtmsg->rtm_flags |= RTM_F_OFFLOAD;
+ write(glob->sock, fpm, fpm_msg_len(fpm));
+ }
break;
default:
- fprintf(stdout, "Ignoring unknown netlink message - Type: %d\n",
- hdr->nlmsg_type);
+ fprintf(stdout,
+ "Ignoring netlink message - Type: %s(%d)\n",
+ netlink_msg_type_to_s(hdr->nlmsg_type),
+ hdr->nlmsg_type);
}
}
}
@@ -574,7 +634,7 @@ static void process_fpm_msg(fpm_msg_hdr_t *hdr)
return;
}
- parse_netlink_msg(fpm_msg_data(hdr), fpm_msg_data_len(hdr));
+ parse_netlink_msg(fpm_msg_data(hdr), fpm_msg_data_len(hdr), hdr);
}
/*
@@ -598,18 +658,29 @@ static void fpm_serve(void)
int main(int argc, char **argv)
{
pid_t daemon;
- int d;
+ int r;
+ bool fork_daemon = false;
+
+ memset(glob, 0, sizeof(*glob));
- d = getopt(argc, argv, "d");
- if (d == 'd') {
+ while ((r = getopt(argc, argv, "rd")) != -1) {
+ switch (r) {
+ case 'r':
+ glob->reflect = true;
+ break;
+ case 'd':
+ fork_daemon = true;
+ break;
+ }
+ }
+
+ if (fork_daemon) {
daemon = fork();
if (daemon)
exit(0);
}
- memset(glob, 0, sizeof(*glob));
-
if (!create_listen_sock(FPM_DEFAULT_PORT, &glob->server_sock))
exit(1);
diff --git a/zebra/interface.c b/zebra/interface.c
index 8111863558..b824977f9e 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -176,6 +176,10 @@ static void if_nhg_dependents_release(const struct interface *ifp)
frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) {
rb_node_dep->nhe->ifp = NULL; /* Null it out */
zebra_nhg_check_valid(rb_node_dep->nhe);
+ if (CHECK_FLAG(rb_node_dep->nhe->flags,
+ NEXTHOP_GROUP_KEEP_AROUND) &&
+ rb_node_dep->nhe->refcnt == 1)
+ zebra_nhg_decrement_ref(rb_node_dep->nhe);
}
}
@@ -1741,6 +1745,9 @@ interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx,
vlanid_t vid;
int i;
+ if (vniarray == NULL)
+ return;
+
memset(&vni_start, 0, sizeof(vni_start));
memset(&vni_end, 0, sizeof(vni_end));
diff --git a/zebra/main.c b/zebra/main.c
index 27e98ed999..d83f1d0491 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -205,6 +205,12 @@ static void sigint(void)
list_delete(&zrouter.client_list);
list_delete(&zrouter.stale_client_list);
+ /*
+ * Besides other clean-ups zebra's vrf_disable() also enqueues installed
+ * routes for removal from the kernel, unless ZEBRA_VRF_RETAIN is set.
+ */
+ vrf_iterate(vrf_disable);
+
/* Indicate that all new dplane work has been enqueued. When that
* work is complete, the dataplane will enqueue an event
* with the 'finalize' function.
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index f092fc5c85..dde4704936 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1550,7 +1550,7 @@ static ssize_t fill_seg6ipt_encap(char *buffer, size_t buflen,
srh->first_segment = segs->num_segs - 1;
for (i = 0; i < segs->num_segs; i++) {
- memcpy(&srh->segments[i], &segs->seg[i],
+ memcpy(&srh->segments[segs->num_segs - i - 1], &segs->seg[i],
sizeof(struct in6_addr));
}
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index 6aca643bd8..470391de9b 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -184,13 +184,13 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf,
static void rtadv_send_packet(int sock, struct interface *ifp,
enum ipv6_nd_suppress_ra_status stop)
{
- struct msghdr msg;
- struct iovec iov;
+ struct msghdr msg = { 0 };
+ struct iovec iov = { 0 };
struct cmsghdr *cmsgptr;
struct in6_pktinfo *pkt;
- struct sockaddr_in6 addr;
- unsigned char buf[RTADV_MSG_SIZE];
- char adata[RTADV_ADATA_SIZE];
+ struct sockaddr_in6 addr = { 0 };
+ unsigned char buf[RTADV_MSG_SIZE] = { 0 };
+ char adata[RTADV_ADATA_SIZE] = { 0 };
struct nd_router_advert *rtadv;
int ret;
diff --git a/zebra/zebra_cli.c b/zebra/zebra_cli.c
index 00e0a49cb8..3e03d74775 100644
--- a/zebra/zebra_cli.c
+++ b/zebra/zebra_cli.c
@@ -241,7 +241,7 @@ DEFUN_YANG_NOSH (link_params,
return ret;
}
-DEFUN_NOSH (exit_link_params,
+DEFUN_YANG_NOSH (exit_link_params,
exit_link_params_cmd,
"exit-link-params",
"Exit from Link Params configuration mode\n")
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 99693a5874..06b34da209 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -39,6 +39,13 @@ DEFINE_MTYPE_STATIC(ZEBRA, DP_NS, "DPlane NSes");
# define AOK 0
#endif
+/*
+ * Dataplane API version. This must be updated when any incompatible changes
+ * are made. The minor version (at least) should be updated when new APIs
+ * are introduced.
+ */
+static uint32_t zdplane_version = MAKE_FRRVERSION(2, 0, 0);
+
/* Control for collection of extra interface info with route updates; a plugin
* can enable the extra info via a dplane api.
*/
@@ -664,6 +671,12 @@ neigh_update_internal(enum dplane_op_e op, const struct interface *ifp,
* Public APIs
*/
+/* Access the dplane API version */
+uint32_t zebra_dplane_get_version(void)
+{
+ return zdplane_version;
+}
+
/* Obtain thread_master for dataplane thread */
struct event_loop *dplane_get_thread_master(void)
{
@@ -6351,7 +6364,7 @@ dplane_provider_dequeue_out_ctx(struct zebra_dplane_provider *prov)
*/
bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
{
- return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
+ return CHECK_FLAG(prov->dp_flags, DPLANE_PROV_FLAG_THREADED);
}
#ifdef HAVE_NETLINK
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 2f7d218508..060b1c8b9e 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -24,6 +24,13 @@
extern "C" {
#endif
+/* Retrieve the dataplane API version number; see libfrr.h to decode major,
+ * minor, sub version values.
+ * Plugins should pay attention to the major version number, at least, to
+ * be able to detect API changes that may not be backward-compatible.
+ */
+uint32_t zebra_dplane_get_version(void);
+
/* Key netlink info from zebra ns */
struct zebra_dplane_info {
ns_id_t ns_id;
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
index ae9b3c49eb..ebb5a42298 100644
--- a/zebra/zebra_evpn.c
+++ b/zebra/zebra_evpn.c
@@ -351,7 +351,7 @@ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p,
if (!client)
return 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
zclient_create_header(s, cmd, vrf_id);
stream_put(s, p, sizeof(struct prefix));
@@ -1140,7 +1140,7 @@ int zebra_evpn_send_add_to_client(struct zebra_evpn *zevpn)
svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
stream_putl(s, zevpn->vni);
@@ -1192,7 +1192,7 @@ int zebra_evpn_send_del_to_client(struct zebra_evpn *zevpn)
zebra_evpn_update_all_es(zevpn);
}
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
stream_reset(s);
zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id());
diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c
index 753354df61..bfc060db61 100644
--- a/zebra/zebra_evpn_mac.c
+++ b/zebra/zebra_evpn_mac.c
@@ -933,7 +933,7 @@ int zebra_evpn_macip_send_msg_to_client(vni_t vni,
if (!client)
return 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
zclient_create_header(s, cmd, zebra_vrf_get_evpn_id());
stream_putl(s, vni);
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index 35d5027fb8..0d9d912f83 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -1935,7 +1935,7 @@ static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es)
if (!client)
return 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
zclient_create_header(s, ZEBRA_LOCAL_ES_ADD, zebra_vrf_get_evpn_id());
stream_put(s, &es->esi, sizeof(esi_t));
@@ -1971,7 +1971,7 @@ static int zebra_evpn_es_send_del_to_client(struct zebra_evpn_es *es)
if (!client)
return 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
stream_reset(s);
zclient_create_header(s, ZEBRA_LOCAL_ES_DEL, zebra_vrf_get_evpn_id());
@@ -2639,7 +2639,7 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
if (!client)
return 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_SMALL_PACKET_SIZE);
zclient_create_header(s,
add ? ZEBRA_LOCAL_ES_EVI_ADD : ZEBRA_LOCAL_ES_EVI_DEL,
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 1dd96347f3..95207ce75c 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -592,19 +592,19 @@ int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
RTM_DELNEIGH : RTM_NEWNEIGH;
req->hdr.nlmsg_flags = NLM_F_REQUEST;
if (req->hdr.nlmsg_type == RTM_NEWNEIGH)
- req->hdr.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+ SET_FLAG(req->hdr.nlmsg_flags, (NLM_F_CREATE | NLM_F_REPLACE));
/* Construct ndmsg */
req->ndm.ndm_family = AF_BRIDGE;
req->ndm.ndm_ifindex = mac->vxlan_if;
req->ndm.ndm_state = NUD_REACHABLE;
- req->ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
+ SET_FLAG(req->ndm.ndm_flags, (NTF_SELF | NTF_MASTER));
if (CHECK_FLAG(mac->zebra_flags,
(ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)))
- req->ndm.ndm_state |= NUD_NOARP;
+ SET_FLAG(req->ndm.ndm_state, NUD_NOARP);
else
- req->ndm.ndm_flags |= NTF_EXT_LEARNED;
+ SET_FLAG(req->ndm.ndm_flags, NTF_EXT_LEARNED);
/* Add attributes */
nl_attr_put(&req->hdr, in_buf_len, NDA_LLADDR, &mac->macaddr, 6);
diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c
index f4241f1d72..cee66cc055 100644
--- a/zebra/zebra_gr.c
+++ b/zebra/zebra_gr.c
@@ -298,6 +298,16 @@ struct zebra_gr_afi_clean {
* Functions to deal with capabilities
*/
+void zebra_gr_client_final_shutdown(struct zserv *client)
+{
+ struct client_gr_info *info;
+
+ while (!TAILQ_EMPTY(&client->gr_info_queue)) {
+ info = TAILQ_FIRST(&client->gr_info_queue);
+ zebra_gr_client_info_delete(client, info);
+ }
+}
+
/*
* Function to decode and call appropriate functions
* to handle client capabilities.
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
index 4f7a1cd4ce..0f591810b9 100644
--- a/zebra/zebra_l2.c
+++ b/zebra/zebra_l2.c
@@ -111,13 +111,13 @@ static void zebra_l2_bond_lacp_bypass_eval(struct zebra_if *bond_zif)
{
struct listnode *node;
struct zebra_if *bond_mbr;
- bool old_bypass = !!(bond_zif->flags & ZIF_FLAG_LACP_BYPASS);
+ bool old_bypass = !!CHECK_FLAG(bond_zif->flags, ZIF_FLAG_LACP_BYPASS);
bool new_bypass = false;
if (bond_zif->bond_info.mbr_zifs) {
for (ALL_LIST_ELEMENTS_RO(bond_zif->bond_info.mbr_zifs, node,
bond_mbr)) {
- if (bond_mbr->flags & ZIF_FLAG_LACP_BYPASS) {
+ if (CHECK_FLAG(bond_mbr->flags, ZIF_FLAG_LACP_BYPASS)) {
new_bypass = true;
break;
}
@@ -132,9 +132,9 @@ static void zebra_l2_bond_lacp_bypass_eval(struct zebra_if *bond_zif)
bond_zif->ifp->name, new_bypass ? "on" : "off");
if (new_bypass)
- bond_zif->flags |= ZIF_FLAG_LACP_BYPASS;
+ SET_FLAG(bond_zif->flags, ZIF_FLAG_LACP_BYPASS);
else
- bond_zif->flags &= ~ZIF_FLAG_LACP_BYPASS;
+ UNSET_FLAG(bond_zif->flags, ZIF_FLAG_LACP_BYPASS);
if (bond_zif->es_info.es)
zebra_evpn_es_bypass_update(bond_zif->es_info.es, bond_zif->ifp,
@@ -174,8 +174,7 @@ void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf_id)
}
} else {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("bond mbr %s link to bond skipped",
- zif->ifp->name);
+ zlog_debug("bond mbr %s link to bond skipped", zif->ifp->name);
}
}
@@ -186,8 +185,7 @@ void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif)
if (!bond_slave->bond_if) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("bond mbr %s unlink from bond skipped",
- zif->ifp->name);
+ zlog_debug("bond mbr %s unlink from bond skipped", zif->ifp->name);
return;
}
@@ -218,8 +216,7 @@ void zebra_l2if_update_bond(struct interface *ifp, bool add)
if (add) {
if (!bond->mbr_zifs) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("bond %s mbr list create",
- ifp->name);
+ zlog_debug("bond %s mbr list create", ifp->name);
bond->mbr_zifs = list_new();
}
} else {
@@ -347,7 +344,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp,
ctx.old_vtep_ip = zif->l2info.vxl.vtep_ip;
if (!IPV4_ADDR_SAME(&ctx.old_vtep_ip, &vxlan_info->vtep_ip)) {
- chgflags |= ZEBRA_VXLIF_LOCAL_IP_CHANGE;
+ SET_FLAG(chgflags, ZEBRA_VXLIF_LOCAL_IP_CHANGE);
zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip;
}
@@ -355,7 +352,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp,
ctx.old_vni = vxlan_info->vni_info.vni;
if (!IPV4_ADDR_SAME(&zif->l2info.vxl.vni_info.vni.mcast_grp,
&vxlan_info->vni_info.vni.mcast_grp)) {
- chgflags |= ZEBRA_VXLIF_MCAST_GRP_CHANGE;
+ SET_FLAG(chgflags, ZEBRA_VXLIF_MCAST_GRP_CHANGE);
zif->l2info.vxl.vni_info.vni.mcast_grp =
vxlan_info->vni_info.vni.mcast_grp;
}
@@ -441,11 +438,11 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp,
if (zif->zif_type == ZEBRA_IF_VXLAN
&& chgflags != ZEBRA_BRIDGE_NO_ACTION) {
- if (chgflags & ZEBRA_BRIDGE_MASTER_MAC_CHANGE) {
+ if (CHECK_FLAG(chgflags, ZEBRA_BRIDGE_MASTER_MAC_CHANGE)) {
ctx.chgflags = ZEBRA_VXLIF_MASTER_MAC_CHANGE;
zebra_vxlan_if_update(ifp, &ctx);
}
- if (chgflags & ZEBRA_BRIDGE_MASTER_UP) {
+ if (CHECK_FLAG(chgflags, ZEBRA_BRIDGE_MASTER_UP)) {
ctx.chgflags = ZEBRA_VXLIF_MASTER_CHANGE;
zebra_vxlan_if_update(ifp, &ctx);
}
@@ -494,16 +491,16 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex,
zif = ifp->info;
assert(zif);
- old_bypass = !!(zif->flags & ZIF_FLAG_LACP_BYPASS);
+ old_bypass = !!CHECK_FLAG(zif->flags, ZIF_FLAG_LACP_BYPASS);
if (old_bypass != new_bypass) {
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_EVENT)
zlog_debug("bond-mbr %s lacp bypass changed to %s",
zif->ifp->name, new_bypass ? "on" : "off");
if (new_bypass)
- zif->flags |= ZIF_FLAG_LACP_BYPASS;
+ SET_FLAG(zif->flags, ZIF_FLAG_LACP_BYPASS);
else
- zif->flags &= ~ZIF_FLAG_LACP_BYPASS;
+ UNSET_FLAG(zif->flags, ZIF_FLAG_LACP_BYPASS);
bond_mbr = &zif->bondslave_info;
if (bond_mbr->bond_if) {
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 15e36acda8..4cc85d461f 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -329,7 +329,7 @@ static void fec_evaluate(struct zebra_vrf *zvrf)
/* Skip configured FECs and those without a label index.
*/
- if (fec->flags & FEC_FLAG_CONFIGURED
+ if (CHECK_FLAG(fec->flags, FEC_FLAG_CONFIGURED)
|| fec->label_index == MPLS_INVALID_LABEL_INDEX)
continue;
@@ -2291,7 +2291,7 @@ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p,
new_client = true;
} else {
/* Check if the FEC has been statically defined in the config */
- is_configured_fec = fec->flags & FEC_FLAG_CONFIGURED;
+ is_configured_fec = CHECK_FLAG(fec->flags, FEC_FLAG_CONFIGURED);
/* Client may register same FEC with different label index. */
new_client =
(listnode_lookup(fec->client_list, client) == NULL);
@@ -2382,8 +2382,8 @@ int zebra_mpls_fec_unregister(struct zebra_vrf *zvrf, struct prefix *p,
/* If not a configured entry, delete the FEC if no other clients. Before
* deleting, see if any LSP needs to be uninstalled.
*/
- if (!(fec->flags & FEC_FLAG_CONFIGURED)
- && list_isempty(fec->client_list)) {
+ if (!CHECK_FLAG(fec->flags, FEC_FLAG_CONFIGURED) &&
+ list_isempty(fec->client_list)) {
mpls_label_t old_label = fec->label;
fec->label = MPLS_INVALID_LABEL; /* reset */
fec_change_update_lsp(zvrf, fec, old_label);
@@ -2420,7 +2420,7 @@ static int zebra_mpls_cleanup_fecs_for_client(struct zserv *client)
if (fec_client == client) {
listnode_delete(fec->client_list,
fec_client);
- if (!(fec->flags & FEC_FLAG_CONFIGURED)
+ if (!CHECK_FLAG(fec->flags, FEC_FLAG_CONFIGURED)
&& list_isempty(fec->client_list))
fec_del(fec);
break;
@@ -2476,7 +2476,7 @@ static int zebra_mpls_cleanup_zclient_labels(struct zserv *client)
* hash..
*/
struct zebra_fec *zebra_mpls_fec_for_label(struct zebra_vrf *zvrf,
- mpls_label_t label)
+ struct prefix *p, mpls_label_t label)
{
struct route_node *rn;
struct zebra_fec *fec;
@@ -2491,8 +2491,11 @@ struct zebra_fec *zebra_mpls_fec_for_label(struct zebra_vrf *zvrf,
if (!rn->info)
continue;
fec = rn->info;
- if (fec->label == label)
+ if (fec->label == label) {
+ if (p && prefix_same(p, &rn->p))
+ return NULL;
return fec;
+ }
}
}
@@ -2502,9 +2505,10 @@ struct zebra_fec *zebra_mpls_fec_for_label(struct zebra_vrf *zvrf,
/*
* Inform if specified label is currently bound to a FEC or not.
*/
-int zebra_mpls_label_already_bound(struct zebra_vrf *zvrf, mpls_label_t label)
+int zebra_mpls_label_already_bound(struct zebra_vrf *zvrf, struct prefix *p,
+ mpls_label_t label)
{
- return (zebra_mpls_fec_for_label(zvrf, label) ? 1 : 0);
+ return (zebra_mpls_fec_for_label(zvrf, p, label) ? 1 : 0);
}
/*
@@ -2538,7 +2542,7 @@ int zebra_mpls_static_fec_add(struct zebra_vrf *zvrf, struct prefix *p,
if (IS_ZEBRA_DEBUG_MPLS)
zlog_debug("Add fec %pFX label %u", p, in_label);
} else {
- fec->flags |= FEC_FLAG_CONFIGURED;
+ SET_FLAG(fec->flags, FEC_FLAG_CONFIGURED);
if (fec->label == in_label)
/* Duplicate config */
return 0;
@@ -2587,7 +2591,7 @@ int zebra_mpls_static_fec_del(struct zebra_vrf *zvrf, struct prefix *p)
}
old_label = fec->label;
- fec->flags &= ~FEC_FLAG_CONFIGURED;
+ UNSET_FLAG(fec->flags, FEC_FLAG_CONFIGURED);
fec->label = MPLS_INVALID_LABEL;
/* If no client exists, just delete the FEC. */
@@ -2630,7 +2634,7 @@ int zebra_mpls_write_fec_config(struct vty *vty, struct zebra_vrf *zvrf)
char lstr[BUFSIZ];
fec = rn->info;
- if (!(fec->flags & FEC_FLAG_CONFIGURED))
+ if (!CHECK_FLAG(fec->flags, FEC_FLAG_CONFIGURED))
continue;
write = 1;
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 1ed2f9b41c..dd6f960146 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -203,12 +203,13 @@ int zebra_mpls_fec_unregister(struct zebra_vrf *zvrf, struct prefix *p,
* hash..
*/
struct zebra_fec *zebra_mpls_fec_for_label(struct zebra_vrf *zvrf,
- mpls_label_t label);
+ struct prefix *p, mpls_label_t label);
/*
* Inform if specified label is currently bound to a FEC or not.
*/
-int zebra_mpls_label_already_bound(struct zebra_vrf *zvrf, mpls_label_t label);
+int zebra_mpls_label_already_bound(struct zebra_vrf *zvrf, struct prefix *p,
+ mpls_label_t label);
/*
* Add static FEC to label binding. If there are clients registered for this
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index 9cbe6a2e70..85a53dd4c5 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -64,8 +64,8 @@ static int kernel_send_rtmsg_v4(int action, mpls_label_t in_label,
sa_label_in.smpls_family = AF_MPLS;
sa_label_in.smpls_label = htonl(in_label << MPLS_LABEL_OFFSET);
/* adjust header */
- hdr.rtm_flags |= RTF_MPLS | RTF_MPATH;
- hdr.rtm_addrs |= RTA_DST;
+ SET_FLAG(hdr.rtm_flags, (RTF_MPLS | RTF_MPATH));
+ SET_FLAG(hdr.rtm_addrs, RTA_DST);
hdr.rtm_msglen += sizeof(sa_label_in);
/* adjust iovec */
iov[iovcnt].iov_base = &sa_label_in;
@@ -77,8 +77,8 @@ static int kernel_send_rtmsg_v4(int action, mpls_label_t in_label,
nexthop.sin_family = AF_INET;
nexthop.sin_addr = nhlfe->nexthop->gate.ipv4;
/* adjust header */
- hdr.rtm_flags |= RTF_GATEWAY;
- hdr.rtm_addrs |= RTA_GATEWAY;
+ SET_FLAG(hdr.rtm_flags, RTF_GATEWAY);
+ SET_FLAG(hdr.rtm_addrs, RTA_GATEWAY);
hdr.rtm_msglen += sizeof(nexthop);
/* adjust iovec */
iov[iovcnt].iov_base = &nexthop;
@@ -93,8 +93,8 @@ static int kernel_send_rtmsg_v4(int action, mpls_label_t in_label,
htonl(nhlfe->nexthop->nh_label->label[0]
<< MPLS_LABEL_OFFSET);
/* adjust header */
- hdr.rtm_addrs |= RTA_SRC;
- hdr.rtm_flags |= RTF_MPLS;
+ SET_FLAG(hdr.rtm_addrs, RTA_SRC);
+ SET_FLAG(hdr.rtm_flags, RTF_MPLS);
hdr.rtm_msglen += sizeof(sa_label_out);
/* adjust iovec */
iov[iovcnt].iov_base = &sa_label_out;
@@ -159,8 +159,8 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label,
sa_label_in.smpls_family = AF_MPLS;
sa_label_in.smpls_label = htonl(in_label << MPLS_LABEL_OFFSET);
/* adjust header */
- hdr.rtm_flags |= RTF_MPLS | RTF_MPATH;
- hdr.rtm_addrs |= RTA_DST;
+ SET_FLAG(hdr.rtm_flags, (RTF_MPLS | RTF_MPATH));
+ SET_FLAG(hdr.rtm_addrs, RTA_DST);
hdr.rtm_msglen += sizeof(sa_label_in);
/* adjust iovec */
iov[iovcnt].iov_base = &sa_label_in;
@@ -184,8 +184,8 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label,
}
/* adjust header */
- hdr.rtm_flags |= RTF_GATEWAY;
- hdr.rtm_addrs |= RTA_GATEWAY;
+ SET_FLAG(hdr.rtm_flags, RTF_GATEWAY);
+ SET_FLAG(hdr.rtm_addrs, RTA_GATEWAY);
hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
/* adjust iovec */
iov[iovcnt].iov_base = &nexthop;
@@ -200,8 +200,8 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label,
htonl(nhlfe->nexthop->nh_label->label[0]
<< MPLS_LABEL_OFFSET);
/* adjust header */
- hdr.rtm_addrs |= RTA_SRC;
- hdr.rtm_flags |= RTF_MPLS;
+ SET_FLAG(hdr.rtm_addrs, RTA_SRC);
+ SET_FLAG(hdr.rtm_flags, RTF_MPLS);
hdr.rtm_msglen += sizeof(sa_label_out);
/* adjust iovec */
iov[iovcnt].iov_base = &sa_label_out;
@@ -324,8 +324,8 @@ static enum zebra_dplane_result kmpw_install(struct zebra_dplane_ctx *ctx)
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
- if (dplane_ctx_get_pw_flags(ctx) & F_PSEUDOWIRE_CWORD)
- imr.imr_flags |= IMR_FLAG_CONTROLWORD;
+ if (CHECK_FLAG(dplane_ctx_get_pw_flags(ctx), F_PSEUDOWIRE_CWORD))
+ SET_FLAG(imr.imr_flags, IMR_FLAG_CONTROLWORD);
/* pseudowire nexthop */
memset(&ss, 0, sizeof(ss));
diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c
index fd09e6b444..8248d4a555 100644
--- a/zebra/zebra_mpls_vty.c
+++ b/zebra/zebra_mpls_vty.c
@@ -210,7 +210,7 @@ static int zebra_mpls_bind(struct vty *vty, int add_cmd, const char *prefix,
vty_out(vty, "%% Invalid label\n");
return CMD_WARNING_CONFIG_FAILED;
}
- if (zebra_mpls_label_already_bound(zvrf, label)) {
+ if (zebra_mpls_label_already_bound(zvrf, &p, label)) {
vty_out(vty,
"%% Label already bound to a FEC\n");
return CMD_WARNING_CONFIG_FAILED;
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index 5cb9985ee4..ae6232a1bb 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -3102,7 +3102,7 @@ int lib_interface_zebra_ipv6_router_advertisements_rdnss_rdnss_address_create(
struct nb_cb_create_args *args)
{
struct interface *ifp;
- struct rtadv_rdnss rdnss = {0}, *p;
+ struct rtadv_rdnss rdnss = {{{{0}}}}, *p;
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -3181,12 +3181,13 @@ int lib_interface_zebra_ipv6_router_advertisements_dnssl_dnssl_domain_create(
struct nb_cb_create_args *args)
{
struct interface *ifp;
- struct rtadv_dnssl dnssl = {0}, *p;
+ struct rtadv_dnssl dnssl = {{0}}, *p;
int ret;
strlcpy(dnssl.name, yang_dnode_get_string(args->dnode, "domain"),
sizeof(dnssl.name));
ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name);
+ dnssl.encoded_len = ret;
if (args->event == NB_EV_VALIDATE) {
if (ret < 0) {
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
index 46492f023a..63ac7877d0 100644
--- a/zebra/zebra_nb_state.c
+++ b/zebra/zebra_nb_state.c
@@ -55,6 +55,10 @@ lib_interface_zebra_state_zif_type_get_elem(struct nb_cb_get_elem_args *args)
zebra_if = ifp->info;
+ /*
+ * NOTE: when adding a new type to the switch, make sure it is defined
+ * in it's YANG model.
+ */
switch (zebra_if->zif_type) {
case ZEBRA_IF_OTHER:
type = "frr-zebra:zif-other";
diff --git a/zebra/zebra_neigh.c b/zebra/zebra_neigh.c
index 941088afd6..a222e7f6e8 100644
--- a/zebra/zebra_neigh.c
+++ b/zebra/zebra_neigh.c
@@ -83,7 +83,7 @@ zebra_neigh_new(ifindex_t ifindex, struct ipaddr *ip, struct ethaddr *mac)
n->ifindex = ifindex;
if (mac) {
memcpy(&n->mac, mac, sizeof(*mac));
- n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ENT_ACTIVE);
}
/* Add to rb_tree */
@@ -118,10 +118,8 @@ static void zebra_neigh_free(struct zebra_neigh_ent *n)
/* if rules are still using the neigh mark it as inactive and
* update the dataplane
*/
- if (n->flags & ZEBRA_NEIGH_ENT_ACTIVE) {
- n->flags &= ~ZEBRA_NEIGH_ENT_ACTIVE;
- memset(&n->mac, 0, sizeof(n->mac));
- }
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ENT_ACTIVE);
+ memset(&n->mac, 0, sizeof(n->mac));
zebra_neigh_pbr_rules_update(n);
return;
}
@@ -181,7 +179,7 @@ void zebra_neigh_add(struct interface *ifp, struct ipaddr *ip,
return;
memcpy(&n->mac, mac, sizeof(*mac));
- n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ENT_ACTIVE);
/* update rules linked to the neigh */
zebra_neigh_pbr_rules_update(n);
@@ -201,7 +199,7 @@ void zebra_neigh_deref(struct zebra_pbr_rule *rule)
rule->action.neigh = NULL;
/* remove rule from the list and free if it is inactive */
list_delete_node(n->pbr_rule_list, &rule->action.neigh_listnode);
- if (!(n->flags & ZEBRA_NEIGH_ENT_ACTIVE))
+ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ENT_ACTIVE))
zebra_neigh_free(n);
}
diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c
index 1af3a3e857..4cee3b89f1 100644
--- a/zebra/zebra_netns_id.c
+++ b/zebra/zebra_netns_id.c
@@ -42,7 +42,7 @@
#define NETLINK_SOCKET_BUFFER_SIZE 512
#define NETLINK_ALIGNTO 4
#define NETLINK_ALIGN(len) \
- (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1))
+ CHECK_FLAG(((len) + NETLINK_ALIGNTO - 1), ~(NETLINK_ALIGNTO - 1))
#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b)
#endif /* defined(HAVE_NETLINK) */
@@ -66,7 +66,7 @@ static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
nlh->nlmsg_type = type;
nlh->nlmsg_flags = NLM_F_REQUEST;
if (type == RTM_NEWNSID)
- nlh->nlmsg_flags |= NLM_F_ACK;
+ SET_FLAG(nlh->nlmsg_flags, NLM_F_ACK);
nlh->nlmsg_seq = *seq = frr_sequence32_next();
return nlh;
}
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
index 1bb1292e34..617a2225f8 100644
--- a/zebra/zebra_netns_notify.c
+++ b/zebra/zebra_netns_notify.c
@@ -304,7 +304,7 @@ static void zebra_ns_notify_read(struct event *t)
char *netnspath;
struct zebra_netns_info *netnsinfo;
- if (!(event->mask & (IN_CREATE | IN_DELETE)))
+ if (!CHECK_FLAG(event->mask, (IN_CREATE | IN_DELETE)))
continue;
if (offsetof(struct inotify_event, name) + event->len
@@ -350,7 +350,7 @@ static void zebra_ns_notify_read(struct event *t)
memcpy(event_name, event->name, event->len);
event_name[event->len - 1] = 0;
- if (event->mask & IN_DELETE) {
+ if (CHECK_FLAG(event->mask, IN_DELETE)) {
zebra_ns_delete(event_name);
continue;
}
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 934b8ba0db..ff97a11c17 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -1795,8 +1795,8 @@ static struct nexthop *nexthop_set_resolved(afi_t afi,
break;
}
- if (newhop->flags & NEXTHOP_FLAG_ONLINK)
- resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+ if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_ONLINK))
+ SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ONLINK);
/* Copy labels of the resolved route and the parent resolving to it */
if (policy) {
diff --git a/zebra/zebra_routemap_nb_config.c b/zebra/zebra_routemap_nb_config.c
index 5bcfb720e1..ad012da4c2 100644
--- a/zebra/zebra_routemap_nb_config.c
+++ b/zebra/zebra_routemap_nb_config.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#include <zebra.h>
#include "lib/command.h"
diff --git a/zebra/zebra_trace.c b/zebra/zebra_trace.c
index fef5ad20ac..7b0fb3279b 100644
--- a/zebra/zebra_trace.c
+++ b/zebra/zebra_trace.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#define TRACEPOINT_CREATE_PROBES
#define TRACEPOINT_DEFINE
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index da6e2069ff..0b5362094e 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -60,18 +60,19 @@ struct route_show_ctx {
};
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
- safi_t safi, bool use_fib, bool use_json,
- route_tag_t tag,
+ safi_t safi, bool use_fib, json_object *vrf_json,
+ bool use_json, route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
unsigned short ospf_instance_id, uint32_t tableid,
bool show_ng, struct route_show_ctx *ctx);
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, bool use_json);
+static void vty_show_ip_route_summary(struct vty *vty, struct route_table *table,
+ json_object *vrf_json, bool use_json);
static void vty_show_ip_route_summary_prefix(struct vty *vty,
struct route_table *table,
+ json_object *vrf_json,
bool use_json);
/* Helper api to format a nexthop in the 'detailed' output path. */
static void show_nexthop_detail_helper(struct vty *vty,
@@ -148,8 +149,8 @@ DEFPY (show_ip_rpf,
};
return do_show_ip_route(vty, VRF_DEFAULT_NAME, ip ? AFI_IP : AFI_IP6,
- SAFI_MULTICAST, false, uj, 0, NULL, false, 0, 0,
- 0, false, &ctx);
+ SAFI_MULTICAST, false, NULL, uj, 0, NULL, false,
+ 0, 0, 0, false, &ctx);
}
DEFPY (show_ip_rpf_addr,
@@ -856,14 +857,13 @@ static void vty_show_ip_route_detail_json(struct vty *vty,
vty_json(vty, json);
}
-static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
- struct route_table *table, afi_t afi,
- bool use_fib, route_tag_t tag,
- const struct prefix *longer_prefix_p,
- bool supernets_only, int type,
- unsigned short ospf_instance_id, bool use_json,
- uint32_t tableid, bool show_ng,
- struct route_show_ctx *ctx)
+static void
+do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
+ struct route_table *table, afi_t afi, bool use_fib,
+ json_object *vrf_json, route_tag_t tag,
+ const struct prefix *longer_prefix_p, bool supernets_only,
+ int type, unsigned short ospf_instance_id, bool use_json,
+ uint32_t tableid, bool show_ng, struct route_show_ctx *ctx)
{
struct route_node *rn;
struct route_entry *re;
@@ -885,7 +885,7 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
* => display the VRF and table if specific
*/
- if (use_json)
+ if (use_json && !vrf_json)
json = json_object_new_object();
/* Show all routes. */
@@ -960,7 +960,11 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
if (json_prefix) {
prefix2str(&rn->p, buf, sizeof(buf));
- json_object_object_add(json, buf, json_prefix);
+ if (!vrf_json)
+ json_object_object_add(json, buf, json_prefix);
+ else
+ json_object_object_add(vrf_json, buf,
+ json_prefix);
json_prefix = NULL;
}
}
@@ -969,13 +973,15 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
* This is an extremely expensive operation at scale
* and non-pretty reduces memory footprint significantly.
*/
- if (use_json)
+ if (use_json && !vrf_json) {
vty_json_no_pretty(vty, json);
+ json = NULL;
+ }
}
static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
- afi_t afi, bool use_fib, bool use_json,
- route_tag_t tag,
+ afi_t afi, bool use_fib, json_object *vrf_json,
+ bool use_json, route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
unsigned short ospf_instance_id, bool show_ng,
@@ -995,15 +1001,15 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf,
continue;
do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
- use_fib, use_json, tag, longer_prefix_p,
- supernets_only, type, ospf_instance_id,
- zrt->tableid, show_ng, ctx);
+ use_fib, vrf_json, use_json, tag,
+ longer_prefix_p, supernets_only, type,
+ ospf_instance_id, zrt->tableid, show_ng, ctx);
}
}
static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
- safi_t safi, bool use_fib, bool use_json,
- route_tag_t tag,
+ safi_t safi, bool use_fib, json_object *vrf_json,
+ bool use_json, route_tag_t tag,
const struct prefix *longer_prefix_p,
bool supernets_only, int type,
unsigned short ospf_instance_id, uint32_t tableid,
@@ -1038,7 +1044,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
return CMD_SUCCESS;
}
- do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
+ do_show_route_helper(vty, zvrf, table, afi, use_fib, vrf_json, tag,
longer_prefix_p, supernets_only, type,
ospf_instance_id, use_json, tableid, show_ng, ctx);
@@ -1741,6 +1747,7 @@ DEFPY (show_route,
struct route_show_ctx ctx = {
.multi = vrf_all || table_all,
};
+ json_object *root_json = NULL;
if (!vrf_is_backend_netns()) {
if ((vrf_all || vrf_name) && (table || table_all)) {
@@ -1762,24 +1769,42 @@ DEFPY (show_route,
}
if (vrf_all) {
+ if (!!json)
+ root_json = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ json_object *vrf_json = NULL;
+
if ((zvrf = vrf->info) == NULL
|| (zvrf->table[afi][SAFI_UNICAST] == NULL))
continue;
+ if (!!json)
+ vrf_json = json_object_new_object();
+
if (table_all)
- do_show_ip_route_all(
- vty, zvrf, afi, !!fib, !!json, tag,
- prefix_str ? prefix : NULL,
- !!supernets_only, type,
- ospf_instance_id, !!ng, &ctx);
+ do_show_ip_route_all(vty, zvrf, afi, !!fib,
+ vrf_json, !!json, tag,
+ prefix_str ? prefix : NULL,
+ !!supernets_only, type,
+ ospf_instance_id, !!ng,
+ &ctx);
else
- do_show_ip_route(
- vty, zvrf_name(zvrf), afi, SAFI_UNICAST,
- !!fib, !!json, tag,
- prefix_str ? prefix : NULL,
- !!supernets_only, type,
- ospf_instance_id, table, !!ng, &ctx);
+ do_show_ip_route(vty, zvrf_name(zvrf), afi,
+ SAFI_UNICAST, !!fib, vrf_json,
+ !!json, tag,
+ prefix_str ? prefix : NULL,
+ !!supernets_only, type,
+ ospf_instance_id, table, !!ng,
+ &ctx);
+
+ if (!!json)
+ json_object_object_add(root_json,
+ zvrf_name(zvrf),
+ vrf_json);
+ }
+ if (!!json) {
+ vty_json_no_pretty(vty, root_json);
+ root_json = NULL;
}
} else {
vrf_id_t vrf_id = VRF_DEFAULT;
@@ -1795,13 +1820,13 @@ DEFPY (show_route,
return CMD_SUCCESS;
if (table_all)
- do_show_ip_route_all(vty, zvrf, afi, !!fib, !!json, tag,
- prefix_str ? prefix : NULL,
+ do_show_ip_route_all(vty, zvrf, afi, !!fib, NULL, !!json,
+ tag, prefix_str ? prefix : NULL,
!!supernets_only, type,
ospf_instance_id, !!ng, &ctx);
else
do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST,
- !!fib, !!json, tag,
+ !!fib, NULL, !!json, tag,
prefix_str ? prefix : NULL,
!!supernets_only, type,
ospf_instance_id, table, !!ng, &ctx);
@@ -1973,11 +1998,15 @@ DEFPY (show_route_summary,
afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
struct route_table *table;
bool uj = use_json(argc, argv);
+ json_object *vrf_json = NULL;
if (vrf_all) {
struct vrf *vrf;
struct zebra_vrf *zvrf;
+ if (uj && !vrf_json)
+ vrf_json = json_object_new_object();
+
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if ((zvrf = vrf->info) == NULL)
continue;
@@ -1995,10 +2024,14 @@ DEFPY (show_route_summary,
if (prefix)
vty_show_ip_route_summary_prefix(vty, table,
- uj);
+ vrf_json, uj);
else
- vty_show_ip_route_summary(vty, table, uj);
+ vty_show_ip_route_summary(vty, table, vrf_json,
+ uj);
}
+
+ if (uj)
+ vty_json(vty, vrf_json);
} else {
vrf_id_t vrf_id = VRF_DEFAULT;
@@ -2014,9 +2047,9 @@ DEFPY (show_route_summary,
return CMD_SUCCESS;
if (prefix)
- vty_show_ip_route_summary_prefix(vty, table, uj);
+ vty_show_ip_route_summary_prefix(vty, table, NULL, uj);
else
- vty_show_ip_route_summary(vty, table, uj);
+ vty_show_ip_route_summary(vty, table, NULL, uj);
}
return CMD_SUCCESS;
@@ -2222,8 +2255,8 @@ static void show_ip_route_dump_vty(struct vty *vty, struct route_table *table)
}
}
-static void vty_show_ip_route_summary(struct vty *vty,
- struct route_table *table, bool use_json)
+static void vty_show_ip_route_summary(struct vty *vty, struct route_table *table,
+ json_object *vrf_json, bool use_json)
{
struct route_node *rn;
struct route_entry *re;
@@ -2237,6 +2270,8 @@ static void vty_show_ip_route_summary(struct vty *vty,
uint32_t is_ibgp;
json_object *json_route_summary = NULL;
json_object *json_route_routes = NULL;
+ const char *vrf_name = zvrf_name(
+ ((struct rib_table_info *)route_table_get_info(table))->zvrf);
memset(&rib_cnt, 0, sizeof(rib_cnt));
memset(&fib_cnt, 0, sizeof(fib_cnt));
@@ -2287,10 +2322,7 @@ static void vty_show_ip_route_summary(struct vty *vty,
if (!use_json)
vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source",
- "Routes", "FIB",
- zvrf_name(((struct rib_table_info *)
- route_table_get_info(table))
- ->zvrf));
+ "Routes", "FIB", vrf_name);
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
if ((rib_cnt[i] > 0) || (i == ZEBRA_ROUTE_BGP
@@ -2382,7 +2414,11 @@ static void vty_show_ip_route_summary(struct vty *vty,
json_object_int_add(json_route_summary, "routesTotalFib",
fib_cnt[ZEBRA_ROUTE_TOTAL]);
- vty_json(vty, json_route_summary);
+ if (!vrf_json)
+ vty_json(vty, json_route_summary);
+ else
+ json_object_object_add(vrf_json, vrf_name,
+ json_route_summary);
} else {
vty_out(vty, "------\n");
vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
@@ -2400,6 +2436,7 @@ 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,
+ json_object *vrf_json,
bool use_json)
{
struct route_node *rn;
@@ -2413,6 +2450,8 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
int cnt;
json_object *json_route_summary = NULL;
json_object *json_route_routes = NULL;
+ const char *vrf_name = zvrf_name(
+ ((struct rib_table_info *)route_table_get_info(table))->zvrf);
memset(&rib_cnt, 0, sizeof(rib_cnt));
memset(&fib_cnt, 0, sizeof(fib_cnt));
@@ -2452,10 +2491,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
if (!use_json)
vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source",
- "Prefix Routes", "FIB",
- zvrf_name(((struct rib_table_info *)
- route_table_get_info(table))
- ->zvrf));
+ "Prefix Routes", "FIB", vrf_name);
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
if (rib_cnt[i] > 0) {
@@ -2530,7 +2566,11 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
json_object_int_add(json_route_summary, "prefixRoutesTotalFib",
fib_cnt[ZEBRA_ROUTE_TOTAL]);
- vty_json(vty, json_route_summary);
+ if (!vrf_json)
+ vty_json(vty, json_route_summary);
+ else
+ json_object_object_add(vrf_json, vrf_name,
+ json_route_summary);
} else {
vty_out(vty, "------\n");
vty_out(vty, "%-20s %-20d %-20d \n", "Totals",
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index cc12cb4221..89b43f6d22 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -5159,6 +5159,15 @@ void zebra_vxlan_macvlan_up(struct interface *ifp)
return;
link_ifp = zif->link;
+ if (!link_ifp) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "macvlan parent link is not found. Parent index %d ifp %s",
+ zif->link_ifindex,
+ ifindex2ifname(zif->link_ifindex,
+ ifp->vrf->vrf_id));
+ return;
+ }
link_zif = link_ifp->info;
assert(link_zif);
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 3671fdb507..27668534ee 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -675,10 +675,14 @@ static void zserv_client_free(struct zserv *client)
* If any instance are graceful restart enabled,
* client is not deleted
*/
- if (DYNAMIC_CLIENT_GR_DISABLED(client)) {
+ if (DYNAMIC_CLIENT_GR_DISABLED(client) || zebra_router_in_shutdown()) {
if (IS_ZEBRA_DEBUG_EVENT)
zlog_debug("%s: Deleting client %s", __func__,
zebra_route_string(client->proto));
+
+ if (zebra_router_in_shutdown())
+ zebra_gr_client_final_shutdown(client);
+
zserv_client_delete(client);
} else {
/* Handle cases where client has GR instance. */
@@ -1121,12 +1125,10 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client)
vty_out(vty, "ES-EVI %-12u%-12u%-12u\n",
client->local_es_evi_add_cnt, 0, client->local_es_evi_del_cnt);
vty_out(vty, "Errors: %u\n", client->error_cnt);
-
-#if defined DEV_BUILD
vty_out(vty, "Input Fifo: %zu:%zu Output Fifo: %zu:%zu\n",
client->ibuf_fifo->count, client->ibuf_fifo->max_count,
client->obuf_fifo->count, client->obuf_fifo->max_count);
-#endif
+
vty_out(vty, "\n");
}
diff --git a/zebra/zserv.h b/zebra/zserv.h
index e1c990fb05..57d673060f 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -378,6 +378,7 @@ __attribute__((__noreturn__)) void zebra_finalize(struct event *event);
/*
* Graceful restart functions.
*/
+extern void zebra_gr_client_final_shutdown(struct zserv *client);
extern int zebra_gr_client_disconnect(struct zserv *client);
extern void zebra_gr_client_reconnect(struct zserv *client);
extern void zebra_gr_stale_client_cleanup(struct list *client_list);