summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_bmp.c68
-rw-r--r--bgpd/bgp_evpn_vty.c69
-rw-r--r--bgpd/bgp_io.c2
-rw-r--r--bgpd/bgp_nexthop.c192
-rw-r--r--bgpd/bgp_nexthop.h2
-rw-r--r--bgpd/bgp_nht.c6
-rw-r--r--bgpd/bgp_nht.h5
-rw-r--r--bgpd/bgp_route.c105
-rw-r--r--bgpd/bgp_route.h1
-rw-r--r--bgpd/bgp_trace.h18
-rw-r--r--bgpd/bgp_updgrp_adv.c95
-rw-r--r--bgpd/bgp_vty.c81
-rw-r--r--bgpd/bgp_zebra.c2
-rw-r--r--bgpd/bgpd.c8
-rw-r--r--bgpd/bgpd.h3
-rw-r--r--doc/developer/building-frr-for-alpine.rst4
-rw-r--r--doc/user/bgp.rst4
-rw-r--r--isisd/isis_cli.c11
-rw-r--r--lib/if.c118
-rw-r--r--lib/mgmt_be_client.c92
-rw-r--r--lib/mgmt_be_client.h16
-rw-r--r--lib/mgmt_msg_native.h13
-rw-r--r--lib/northbound.c6
-rw-r--r--lib/northbound.h53
-rw-r--r--lib/northbound_oper.c8
-rw-r--r--lib/route_types.txt2
-rwxr-xr-xospfclient/ospfclient.py4
-rw-r--r--ospfd/ospf_vty.c12
-rw-r--r--tests/lib/northbound/test_oper_data.c41
-rw-r--r--tests/lib/northbound/test_oper_data.refout12
-rw-r--r--tests/topotests/bgp_bmp/bgpbmp.py28
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp_2.py163
-rw-r--r--tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py15
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py14
-rw-r--r--tests/topotests/lib/bmp_collector/bgp/update/rd.py9
-rwxr-xr-xtests/topotests/lib/fe_client.py42
-rw-r--r--tests/topotests/mgmt_oper/oper.py17
-rw-r--r--tests/topotests/mgmt_oper/test_oper.py9
-rw-r--r--tools/gen_northbound_callbacks.c72
-rw-r--r--yang/frr-test-module.yang3
-rw-r--r--zebra/zebra_nb_config.c15
-rw-r--r--zebra/zebra_routemap.c9
42 files changed, 1091 insertions, 358 deletions
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index acc49cac94..036bece359 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -28,6 +28,7 @@
#include "bgpd/bgp_table.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_nht.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_errors.h"
@@ -1708,6 +1709,26 @@ static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi,
return 0;
}
+static int bmp_nht_path_valid(struct bgp *bgp, struct bgp_path_info *path, bool valid)
+{
+ struct bgp_dest *dest = path->net;
+ struct bgp_table *table;
+
+ if (frrtrace_enabled(frr_bgp, bmp_nht_path_valid)) {
+ char pfxprint[PREFIX2STR_BUFFER];
+
+ prefix2str(&dest->rn->p, pfxprint, sizeof(pfxprint));
+ frrtrace(4, frr_bgp, bmp_nht_path_valid, bgp, pfxprint, path, valid);
+ }
+ if (bgp->peer_self == path->peer)
+ /* self declared networks or redistributed networks are not relevant for bmp */
+ return 0;
+
+ table = bgp_dest_table(dest);
+
+ return bmp_process(bgp, table->afi, table->safi, dest, path->peer, !valid);
+}
+
static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type,
uint32_t value)
{
@@ -2013,11 +2034,16 @@ static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
size_t open_len = stream_get_endp(s);
bbpeer->open_rx_len = open_len;
+ if (bbpeer->open_rx)
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len);
memcpy(bbpeer->open_rx, s->data, open_len);
bbpeer->open_tx_len = open_len;
- bbpeer->open_tx = bbpeer->open_rx;
+ if (bbpeer->open_tx)
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
+ bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, open_len);
+ memcpy(bbpeer->open_tx, s->data, open_len);
stream_free(s);
}
@@ -2057,6 +2083,7 @@ bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force)
} else {
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
if (bbpeer) {
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
bmp_peerh_del(&bmp_peerh, bbpeer);
XFREE(MTYPE_BMP_PEER, bbpeer);
@@ -3090,11 +3117,14 @@ static int bmp_route_update(struct bgp *bgp, afi_t afi, safi_t safi,
return 0;
}
- struct bmp_bgp *bmpbgp = bmp_bgp_get(bgp);
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
struct peer *peer = updated_route->peer;
struct bmp_targets *bt;
struct bmp *bmp;
+ if (!bmpbgp)
+ return 0;
+
frr_each (bmp_targets, &bmpbgp->targets, bt) {
if (CHECK_FLAG(bt->afimon[afi][safi], BMP_MON_LOC_RIB)) {
is_locribmon_enabled = true;
@@ -3149,6 +3179,37 @@ static int bgp_bmp_early_fini(void)
return 0;
}
+/* called when the routerid of an instance changes */
+static int bmp_bgp_attribute_updated(struct bgp *bgp, bool withdraw)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+
+ if (!bmpbgp)
+ return 0;
+
+ bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown);
+
+ if (bmpbgp->vrf_state == vrf_state_down)
+ /* do not send peer events, router id will not be enough to set state to up
+ */
+ return 0;
+
+ /* vrf_state is up: trigger a peer event
+ */
+ bmp_send_all_safe(bmpbgp, bmp_peerstate(bgp->peer_self, withdraw));
+ return 1;
+}
+
+static int bmp_routerid_update(struct bgp *bgp, bool withdraw)
+{
+ return bmp_bgp_attribute_updated(bgp, withdraw);
+}
+
+static int bmp_route_distinguisher_update(struct bgp *bgp, afi_t afi, bool preconfig)
+{
+ return bmp_bgp_attribute_updated(bgp, preconfig);
+}
+
/* called when a bgp instance goes up/down, implying that the underlying VRF
* has been created or deleted in zebra
*/
@@ -3194,6 +3255,7 @@ static int bgp_bmp_module_init(void)
hook_register(peer_status_changed, bmp_peer_status_changed);
hook_register(peer_backward_transition, bmp_peer_backward);
hook_register(bgp_process, bmp_process);
+ hook_register(bgp_nht_path_update, bmp_nht_path_valid);
hook_register(bgp_inst_config_write, bmp_config_write);
hook_register(bgp_inst_delete, bmp_bgp_del);
hook_register(frr_late_init, bgp_bmp_init);
@@ -3201,6 +3263,8 @@ static int bgp_bmp_module_init(void)
hook_register(frr_early_fini, bgp_bmp_early_fini);
hook_register(bgp_instance_state, bmp_vrf_state_changed);
hook_register(bgp_vrf_status_changed, bmp_vrf_itf_state_changed);
+ hook_register(bgp_routerid_update, bmp_routerid_update);
+ hook_register(bgp_route_distinguisher_update, bmp_route_distinguisher_update);
return 0;
}
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index d9dfc4c5eb..dc6e0d33c2 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -3108,6 +3108,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
afi_t afi;
safi_t safi;
uint32_t prefix_cnt, path_cnt;
+ int first = true;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
@@ -3132,8 +3133,15 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
sizeof(rd_str), bgp->asnotation);
- if (json)
+ if (json) {
+ if (first) {
+ vty_out(vty, "\"%s\":", rd_str);
+ first = false;
+ } else {
+ vty_out(vty, ",\"%s\":", rd_str);
+ }
json_rd = json_object_new_object();
+ }
rd_header = 1;
@@ -3247,18 +3255,18 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
}
if (json) {
- if (add_rd_to_json)
- json_object_object_add(json, rd_str, json_rd);
- else {
+ if (add_rd_to_json) {
+ vty_json_no_pretty(vty, json_rd);
+ } else {
+ vty_out(vty, "{}");
json_object_free(json_rd);
- json_rd = NULL;
}
}
}
if (json) {
- json_object_int_add(json, "numPrefix", prefix_cnt);
- json_object_int_add(json, "numPaths", path_cnt);
+ vty_out(vty, ",\"numPrefix\":%u", prefix_cnt);
+ vty_out(vty, ",\"numPaths\":%u", path_cnt);
} else {
if (prefix_cnt == 0) {
vty_out(vty, "No EVPN prefixes %sexist\n",
@@ -3276,20 +3284,18 @@ int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
{
json_object *json = NULL;
- if (use_json)
+ if (use_json) {
json = json_object_new_object();
+ vty_out(vty, "{\n");
+ }
evpn_show_all_routes(vty, bgp, type, json, detail, false);
- if (use_json)
- /*
- * We are using no_pretty here because under extremely high
- * settings (lots of routes with many different paths) this can
- * save several minutes of output when FRR is run on older cpu's
- * or more underperforming routers out there. So for route
- * scale, we need to use no_pretty json.
- */
- vty_json_no_pretty(vty, json);
+ if (use_json) {
+ vty_out(vty, "}\n");
+ json_object_free(json);
+ }
+
return CMD_SUCCESS;
}
@@ -4940,8 +4946,10 @@ DEFUN(show_bgp_l2vpn_evpn_route,
if (!bgp)
return CMD_WARNING;
- if (uj)
+ if (uj) {
json = json_object_new_object();
+ vty_out(vty, "{\n");
+ }
if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
return CMD_WARNING;
@@ -4954,13 +4962,10 @@ DEFUN(show_bgp_l2vpn_evpn_route,
evpn_show_all_routes(vty, bgp, type, json, detail, self_orig);
- /*
- * This is an extremely expensive operation at scale
- * and as such we need to save as much time as is
- * possible.
- */
- if (uj)
- vty_json_no_pretty(vty, json);
+ if (uj) {
+ vty_out(vty, "}\n");
+ json_object_free(json);
+ }
return CMD_SUCCESS;
}
@@ -5017,10 +5022,20 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
return CMD_WARNING;
- if (rd_all)
+ if (rd_all) {
+ if (uj)
+ vty_out(vty, "{\n");
+
evpn_show_all_routes(vty, bgp, type, json, 1, false);
- else
+
+ if (uj) {
+ vty_out(vty, "}\n");
+ json_object_free(json);
+ return CMD_SUCCESS;
+ }
+ } else {
evpn_show_route_rd(vty, bgp, &prd, type, json);
+ }
if (uj)
vty_json(vty, json);
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
index 9e9251c854..5d0f14cc5c 100644
--- a/bgpd/bgp_io.c
+++ b/bgpd/bgp_io.c
@@ -199,7 +199,7 @@ static int read_ibuf_work(struct peer_connection *connection)
assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize);
stream_set_endp(pkt, pktsize);
- frrtrace(2, frr_bgp, packet_read, connection->peer, pkt);
+ frrtrace(2, frr_bgp, packet_read, connection, pkt);
frr_with_mutex (&connection->io_mtx) {
stream_fifo_push(connection->ibuf, pkt);
}
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index 5fda5701f3..ed689c8bac 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -869,6 +869,9 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
? bnc->ifindex_ipv6_ll
: nexthop->ifindex,
bgp->vrf_id));
+ json_object_int_add(json_gate, "ifindex",
+ bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll
+ : nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV4:
json_object_string_addf(json_gate, "ip", "%pI4",
@@ -882,6 +885,9 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
? bnc->ifindex_ipv6_ll
: nexthop->ifindex,
bgp->vrf_id));
+ json_object_int_add(json_gate, "ifindex",
+ bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll
+ : nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
json_object_string_addf(json_gate, "ip", "%pI4",
@@ -893,6 +899,9 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
? bnc->ifindex_ipv6_ll
: nexthop->ifindex,
bgp->vrf_id));
+ json_object_int_add(json_gate, "ifindex",
+ bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll
+ : nexthop->ifindex);
break;
case NEXTHOP_TYPE_BLACKHOLE:
json_object_boolean_true_add(json_gate,
@@ -926,13 +935,13 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
vty_out(vty, " gate %pI6", &nexthop->gate.ipv6);
if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX &&
bnc->ifindex_ipv6_ll)
- vty_out(vty, ", if %s\n",
- ifindex2ifname(bnc->ifindex_ipv6_ll,
- bgp->vrf_id));
+ vty_out(vty, ", if %s, ifindex %d\n",
+ ifindex2ifname(bnc->ifindex_ipv6_ll, bgp->vrf_id),
+ bnc->ifindex_ipv6_ll);
else if (nexthop->ifindex)
- vty_out(vty, ", if %s\n",
- ifindex2ifname(nexthop->ifindex,
- bgp->vrf_id));
+ vty_out(vty, ", if %s, ifindex %d\n",
+ ifindex2ifname(nexthop->ifindex, bgp->vrf_id),
+ nexthop->ifindex);
else
vty_out(vty, "\n");
break;
@@ -941,22 +950,22 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
vty_out(vty, " gate %pI4", &nexthop->gate.ipv4);
if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX &&
bnc->ifindex_ipv6_ll)
- vty_out(vty, ", if %s\n",
- ifindex2ifname(bnc->ifindex_ipv6_ll,
- bgp->vrf_id));
+ vty_out(vty, ", if %s, ifindex %d\n",
+ ifindex2ifname(bnc->ifindex_ipv6_ll, bgp->vrf_id),
+ bnc->ifindex_ipv6_ll);
else if (nexthop->ifindex)
- vty_out(vty, ", if %s\n",
- ifindex2ifname(nexthop->ifindex,
- bgp->vrf_id));
+ vty_out(vty, ", if %s, ifindex %d\n",
+ ifindex2ifname(nexthop->ifindex, bgp->vrf_id),
+ nexthop->ifindex);
else
vty_out(vty, "\n");
break;
case NEXTHOP_TYPE_IFINDEX:
- vty_out(vty, " if %s\n",
- ifindex2ifname(bnc->ifindex_ipv6_ll
- ? bnc->ifindex_ipv6_ll
- : nexthop->ifindex,
- bgp->vrf_id));
+ vty_out(vty, " if %s, ifindex %d\n",
+ ifindex2ifname(bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll
+ : nexthop->ifindex,
+ bgp->vrf_id),
+ bnc->ifindex_ipv6_ll ? bnc->ifindex_ipv6_ll : nexthop->ifindex);
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " blackhole\n");
@@ -970,9 +979,8 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
json_object_object_add(json, "nexthops", json_gates);
}
-static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
- struct bgp_nexthop_cache *bnc, bool specific,
- json_object *json)
+static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, struct bgp_nexthop_cache *bnc,
+ bool detail, bool uj)
{
char buf[PREFIX2STR_BUFFER];
time_t tbuf;
@@ -983,10 +991,10 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
peer = (struct peer *)bnc->nht_info;
- if (json)
+ if (uj)
json_nexthop = json_object_new_object();
if (bnc->srte_color) {
- if (json)
+ if (uj)
json_object_int_add(json_nexthop, "srteColor",
bnc->srte_color);
else
@@ -994,7 +1002,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, buf, sizeof(buf));
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) {
- if (json) {
+ if (uj) {
json_object_boolean_true_add(json_nexthop, "valid");
json_object_boolean_true_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "igpMetric",
@@ -1022,7 +1030,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop);
} else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) {
- if (json) {
+ if (uj) {
json_object_boolean_true_add(json_nexthop, "valid");
json_object_boolean_false_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "igpMetric",
@@ -1042,7 +1050,7 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop);
} else {
- if (json) {
+ if (uj) {
json_object_boolean_false_add(json_nexthop, "valid");
json_object_boolean_false_add(json_nexthop, "complete");
json_object_int_add(json_nexthop, "pathCount",
@@ -1074,8 +1082,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
}
tbuf = time(NULL) - (monotime(NULL) - bnc->last_update);
- if (json) {
- if (!specific) {
+ if (uj) {
+ if (detail) {
json_last_update = json_object_new_object();
json_object_int_add(json_last_update, "epoch", tbuf);
json_object_string_add(json_last_update, "string",
@@ -1090,22 +1098,25 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
}
/* show paths dependent on nexthop, if needed. */
- if (specific)
+ if (detail)
bgp_show_nexthop_paths(vty, bgp, bnc, json_nexthop);
- if (json)
- json_object_object_add(json, buf, json_nexthop);
+
+ if (uj) {
+ vty_out(vty, "\"%s\":", buf);
+ vty_json_no_pretty(vty, json_nexthop);
+ }
}
-static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp,
- bool import_table, json_object *json, afi_t afi,
- bool detail)
+static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, bool import_table, bool uj,
+ afi_t afi, bool detail)
{
struct bgp_nexthop_cache *bnc;
struct bgp_nexthop_cache_head(*tree)[AFI_MAX];
- json_object *json_afi = NULL;
bool found = false;
+ bool firstafi = true;
+ bool firstnh = true;
- if (!json) {
+ if (!uj) {
if (import_table)
vty_out(vty, "Current BGP import check cache:\n");
else
@@ -1117,34 +1128,42 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp,
tree = &bgp->nexthop_cache_table;
if (afi == AFI_IP || afi == AFI_IP6) {
- if (json)
- json_afi = json_object_new_object();
+ if (uj)
+ vty_out(vty, "%s:{", (afi == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) {
- bgp_show_nexthop(vty, bgp, bnc, detail, json_afi);
+ if (uj)
+ vty_out(vty, "%s", firstnh ? "" : ",");
+ bgp_show_nexthop(vty, bgp, bnc, detail, uj);
found = true;
+ firstnh = false;
}
- if (found && json)
- json_object_object_add(
- json, (afi == AFI_IP) ? "ipv4" : "ipv6",
- json_afi);
+ if (found && uj)
+ vty_out(vty, "}");
return;
}
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
- if (json && (afi == AFI_IP || afi == AFI_IP6))
- json_afi = json_object_new_object();
- frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc)
- bgp_show_nexthop(vty, bgp, bnc, detail, json_afi);
- if (json && (afi == AFI_IP || afi == AFI_IP6))
- json_object_object_add(
- json, (afi == AFI_IP) ? "ipv4" : "ipv6",
- json_afi);
+ if (afi != AFI_IP && afi != AFI_IP6)
+ continue;
+ if (uj)
+ vty_out(vty, "%s%s:{", firstafi ? "" : ",",
+ (afi == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
+ firstafi = false;
+ firstnh = true;
+ frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) {
+ if (uj)
+ vty_out(vty, "%s", firstnh ? "" : ",");
+ bgp_show_nexthop(vty, bgp, bnc, detail, uj);
+ firstnh = false;
+ }
+
+ if (uj)
+ vty_out(vty, "}");
}
}
-static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
- const char *nhopip_str, bool import_table,
- json_object *json, afi_t afi, bool detail)
+static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, const char *nhopip_str,
+ bool import_table, bool uj, afi_t afi, bool detail)
{
struct bgp *bgp;
@@ -1153,7 +1172,7 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
else
bgp = bgp_get_default();
if (!bgp) {
- if (!json)
+ if (!uj)
vty_out(vty, "%% No such BGP instance exist\n");
return CMD_WARNING;
}
@@ -1163,61 +1182,57 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
struct bgp_nexthop_cache_head (*tree)[AFI_MAX];
struct bgp_nexthop_cache *bnc;
bool found = false;
- json_object *json_afi = NULL;
if (!str2prefix(nhopip_str, &nhop)) {
- if (!json)
+ if (!uj)
vty_out(vty, "nexthop address is malformed\n");
return CMD_WARNING;
}
tree = import_table ? &bgp->import_check_table
: &bgp->nexthop_cache_table;
- if (json)
- json_afi = json_object_new_object();
+ if (uj)
+ vty_out(vty, "%s:{",
+ (family2afi(nhop.family) == AFI_IP) ? "\"ipv4\"" : "\"ipv6\"");
frr_each (bgp_nexthop_cache, &(*tree)[family2afi(nhop.family)],
bnc) {
if (prefix_cmp(&bnc->prefix, &nhop))
continue;
- bgp_show_nexthop(vty, bgp, bnc, true, json_afi);
+ bgp_show_nexthop(vty, bgp, bnc, true, uj);
found = true;
}
- if (json)
- json_object_object_add(
- json,
- (family2afi(nhop.family) == AFI_IP) ? "ipv4"
- : "ipv6",
- json_afi);
- if (!found && !json)
+ if (!found && !uj)
vty_out(vty, "nexthop %s does not have entry\n",
nhopip_str);
+
+ if (uj)
+ vty_out(vty, "}");
} else
- bgp_show_nexthops(vty, bgp, import_table, json, afi, detail);
+ bgp_show_nexthops(vty, bgp, import_table, uj, afi, detail);
return CMD_SUCCESS;
}
-static void bgp_show_all_instances_nexthops_vty(struct vty *vty,
- json_object *json, afi_t afi,
- bool detail)
+static void bgp_show_all_instances_nexthops_vty(struct vty *vty, bool uj, afi_t afi, bool detail)
{
struct listnode *node, *nnode;
struct bgp *bgp;
const char *inst_name;
- json_object *json_instance = NULL;
+ bool firstinst = true;
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
inst_name = (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
? VRF_DEFAULT_NAME
: bgp->name;
- if (json)
- json_instance = json_object_new_object();
+ if (uj)
+ vty_out(vty, "%s\"%s\":{", firstinst ? "" : ",", inst_name);
+
else
vty_out(vty, "\nInstance %s:\n", inst_name);
- bgp_show_nexthops(vty, bgp, false, json_instance, afi, detail);
-
- if (json)
- json_object_object_add(json, inst_name, json_instance);
+ bgp_show_nexthops(vty, bgp, false, uj, afi, detail);
+ firstinst = false;
+ if (uj)
+ vty_out(vty, "}");
}
}
@@ -1241,20 +1256,18 @@ DEFPY (show_ip_bgp_nexthop,
JSON_STR)
{
int rc = 0;
- json_object *json = NULL;
afi_t afiz = AFI_UNSPEC;
if (uj)
- json = json_object_new_object();
+ vty_out(vty, "{\n");
if (afi)
afiz = bgp_vty_afi_from_str(afi);
- rc = show_ip_bgp_nexthop_table(vty, vrf, nhop_str, false, json, afiz,
- detail);
+ rc = show_ip_bgp_nexthop_table(vty, vrf, nhop_str, false, uj, afiz, detail);
if (uj)
- vty_json(vty, json);
+ vty_out(vty, "}\n");
return rc;
}
@@ -1271,16 +1284,14 @@ DEFPY (show_ip_bgp_import_check,
JSON_STR)
{
int rc = 0;
- json_object *json = NULL;
if (uj)
- json = json_object_new_object();
+ vty_out(vty, "{\n");
- rc = show_ip_bgp_nexthop_table(vty, vrf, NULL, true, json, AFI_UNSPEC,
- detail);
+ rc = show_ip_bgp_nexthop_table(vty, vrf, NULL, true, uj, AFI_UNSPEC, detail);
if (uj)
- vty_json(vty, json);
+ vty_out(vty, "}\n");
return rc;
}
@@ -1298,19 +1309,18 @@ DEFPY (show_ip_bgp_instance_all_nexthop,
"Show detailed information\n"
JSON_STR)
{
- json_object *json = NULL;
afi_t afiz = AFI_UNSPEC;
if (uj)
- json = json_object_new_object();
+ vty_out(vty, "{");
if (afi)
afiz = bgp_vty_afi_from_str(afi);
- bgp_show_all_instances_nexthops_vty(vty, json, afiz, detail);
+ bgp_show_all_instances_nexthops_vty(vty, uj, afiz, detail);
if (uj)
- vty_json(vty, json);
+ vty_out(vty, "}");
return CMD_SUCCESS;
}
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index 6a4a02dcc8..5679c215b1 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -82,7 +82,7 @@ struct bgp_nexthop_cache {
* L3 unreachable | VALID = 0 | VALID = 0
* | INCOMPLETE = 0 | INCOMPLETE = 0
*/
-#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7)
+#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 8)
uint32_t srte_color;
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index ed83757ea3..164e2300c0 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -41,6 +41,9 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
static void bgp_nht_ifp_initial(struct event *thread);
+DEFINE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid),
+ (bgp, pi, valid));
+
static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc)
{
return (bgp_zebra_num_connects() == 0
@@ -1449,6 +1452,9 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc)
}
}
+ if (path_valid != bnc_is_valid_nexthop)
+ hook_call(bgp_nht_path_update, bgp_path, path, bnc_is_valid_nexthop);
+
bgp_process(bgp_path, dest, path, afi, safi);
}
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
index e7c6fdc281..345089ac5a 100644
--- a/bgpd/bgp_nht.h
+++ b/bgpd/bgp_nht.h
@@ -83,4 +83,9 @@ extern void bgp_nht_ifp_up(struct interface *ifp);
extern void bgp_nht_ifp_down(struct interface *ifp);
extern void bgp_nht_interface_events(struct peer *peer);
+
+/* called when a path becomes valid or invalid, because of nexthop tracking */
+DECLARE_HOOK(bgp_nht_path_update, (struct bgp *bgp, struct bgp_path_info *pi, bool valid),
+ (bgp, pi, valid));
+
#endif /* _BGP_NHT_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 0f899d9617..f519534192 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -649,7 +649,7 @@ static bool use_bgp_med_value(struct attr *attr, struct bgp *bgp)
/* 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)
+uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp)
{
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)))
return attr->med;
@@ -15106,6 +15106,8 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
json_object *json = NULL;
json_object *json_ar = NULL;
bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ bool first = true;
+ struct update_subgroup *subgrp;
/* Init BGP headers here so they're only displayed once
* even if 'table' is 2-tier (MPLS_VPN, ENCAP, EVPN).
@@ -15174,6 +15176,28 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
else
table = bgp->rib[afi][safi];
+ subgrp = peer_subgroup(peer, afi, safi);
+ if (use_json) {
+ if (type == bgp_show_adj_route_advertised || type == bgp_show_adj_route_received) {
+ if (header1) {
+ int version = table ? table->version : 0;
+ vty_out(vty, "\"bgpTableVersion\":%d", version);
+ vty_out(vty, ",\"bgpLocalRouterId\":\"%pI4\"", &bgp->router_id);
+ vty_out(vty, ",\"defaultLocPrf\":%u", bgp->default_local_pref);
+ vty_out(vty, ",\"localAS\":%u", bgp->as);
+ if (type == bgp_show_adj_route_advertised && subgrp &&
+ CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ vty_out(vty, ",\"bgpOriginatingDefaultNetwork\":\"%s\"",
+ (afi == AFI_IP) ? "0.0.0.0/0" : "::/0");
+ }
+
+ if (type == bgp_show_adj_route_advertised)
+ vty_out(vty, ",\"advertisedRoutes\": ");
+ if (type == bgp_show_adj_route_received)
+ vty_out(vty, ",\"receivedRoutes\": ");
+ }
+ }
+
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
|| (safi == SAFI_EVPN)) {
@@ -15192,6 +15216,7 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
json_routes = json_object_new_object();
const struct prefix_rd *prd;
+
prd = (const struct prefix_rd *)bgp_dest_get_prefix(
dest);
@@ -15205,34 +15230,56 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
&filtered_count_per_rd);
/* Don't include an empty RD in the output! */
- if (json_routes && (output_count_per_rd > 0))
- json_object_object_add(json_ar, rd_str,
- json_routes);
+ if (json_routes && (output_count_per_rd > 0) && use_json) {
+ if (type == bgp_show_adj_route_advertised ||
+ type == bgp_show_adj_route_received) {
+ if (first) {
+ vty_out(vty, "\"%s\":", rd_str);
+ first = false;
+ } else {
+ vty_out(vty, ",\"%s\":", rd_str);
+ }
+ vty_json_no_pretty(vty, json_routes);
+ } else {
+ json_object_object_add(json_ar, rd_str, json_routes);
+ }
+ }
output_count += output_count_per_rd;
filtered_count += filtered_count_per_rd;
}
- } else
+ } else {
show_adj_route(vty, peer, table, afi, safi, type, rmap_name,
json, json_ar, show_flags, &header1, &header2,
rd_str, match, &output_count, &filtered_count);
+ if (use_json) {
+ if (type == bgp_show_adj_route_advertised ||
+ type == bgp_show_adj_route_received) {
+ vty_json_no_pretty(vty, json_ar);
+ }
+ }
+ }
+
if (use_json) {
- if (type == bgp_show_adj_route_advertised)
- json_object_object_add(json, "advertisedRoutes",
- json_ar);
- else
+ if (type == bgp_show_adj_route_advertised || type == bgp_show_adj_route_received) {
+ vty_out(vty, ",\"totalPrefixCounter\":%lu", output_count);
+ vty_out(vty, ",\"filteredPrefixCounter\":%lu", filtered_count);
+ json_object_free(json);
+ } else {
+ /* for bgp_show_adj_route_filtered & bgp_show_adj_route_bestpath type */
json_object_object_add(json, "receivedRoutes", json_ar);
- json_object_int_add(json, "totalPrefixCounter", output_count);
- json_object_int_add(json, "filteredPrefixCounter",
- filtered_count);
-
- /*
- * This is an extremely expensive operation at scale
- * and non-pretty reduces memory footprint significantly.
- */
- vty_json_no_pretty(vty, json);
- } else if (output_count > 0) {
+ json_object_int_add(json, "totalPrefixCounter", output_count);
+ json_object_int_add(json, "filteredPrefixCounter", filtered_count);
+ }
+
+ /*
+ * This is an extremely expensive operation at scale
+ * and non-pretty reduces memory footprint significantly.
+ */
+ if ((type != bgp_show_adj_route_advertised) && (type != bgp_show_adj_route_received))
+ vty_json_no_pretty(vty, json);
+ } else if (output_count > 0) {
if (!match && filtered_count > 0)
vty_out(vty,
"\nTotal number of prefixes %ld (%ld filtered)\n",
@@ -15335,6 +15382,7 @@ DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
uint16_t show_flags = 0;
struct listnode *node;
struct bgp *abgp;
+ int ret;
if (detail || prefix_str)
SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
@@ -15376,9 +15424,22 @@ DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
else if (argv_find(argv, argc, "filtered-routes", &idx))
type = bgp_show_adj_route_filtered;
- if (!all)
- return peer_adj_routes(vty, peer, afi, safi, type, route_map,
- prefix_str ? prefix : NULL, show_flags);
+ if (!all) {
+ if (uj)
+ if (type == bgp_show_adj_route_advertised ||
+ type == bgp_show_adj_route_received)
+ vty_out(vty, "{\n");
+
+ ret = peer_adj_routes(vty, peer, afi, safi, type, route_map,
+ prefix_str ? prefix : NULL, show_flags);
+ if (uj)
+ if (type == bgp_show_adj_route_advertised ||
+ type == bgp_show_adj_route_received)
+ vty_out(vty, "}\n");
+
+ return ret;
+ }
+
if (uj)
vty_out(vty, "{\n");
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 474e229575..c071120de9 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -1013,4 +1013,5 @@ extern void bgp_meta_queue_free(struct meta_queue *mq);
extern int early_route_process(struct bgp *bgp, struct bgp_dest *dest);
extern int other_route_process(struct bgp *bgp, struct bgp_dest *dest);
extern int eoiu_marker_process(struct bgp *bgp, struct bgp_dest *dest);
+extern uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp);
#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_trace.h b/bgpd/bgp_trace.h
index a77a25e435..43bc7a5a17 100644
--- a/bgpd/bgp_trace.h
+++ b/bgpd/bgp_trace.h
@@ -211,6 +211,24 @@ TRACEPOINT_EVENT(
TRACEPOINT_LOGLEVEL(frr_bgp, bmp_process, TRACE_DEBUG)
/*
+ * BMP is hooked for a nexthop tracking event
+ */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_nht_path_valid,
+ TP_ARGS(struct bgp *, bgp, char *, pfx, struct bgp_path_info *,
+ path, bool, valid),
+ TP_FIELDS(
+ ctf_string(bgp, bgp->name_pretty)
+ ctf_string(prefix, pfx)
+ ctf_string(path, PEER_HOSTNAME(path->peer))
+ ctf_integer(bool, valid, valid)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_nht_path_valid, TRACE_DEBUG)
+
+/*
* bgp_dest_lock/bgp_dest_unlock
*/
TRACEPOINT_EVENT(
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index a1bf9a4c61..8f816eb30d 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -228,64 +228,67 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
afi2str(afi), safi2str(safi), ctx->dest);
UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
- /* withdraw stale addpath without waiting for the coalesce timer timeout.
- * Otherwise, since adj->addpath_tx_id is overwritten, the code never
- * notice anymore it has to do a withdrawal.
- */
- if (addpath_capable)
- subgrp_withdraw_stale_addpath(ctx, subgrp);
- /*
- * Skip the subgroups that have coalesce timer running. We will
- * walk the entire prefix table for those subgroups when the
- * coalesce timer fires.
- */
- if (!subgrp->t_coalesce) {
-
- /* An update-group that uses addpath */
- if (addpath_capable) {
- subgrp_announce_addpath_best_selected(ctx->dest,
- subgrp);
+ /* An update-group that uses addpath */
+ if (addpath_capable) {
+ /* Send withdrawals without waiting for coalesting timer
+ * to expire.
+ */
+ if (subgrp->t_coalesce) {
+ subgrp_withdraw_stale_addpath(ctx, subgrp);
- /* Process the bestpath last so the "show [ip]
- * bgp neighbor x.x.x.x advertised"
- * output shows the attributes from the bestpath
- */
- if (ctx->pi)
- subgroup_process_announce_selected(
- subgrp, ctx->pi, ctx->dest, afi,
- safi,
- bgp_addpath_id_for_peer(
- peer, afi, safi,
- &ctx->pi->tx_addpath));
+ goto done;
}
- /* An update-group that does not use addpath */
- else {
- if (ctx->pi) {
- subgroup_process_announce_selected(
- subgrp, ctx->pi, ctx->dest, afi,
- safi,
- bgp_addpath_id_for_peer(
- peer, afi, safi,
- &ctx->pi->tx_addpath));
- } else {
- /* Find the addpath_tx_id of the path we
- * had advertised and
- * send a withdraw */
- RB_FOREACH_SAFE (adj, bgp_adj_out_rb,
- &ctx->dest->adj_out,
+
+ subgrp_withdraw_stale_addpath(ctx, subgrp);
+ subgrp_announce_addpath_best_selected(ctx->dest, subgrp);
+
+ /* Process the bestpath last so the
+ * "show [ip] bgp neighbor x.x.x.x advertised" output shows
+ * the attributes from the bestpath.
+ */
+ if (ctx->pi)
+ subgroup_process_announce_selected(
+ subgrp, ctx->pi, ctx->dest, afi, safi,
+ bgp_addpath_id_for_peer(peer, afi, safi,
+ &ctx->pi->tx_addpath));
+ } else {
+ /* Send withdrawals without waiting for coalesting timer
+ * to expire.
+ */
+ if (subgrp->t_coalesce) {
+ if (!ctx->pi || CHECK_FLAG(ctx->pi->flags, BGP_PATH_UNUSEABLE)) {
+ RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->dest->adj_out,
adj_next) {
if (adj->subgroup == subgrp) {
subgroup_process_announce_selected(
- subgrp, NULL,
- ctx->dest, afi,
- safi,
+ subgrp, NULL, ctx->dest, afi, safi,
adj->addpath_tx_id);
}
}
}
+
+ goto done;
+ }
+
+ if (ctx->pi) {
+ subgroup_process_announce_selected(
+ subgrp, ctx->pi, ctx->dest, afi, safi,
+ bgp_addpath_id_for_peer(peer, afi, safi,
+ &ctx->pi->tx_addpath));
+ } else {
+ RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->dest->adj_out,
+ adj_next) {
+ if (adj->subgroup == subgrp) {
+ subgroup_process_announce_selected(subgrp, NULL,
+ ctx->dest, afi,
+ safi,
+ adj->addpath_tx_id);
+ }
+ }
}
}
+done:
/* Notify BGP Conditional advertisement */
bgp_notify_conditional_adv_scanner(subgrp);
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 550adf93db..c6b09481b6 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -141,6 +141,8 @@ DEFINE_HOOK(bgp_inst_config_write,
DEFINE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp));
DEFINE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp));
DEFINE_HOOK(bgp_snmp_traps_config_write, (struct vty * vty), (vty));
+DEFINE_HOOK(bgp_route_distinguisher_update, (struct bgp *bgp, afi_t afi, bool preconfig),
+ (bgp, afi, preconfig));
static struct peer_group *listen_range_exists(struct bgp *bgp,
struct prefix *range, int exact);
@@ -9805,6 +9807,14 @@ DEFPY (af_rd_vpn_export,
vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
bgp_get_default(), bgp);
+ if (!bgp->vpn_policy[afi].tovpn_rd_pretty && !rd_str)
+ return CMD_SUCCESS;
+
+ if (yes && bgp->vpn_policy[afi].tovpn_rd_pretty && rd_str &&
+ strmatch(rd_str, bgp->vpn_policy[afi].tovpn_rd_pretty))
+ return CMD_SUCCESS;
+
+ hook_call(bgp_route_distinguisher_update, bgp, afi, true);
if (yes) {
if (bgp->vpn_policy[afi].tovpn_rd_pretty)
XFREE(MTYPE_BGP_NAME, bgp->vpn_policy[afi].tovpn_rd_pretty);
@@ -9815,9 +9825,11 @@ DEFPY (af_rd_vpn_export,
BGP_VPN_POLICY_TOVPN_RD_SET);
} else {
XFREE(MTYPE_BGP_NAME, bgp->vpn_policy[afi].tovpn_rd_pretty);
+ bgp->vpn_policy[afi].tovpn_rd_pretty = NULL;
UNSET_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_RD_SET);
}
+ hook_call(bgp_route_distinguisher_update, bgp, afi, false);
/* post-change: re-export vpn routes */
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
@@ -11476,6 +11488,72 @@ DEFPY (show_bgp_vrfs,
return CMD_SUCCESS;
}
+DEFPY(show_bgp_router,
+ show_bgp_router_cmd,
+ "show bgp router [json]",
+ SHOW_STR
+ BGP_STR
+ "Overall BGP information\n"
+ JSON_STR)
+{
+ char timebuf[MONOTIME_STRLEN];
+ time_t unix_timestamp;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ time_to_string(bm->start_time, timebuf);
+
+ if (uj) {
+ unix_timestamp = time(NULL) - (monotime(NULL) - bm->start_time);
+ json_object_int_add(json, "bgpStartedAt", unix_timestamp);
+ json_object_boolean_add(json, "bgpStartedGracefully",
+ CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART));
+ }
+
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_RESTART)) {
+ if (!uj)
+ vty_out(vty, "BGP started gracefully at %s", timebuf);
+ else
+ json_object_boolean_add(json, "grComplete",
+ CHECK_FLAG(bm->flags, BM_FLAG_GR_COMPLETE));
+
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GR_COMPLETE)) {
+ time_to_string(bm->gr_completion_time, timebuf);
+ if (uj) {
+ unix_timestamp = time(NULL) -
+ (monotime(NULL) - bm->gr_completion_time);
+ json_object_int_add(json, "grCompletedAt", unix_timestamp);
+ } else
+ vty_out(vty, "Graceful restart completed at %s", timebuf);
+ } else {
+ if (!uj)
+ vty_out(vty, "Graceful restart is in progress\n");
+ }
+ } else {
+ if (!uj)
+ vty_out(vty, "BGP started at %s", timebuf);
+ }
+
+ if (uj) {
+ json_object_boolean_add(json, "bgpInMaintenanceMode",
+ (CHECK_FLAG(bm->flags, BM_FLAG_MAINTENANCE_MODE)));
+ json_object_int_add(json, "bgpInstanceCount", listcount(bm->bgp));
+
+ vty_json(vty, json);
+ } else {
+ if (CHECK_FLAG(bm->flags, BM_FLAG_MAINTENANCE_MODE))
+ vty_out(vty, "BGP is in Maintenance mode (BGP GSHUT is in effect)\n");
+
+ vty_out(vty, "Number of BGP instances (including default): %d\n",
+ listcount(bm->bgp));
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_bgp_mac_hash,
show_bgp_mac_hash_cmd,
"show bgp mac hash",
@@ -21927,6 +22005,9 @@ void bgp_vty_init(void)
/* "show [ip] bgp vrfs" commands. */
install_element(VIEW_NODE, &show_bgp_vrfs_cmd);
+ /* Some overall BGP information */
+ install_element(VIEW_NODE, &show_bgp_router_cmd);
+
/* Community-list. */
community_list_vty();
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index e3465feda8..7ad9ce4726 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1339,7 +1339,7 @@ static void bgp_zebra_announce_parse_nexthop(
* overridden on 1st nexthop */
if (mpinfo == info) {
if (metric)
- *metric = mpinfo_cp->attr->med;
+ *metric = bgp_med_value(mpinfo_cp->attr, bgp);
if (tag)
*tag = mpinfo_cp->attr->tag;
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 2f234e3a5a..05bc804db4 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -86,6 +86,7 @@ DEFINE_QOBJ_TYPE(bgp);
DEFINE_QOBJ_TYPE(peer);
DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
DEFINE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
+DEFINE_HOOK(bgp_routerid_update, (struct bgp *bgp, bool withdraw), (bgp, withdraw));
/* BGP process wide configuration. */
static struct bgp_master bgp_master;
@@ -301,6 +302,8 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id,
vpn_handle_router_id_update(bgp, true, is_config);
+ hook_call(bgp_routerid_update, bgp, true);
+
IPV4_ADDR_COPY(&bgp->router_id, id);
/* Set all peer's local identifier with this value. */
@@ -318,6 +321,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id,
vpn_handle_router_id_update(bgp, false, is_config);
+ hook_call(bgp_routerid_update, bgp, false);
return 0;
}
@@ -6686,7 +6690,7 @@ int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi,
SET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_ALLOWAS_IN_ORIGIN);
member->allowas_in[afi][safi] = 0;
- peer_on_policy_change(peer, afi, safi, 0);
+ peer_on_policy_change(member, afi, safi, 0);
}
} else {
if (member->allowas_in[afi][safi] != allow_num
@@ -6695,7 +6699,7 @@ int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi,
UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_ALLOWAS_IN_ORIGIN);
member->allowas_in[afi][safi] = allow_num;
- peer_on_policy_change(peer, afi, safi, 0);
+ peer_on_policy_change(member, afi, safi, 0);
}
}
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 47214e52e5..65b268c4ea 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -907,6 +907,9 @@ DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled),
(vrf, enabled));
DECLARE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
+DECLARE_HOOK(bgp_routerid_update, (struct bgp *bgp, bool withdraw), (bgp, withdraw));
+DECLARE_HOOK(bgp_route_distinguisher_update, (struct bgp *bgp, afi_t afi, bool preconfig),
+ (bgp, afi, preconfig));
/* Thread callback information */
struct afi_safi_info {
diff --git a/doc/developer/building-frr-for-alpine.rst b/doc/developer/building-frr-for-alpine.rst
index 68e58c9d76..a5ce636ebb 100644
--- a/doc/developer/building-frr-for-alpine.rst
+++ b/doc/developer/building-frr-for-alpine.rst
@@ -47,11 +47,11 @@ Build apk packages
./docker/alpine/build.sh
-This will put the apk packages in:
+This will put the apk packages into the architecture folder in:
::
- ./docker/pkgs/apk/x86_64/
+ ./docker/alpine/pkgs/apk/
Usage
-----
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index dafcac7c84..3642681765 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -4350,6 +4350,10 @@ displays IPv6 routing table.
If ``detail`` option is specified after ``json``, more verbose JSON output
will be displayed.
+.. clicmd:: show bgp router [json]
+
+ This command displays information related BGP router and Graceful Restart.
+
Some other commands provide additional options for filtering the output.
.. clicmd:: show [ip] bgp regexp LINE
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 652efee89a..735e39a377 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -626,10 +626,17 @@ DEFPY_YANG(domain_passwd, domain_passwd_cmd,
}
DEFPY_YANG(no_area_passwd, no_area_passwd_cmd,
- "no <area-password|domain-password>$cmd",
+ "no <area-password|domain-password>$cmd [<clear|md5>$pwd_type WORD$pwd [authenticate snp <send-only|validate>$snp]]",
NO_STR
"Configure the authentication password for an area\n"
- "Set the authentication password for a routing domain\n")
+ "Set the authentication password for a routing domain\n"
+ "Clear-text authentication type\n"
+ "MD5 authentication type\n"
+ "Level-wide password\n"
+ "Authentication\n"
+ "SNP PDUs\n"
+ "Send but do not check PDUs on receiving\n"
+ "Send and check PDUs on receiving\n")
{
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
diff --git a/lib/if.c b/lib/if.c
index 378ca16e49..586fc1d5cd 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1649,90 +1649,90 @@ static int lib_interface_description_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
-/*
- * XPath: /frr-interface:lib/interface/vrf
- */
-static struct yang_data *
-lib_interface_vrf_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error __return_ok(const struct nb_node *nb_node, const void *list_entry,
+ struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
-
- return yang_data_new_string(args->xpath, ifp->vrf->name);
+ return NB_OK;
}
/*
- * XPath: /frr-interface:lib/interface/state/if-index
+ * XPath: /frr-interface:lib/interface/vrf
*/
-static struct yang_data *
-lib_interface_state_if_index_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error lib_interface_vrf_get(const struct nb_node *nb_node, const void *list_entry,
+ struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
+ const struct lysc_node *snode = nb_node->snode;
+ const struct interface *ifp = list_entry;
- return yang_data_new_int32(args->xpath, ifp->ifindex);
+ if (lyd_new_term(parent, snode->module, snode->name, ifp->vrf->name, LYD_NEW_PATH_UPDATE,
+ NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
}
/*
- * XPath: /frr-interface:lib/interface/state/mtu
+ * XPath: /frr-interface:lib/interface/state/if-index
*/
-static struct yang_data *
-lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error lib_interface_state_if_index_get(const struct nb_node *nb_node,
+ const void *list_entry,
+ struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
+ const struct lysc_node *snode = nb_node->snode;
+ const struct interface *ifp = list_entry;
+ int32_t value = ifp->ifindex;
- return yang_data_new_uint32(args->xpath, ifp->mtu);
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
}
/*
- * XPath: /frr-interface:lib/interface/state/mtu6
+ * XPath: /frr-interface:lib/interface/state/mtu[6]
*/
-static struct yang_data *
-lib_interface_state_mtu6_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error lib_interface_state_mtu_get(const struct nb_node *nb_node,
+ const void *list_entry, struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
+ const struct lysc_node *snode = nb_node->snode;
+ const struct interface *ifp = list_entry;
+ uint32_t value = ifp->mtu;
- return yang_data_new_uint32(args->xpath, ifp->mtu6);
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/state/speed
*/
-static struct yang_data *
-lib_interface_state_speed_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error lib_interface_state_speed_get(const struct nb_node *nb_node,
+ const void *list_entry, struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
+ const struct lysc_node *snode = nb_node->snode;
+ const struct interface *ifp = list_entry;
+ uint32_t value = ifp->speed;
- return yang_data_new_uint32(args->xpath, ifp->speed);
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/state/metric
*/
-static struct yang_data *
-lib_interface_state_metric_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error lib_interface_state_metric_get(const struct nb_node *nb_node,
+ const void *list_entry, struct lyd_node *parent)
{
- const struct interface *ifp = args->list_entry;
-
- return yang_data_new_uint32(args->xpath, ifp->metric);
-}
+ const struct lysc_node *snode = nb_node->snode;
+ const struct interface *ifp = list_entry;
+ uint32_t value = ifp->metric;
-/*
- * XPath: /frr-interface:lib/interface/state/flags
- */
-static struct yang_data *
-lib_interface_state_flags_get_elem(struct nb_cb_get_elem_args *args)
-{
- /* TODO: implement me. */
- return NULL;
-}
-
-/*
- * XPath: /frr-interface:lib/interface/state/type
- */
-static struct yang_data *
-lib_interface_state_type_get_elem(struct nb_cb_get_elem_args *args)
-{
- /* TODO: implement me. */
- return NULL;
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
}
/*
@@ -1779,49 +1779,49 @@ const struct frr_yang_module_info frr_interface_info = {
{
.xpath = "/frr-interface:lib/interface/vrf",
.cbs = {
- .get_elem = lib_interface_vrf_get_elem,
+ .get = lib_interface_vrf_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/if-index",
.cbs = {
- .get_elem = lib_interface_state_if_index_get_elem,
+ .get = lib_interface_state_if_index_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/mtu",
.cbs = {
- .get_elem = lib_interface_state_mtu_get_elem,
+ .get = lib_interface_state_mtu_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/mtu6",
.cbs = {
- .get_elem = lib_interface_state_mtu6_get_elem,
+ .get = lib_interface_state_mtu_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/speed",
.cbs = {
- .get_elem = lib_interface_state_speed_get_elem,
+ .get = lib_interface_state_speed_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/metric",
.cbs = {
- .get_elem = lib_interface_state_metric_get_elem,
+ .get = lib_interface_state_metric_get,
}
},
{
.xpath = "/frr-interface:lib/interface/state/flags",
.cbs = {
- .get_elem = lib_interface_state_flags_get_elem,
+ .get = __return_ok,
}
},
{
.xpath = "/frr-interface:lib/interface/state/type",
.cbs = {
- .get_elem = lib_interface_state_type_get_elem,
+ .get = __return_ok,
}
},
{
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index f03006ad0e..05949fd62d 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -312,11 +312,11 @@ static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
return ret;
}
-static int mgmt_be_send_notification(void *__be_client, const char *xpath,
- const struct lyd_node *tree)
+static int __send_notification(struct mgmt_be_client *client, const char *xpath,
+ const struct lyd_node *tree, uint8_t op)
{
- struct mgmt_be_client *client = __be_client;
struct mgmt_msg_notify_data *msg = NULL;
+ // LYD_FORMAT format = LYD_LYB;
LYD_FORMAT format = LYD_JSON;
uint8_t **darrp;
LY_ERR err;
@@ -324,37 +324,91 @@ static int mgmt_be_send_notification(void *__be_client, const char *xpath,
assert(tree);
- debug_be_client("%s: sending YANG notification: %s", __func__,
- tree->schema->name);
+ debug_be_client("%s: sending %sYANG %snotification: %s", __func__,
+ op == NOTIFY_OP_DS_DELETE ? "delete "
+ : op == NOTIFY_OP_DS_REPLACE ? "replace "
+ : op == NOTIFY_OP_DS_PATCH ? "patch "
+ : "",
+ op == NOTIFY_OP_NOTIFICATION ? "" : "DS ", xpath ?: tree->schema->name);
/*
* Allocate a message and append the data to it using `format`
*/
- msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0,
- MTYPE_MSG_NATIVE_NOTIFY);
+ msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0, MTYPE_MSG_NATIVE_NOTIFY);
msg->code = MGMT_MSG_CODE_NOTIFY;
msg->result_type = format;
+ msg->op = op;
mgmt_msg_native_xpath_encode(msg, xpath);
- darrp = mgmt_msg_native_get_darrp(msg);
- err = yang_print_tree_append(darrp, tree, format,
- (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
- LYD_PRINT_WITHSIBLINGS));
- if (err) {
- flog_err(EC_LIB_LIBYANG,
- "%s: error creating notification data: %s", __func__,
- ly_strerrcode(err));
- ret = 1;
- goto done;
+ if (tree) {
+ darrp = mgmt_msg_native_get_darrp(msg);
+ err = yang_print_tree_append(darrp, tree, format,
+ (LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
+ LYD_PRINT_WITHSIBLINGS));
+ if (err) {
+ flog_err(EC_LIB_LIBYANG, "%s: error creating notification data: %s",
+ __func__, ly_strerrcode(err));
+ ret = 1;
+ goto done;
+ }
}
- (void)be_client_send_native_msg(client, msg,
- mgmt_msg_native_get_msg_len(msg), false);
+ ret = be_client_send_native_msg(client, msg, mgmt_msg_native_get_msg_len(msg), false);
done:
mgmt_msg_native_free_msg(msg);
return ret;
}
+/**
+ * mgmt_be_send_ds_delete_notification() - Send DS notification to mgmtd
+ */
+int mgmt_be_send_ds_delete_notification(const char *path)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, NULL, NOTIFY_OP_DS_DELETE);
+}
+
+/**
+ * mgmt_be_send_ds_patch_notification() - Send a YANG patch DS notification to mgmtd
+ */
+int mgmt_be_send_ds_patch_notification(const char *path, const struct lyd_node *patch)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, patch, NOTIFY_OP_DS_PATCH);
+}
+
+/**
+ * mgmt_be_send_ds_replace_notification() - Send a replace DS notification to mgmtd
+ */
+int mgmt_be_send_ds_replace_notification(const char *path, const struct lyd_node *tree)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, tree, NOTIFY_OP_DS_REPLACE);
+}
+
+/**
+ * mgmt_be_send_notification() - Send notification to mgmtd
+ *
+ * This function is attached to the northbound notification hook.
+ */
+static int mgmt_be_send_notification(void *__client, const char *path, const struct lyd_node *tree)
+{
+ __send_notification(__client, path, tree, NOTIFY_OP_NOTIFICATION);
+ return 0;
+}
+
static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
uint64_t txn_id, bool create)
{
diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h
index 6ed8c2a39f..a3e3896d52 100644
--- a/lib/mgmt_be_client.h
+++ b/lib/mgmt_be_client.h
@@ -112,6 +112,22 @@ extern struct mgmt_be_client *
mgmt_be_client_create(const char *name, struct mgmt_be_client_cbs *cbs,
uintptr_t user_data, struct event_loop *event_loop);
+
+/**
+ * mgmt_be_send_ds_delete_notification() - Send a datastore delete notification.
+ */
+extern int mgmt_be_send_ds_delete_notification(const char *path);
+
+/**
+ * mgmt_be_send_ds_patch_notification() - Send a datastore YANG patch notification.
+ */
+extern int mgmt_be_send_ds_patch_notification(const char *path, const struct lyd_node *tree);
+
+/**
+ * mgmt_be_send_ds_replace_notification() - Send a datastore replace notification.
+ */
+extern int mgmt_be_send_ds_replace_notification(const char *path, const struct lyd_node *tree);
+
/*
* Initialize library vty (adds debug support).
*
diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h
index 587a002801..4076977a22 100644
--- a/lib/mgmt_msg_native.h
+++ b/lib/mgmt_msg_native.h
@@ -323,22 +323,29 @@ _Static_assert(sizeof(struct mgmt_msg_get_data) ==
offsetof(struct mgmt_msg_get_data, xpath),
"Size mismatch");
+
+#define NOTIFY_OP_NOTIFICATION 0
+#define NOTIFY_OP_DS_REPLACE 1
+#define NOTIFY_OP_DS_DELETE 2
+#define NOTIFY_OP_DS_PATCH 3
+
/**
* struct mgmt_msg_notify_data - Message carrying notification data.
*
* @result_type: ``LYD_FORMAT`` for format of the @result value.
* @data: The xpath string of the notification followed by the tree data in
* @result_type format.
+ * @op: notify operation type.
*/
struct mgmt_msg_notify_data {
struct mgmt_msg_header;
uint8_t result_type;
- uint8_t resv2[7];
+ uint8_t op;
+ uint8_t resv2[6];
alignas(8) char data[];
};
-_Static_assert(sizeof(struct mgmt_msg_notify_data) ==
- offsetof(struct mgmt_msg_notify_data, data),
+_Static_assert(sizeof(struct mgmt_msg_notify_data) == offsetof(struct mgmt_msg_notify_data, data),
"Size mismatch");
#define EDIT_FLAG_IMPLICIT_LOCK 0x01
diff --git a/lib/northbound.c b/lib/northbound.c
index a385cc9ece..c67ed924a9 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -273,9 +273,11 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH,
!!nb_node->cbs.apply_finish, true);
error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM,
- !!nb_node->cbs.get_elem, false);
+ (nb_node->cbs.get_elem || nb_node->cbs.get), false);
error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT,
- !!nb_node->cbs.get_next, false);
+ (nb_node->cbs.get_next ||
+ (nb_node->snode->nodetype == LYS_LEAFLIST && nb_node->cbs.get)),
+ false);
error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS,
!!nb_node->cbs.get_keys, false);
error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY,
diff --git a/lib/northbound.h b/lib/northbound.h
index 97a1d31e57..42f763c3ef 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -21,6 +21,7 @@ extern "C" {
/* Forward declaration(s). */
struct vty;
struct debug;
+struct nb_node;
struct nb_yang_xpath_tag {
uint32_t ns;
@@ -102,6 +103,20 @@ enum nb_cb_operation {
NB_CB_NOTIFY,
};
+/* Northbound error codes. */
+enum nb_error {
+ NB_OK = 0,
+ NB_ERR,
+ NB_ERR_NO_CHANGES,
+ NB_ERR_NOT_FOUND,
+ NB_ERR_EXISTS,
+ NB_ERR_LOCKED,
+ NB_ERR_VALIDATION,
+ NB_ERR_RESOURCE,
+ NB_ERR_INCONSISTENCY,
+ NB_YIELD,
+};
+
union nb_resource {
int fd;
void *ptr;
@@ -426,6 +441,25 @@ struct nb_callbacks {
void (*apply_finish)(struct nb_cb_apply_finish_args *args);
/*
+ * Operational data callback (new direct tree add method).
+ *
+ * The callback function should create a new lyd_node (leaf) or
+ * lyd_node's (leaf list) for the value and attach to parent.
+ *
+ * nb_node
+ * The node representing the leaf or leaf list
+ * list_entry
+ * List entry from get_next (or NULL).
+ * parent
+ * The parent lyd_node to attach the leaf data to.
+ *
+ * Returns:
+ * Returns an nb_error if the data could not be added to the tree.
+ */
+ enum nb_error (*get)(const struct nb_node *nb_node, const void *list_entry,
+ struct lyd_node *parent);
+
+ /*
* Operational data callback.
*
* The callback function should return the value of a specific leaf,
@@ -672,20 +706,6 @@ struct frr_yang_module_info {
#endif
};
-/* Northbound error codes. */
-enum nb_error {
- NB_OK = 0,
- NB_ERR,
- NB_ERR_NO_CHANGES,
- NB_ERR_NOT_FOUND,
- NB_ERR_EXISTS,
- NB_ERR_LOCKED,
- NB_ERR_VALIDATION,
- NB_ERR_RESOURCE,
- NB_ERR_INCONSISTENCY,
- NB_YIELD,
-};
-
/* Default priority. */
#define NB_DFLT_PRIORITY (UINT32_MAX / 2)
@@ -814,8 +834,9 @@ extern struct debug nb_dbg_libyang;
extern struct nb_config *running_config;
/* Wrappers for the northbound callbacks. */
-extern struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
- const char *xpath,
+extern struct yang_data *nb_callback_has_new_get_elem(const struct nb_node *nb_node);
+
+extern struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, const char *xpath,
const void *list_entry);
extern const void *nb_callback_get_next(const struct nb_node *nb_node,
const void *parent_list_entry,
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index 2c72d8ecef..0364870486 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -654,6 +654,10 @@ static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys,
if (lysc_is_key(snode))
return NB_OK;
+ /* Check for new simple get */
+ if (nb_node->cbs.get)
+ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner);
+
data = nb_callback_get_elem(nb_node, xpath, ni->list_entry);
if (data == NULL)
return NB_OK;
@@ -687,6 +691,10 @@ static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys,
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return NB_OK;
+ /* Check for new simple get */
+ if (nb_node->cbs.get)
+ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner);
+
do {
struct yang_data *data;
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 93cbc36e97..b5f8b6fdf3 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -88,7 +88,7 @@ ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP", vr
ZEBRA_ROUTE_NHG, zebra, none, '-', 0, 0, 0, "Nexthop Group", none
ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE", none
ZEBRA_ROUTE_TABLE_DIRECT, table-direct, zebra, 't', 1, 1, 1, "Table-Direct", zebra
-ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-", none
+ZEBRA_ROUTE_ALL, any, none, '-', 0, 0, 0, "-", none
## help strings
diff --git a/ospfclient/ospfclient.py b/ospfclient/ospfclient.py
index 7477ef8191..588c1c9fdd 100755
--- a/ospfclient/ospfclient.py
+++ b/ospfclient/ospfclient.py
@@ -306,7 +306,7 @@ class OspfApiClient:
self._s = None
self._as = None
self._ls = None
- self._ar = self._r = self._w = None
+ self._ar = self._r = self._aw = self._w = None
self.server = server
self.handlers = handlers if handlers is not None else dict()
self.write_lock = Lock()
@@ -345,7 +345,7 @@ class OspfApiClient:
logging.debug("%s: success", self)
self._r, self._w = await asyncio.open_connection(sock=self._s)
- self._ar, _ = await asyncio.open_connection(sock=self._as)
+ self._ar, self._aw = await asyncio.open_connection(sock=self._as)
self._seq = 1
async def connect(self):
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 27528f6594..95e8b179d8 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -10258,8 +10258,10 @@ DEFUN (ospf_external_route_aggregation,
tag = strtoul(argv[idx + 2]->arg, NULL, 10);
ret = ospf_asbr_external_aggregator_set(ospf, &p, tag);
- if (ret == OSPF_INVALID)
- vty_out(vty, "Invalid configuration!!\n");
+ if (ret == OSPF_FAILURE) {
+ vty_out(vty, "%% Failed to set summary-address!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
return CMD_SUCCESS;
}
@@ -10611,8 +10613,10 @@ DEFUN (ospf_external_route_aggregation_no_adrvertise,
}
ret = ospf_asbr_external_rt_no_advertise(ospf, &p);
- if (ret == OSPF_INVALID)
- vty_out(vty, "Invalid configuration!!\n");
+ if (ret == OSPF_FAILURE) {
+ vty_out(vty, "%% Failed to set summary-address!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
return CMD_SUCCESS;
}
diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c
index 3d700d8a19..0b334c6522 100644
--- a/tests/lib/northbound/test_oper_data.c
+++ b/tests/lib/northbound/test_oper_data.c
@@ -120,6 +120,26 @@ static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next(
}
/*
+ * XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface-new
+ */
+static enum nb_error frr_test_module_vrfs_vrf_interfaces_interface_new_get(
+ const struct nb_node *nb_node, const void *parent_list_entry, struct lyd_node *parent)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ const struct tvrf *vrf;
+ struct listnode *node;
+ const char *interface;
+ LY_ERR err;
+
+ vrf = listgetdata((struct listnode *)parent_list_entry);
+ for (ALL_LIST_ELEMENTS_RO(vrf->interfaces, node, interface)) {
+ err = lyd_new_term(parent, snode->module, snode->name, interface, false, NULL);
+ assert(err == LY_SUCCESS);
+ }
+ return NB_OK;
+}
+
+/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route
*/
static const void *
@@ -228,10 +248,19 @@ frr_test_module_c1value_get_elem(struct nb_cb_get_elem_args *args)
/*
* XPath: /frr-test-module:frr-test-module/c2cont/c2value
*/
-static struct yang_data *
-frr_test_module_c2cont_c2value_get_elem(struct nb_cb_get_elem_args *args)
+static enum nb_error frr_test_module_c2cont_c2value_get(const struct nb_node *nb_node,
+ const void *parent_list_entry,
+ struct lyd_node *parent)
{
- return yang_data_new_uint32(args->xpath, 0xAB010203);
+ const struct lysc_node *snode = nb_node->snode;
+ uint32_t value = 0xAB010203;
+ LY_ERR err;
+
+ err = lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL);
+ assert(err == LY_SUCCESS);
+
+ return NB_OK;
}
/* clang-format off */
@@ -254,6 +283,10 @@ const struct frr_yang_module_info frr_test_module_info = {
.cbs.get_next = frr_test_module_vrfs_vrf_interfaces_interface_get_next,
},
{
+ .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface-new",
+ .cbs.get = frr_test_module_vrfs_vrf_interfaces_interface_new_get,
+ },
+ {
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route",
.cbs.get_next = frr_test_module_vrfs_vrf_routes_route_get_next,
},
@@ -287,7 +320,7 @@ const struct frr_yang_module_info frr_test_module_info = {
},
{
.xpath = "/frr-test-module:frr-test-module/c2cont/c2value",
- .cbs.get_elem = frr_test_module_c2cont_c2value_get_elem,
+ .cbs.get = frr_test_module_c2cont_c2value_get,
},
{
.xpath = NULL,
diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout
index 77e8562525..2536e0306b 100644
--- a/tests/lib/northbound/test_oper_data.refout
+++ b/tests/lib/northbound/test_oper_data.refout
@@ -11,6 +11,12 @@ test# show yang operational-data /frr-test-module:frr-test-module
"eth1",
"eth2",
"eth3"
+ ],
+ "interface-new": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
]
},
"routes": {
@@ -65,6 +71,12 @@ test# show yang operational-data /frr-test-module:frr-test-module
"eth1",
"eth2",
"eth3"
+ ],
+ "interface-new": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
]
},
"routes": {
diff --git a/tests/topotests/bgp_bmp/bgpbmp.py b/tests/topotests/bgp_bmp/bgpbmp.py
index eac78a63f7..acbc405aa4 100644
--- a/tests/topotests/bgp_bmp/bgpbmp.py
+++ b/tests/topotests/bgp_bmp/bgpbmp.py
@@ -187,12 +187,19 @@ def bmp_check_for_prefixes(
def bmp_check_for_peer_message(
- expected_peers, bmp_log_type, bmp_collector, bmp_log_file, is_rd_instance=False
+ expected_peers,
+ bmp_log_type,
+ bmp_collector,
+ bmp_log_file,
+ is_rd_instance=False,
+ peer_bgp_id=None,
+ peer_distinguisher=None,
):
"""
Check for the presence of a peer up message for the peer
"""
global SEQ
+ last_seq = SEQ
# we care only about the new messages
messages = [
@@ -208,6 +215,10 @@ def bmp_check_for_peer_message(
for m in messages:
if is_rd_instance and m["peer_distinguisher"] == "0:0":
continue
+ if peer_distinguisher and m["peer_distinguisher"] != peer_distinguisher:
+ continue
+ if peer_bgp_id and m["peer_bgp_id"] != peer_bgp_id:
+ continue
if (
"peer_ip" in m.keys()
and m["peer_ip"] != "0.0.0.0"
@@ -215,16 +226,23 @@ def bmp_check_for_peer_message(
):
if is_rd_instance and m["peer_type"] != "route distinguisher instance":
continue
- peers.append(m["peer_ip"])
+ peers.append((m["peer_ip"], m["seq"]))
elif m["policy"] == "loc-rib" and m["bmp_log_type"] == bmp_log_type:
- peers.append("0.0.0.0")
+ peers.append(("0.0.0.0", m["seq"]))
# check for prefixes
for ep in expected_peers:
- if ep not in peers:
+ for _ip, _seq in peers:
+ if ep == _ip:
+ msg = "The peer {} is present in the {} log messages."
+ logger.debug(msg.format(ep, bmp_log_type))
+ if _seq > last_seq:
+ last_seq = _seq
+ break
+ else:
msg = "The peer {} is not present in the {} log messages."
logger.debug(msg.format(ep, bmp_log_type))
return False
- SEQ = messages[-1]["seq"]
+ SEQ = last_seq
return True
diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_2.py b/tests/topotests/bgp_bmp/test_bgp_bmp_2.py
index f16ff2b445..e0b9a0f607 100644
--- a/tests/topotests/bgp_bmp/test_bgp_bmp_2.py
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp_2.py
@@ -252,6 +252,169 @@ def test_peer_down():
assert success, "Checking the updated prefixes has been failed !."
+def test_bgp_instance_flapping():
+ """
+ Checking for BGP loc-rib up messages
+ """
+ tgen = get_topogen()
+
+ # create flapping at BMP
+ tgen.net["r1vrf"].cmd("ip link set dev vrf1 down")
+
+ peers = ["0.0.0.0"]
+ logger.info("checking for BMP peer down LOC-RIB message.")
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer down",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert success, "Checking the BMP peer down LOC-RIB message failed !."
+
+ tgen.net["r1vrf"].cmd("ip link set dev vrf1 up")
+
+ logger.info("checking for BMP peer up LOC-RIB message.")
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer up",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert success, "Checking the BMP peer up LOC-RIB message failed !."
+
+
+def test_bgp_routerid_changed():
+ """
+ Checking for BGP loc-rib up messages with new router-id
+ """
+ tgen = get_topogen()
+
+ tgen.gears["r1vrf"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65501 vrf vrf1
+ bgp router-id 192.168.1.77
+ """
+ )
+
+ peers = ["0.0.0.0"]
+
+ logger.info(
+ "checking for BMP peer down LOC-RIB message with router-id set to 192.168.0.1."
+ )
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer down",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert (
+ success
+ ), "Checking the BMP peer down LOC-RIB message with router-id set to 192.168.0.1 failed !."
+
+ logger.info(
+ "checking for BMP peer up LOC-RIB message with router-id set to 192.168.1.77."
+ )
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer up",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ peer_bgp_id="192.168.1.77",
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert (
+ success
+ ), "Checking the BMP peer up LOC-RIB message with router-id set to 192.168.1.77 failed !."
+
+
+def test_reconfigure_route_distinguisher_vrf1():
+ """
+ Checking for BMP peers down messages
+ """
+ tgen = get_topogen()
+
+ bmp_update_seq(
+ tgen.gears["bmp1vrf"], os.path.join(tgen.logdir, "bmp1vrf", "bmp.log")
+ )
+ peers = ["0.0.0.0"]
+
+ tgen.gears["r1vrf"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65501 vrf vrf1
+ address-family ipv4 unicast
+ rd vpn export 666:22
+ exit-address-family
+ address-family ipv6 unicast
+ rd vpn export 666:22
+ """
+ )
+ logger.info(
+ "checking for BMP peer down LOC-RIB message with route-distinguisher set to 444:1"
+ )
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer down",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ peer_distinguisher="444:1",
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert (
+ success
+ ), "Checking the BMP peer down LOC-RIB message with route-distinguisher set to 444:1 failed !."
+
+ logger.info(
+ "checking for BMP peer up LOC-RIB messages with route-distinguisher set to 666:22"
+ )
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer up",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ peer_bgp_id="192.168.1.77",
+ peer_distinguisher="666:22",
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert (
+ success
+ ), "Checking the BMP peer up LOC-RIB message with route-distinguisher set to 666:22 failed !."
+
+ logger.info(
+ "checking for BMP peer up messages with route-distinguisher set to 666:22"
+ )
+ peers = ["192.168.0.2", "192:168::2"]
+ test_func = partial(
+ bmp_check_for_peer_message,
+ peers,
+ "peer up",
+ tgen.gears["bmp1vrf"],
+ os.path.join(tgen.logdir, "bmp1vrf", "bmp.log"),
+ is_rd_instance=True,
+ peer_distinguisher="666:22",
+ )
+ success, _ = topotest.run_and_expect(test_func, True, count=30, wait=1)
+ assert (
+ success
+ ), "Checking the BMP peer up messages with route-distinguisher set to 666:22 failed !."
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
index 5d8338d6eb..7e39b83d8f 100644
--- a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
+++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
@@ -187,6 +187,16 @@ def test_bgp_administrative_reset_gr():
"""
)
+ def _bgp_verify_show_bgp_router_json():
+ output = json.loads(r1.vtysh_cmd("show bgp router json"))
+ expected = {
+ "bgpStartedAt": "*",
+ "bgpStartedGracefully": False,
+ "bgpInMaintenanceMode": False,
+ "bgpInstanceCount": 1,
+ }
+ return topotest.json_cmp(output, expected)
+
step("Initial BGP converge")
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
@@ -205,6 +215,11 @@ def test_bgp_administrative_reset_gr():
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to send Administrative Reset notification from R2"
+ step("Check show bgp router json")
+ test_func = functools.partial(_bgp_verify_show_bgp_router_json)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Invalid BGP router details"
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
index 3932c29b98..ee7e00b323 100644
--- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -232,6 +232,20 @@ def test_local_vs_non_local():
assert False, "Route 60.0.0.0/24 should not have fibPending"
+def test_ip_protocol_any_fib_filter():
+ # "Filtered route of source protocol any should not get installed in fib"
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf\nno ip protocol bgp")
+ r2.vtysh_cmd("conf\nip protocol any route-map LIMIT")
+ test_bgp_route()
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/lib/bmp_collector/bgp/update/rd.py b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
index 3f08de5ae9..d44060bf2f 100644
--- a/tests/topotests/lib/bmp_collector/bgp/update/rd.py
+++ b/tests/topotests/lib/bmp_collector/bgp/update/rd.py
@@ -4,6 +4,7 @@
# Authored by Farid Mihoub <farid.mihoub@6wind.com>
#
import ipaddress
+import socket
import struct
@@ -45,9 +46,11 @@ class RouteDistinguisher:
self.repr_str = f"{self.as_number}:{self.assigned_sp}"
elif rd_type == 1:
- (self.admin_ipv4, self.assigned_sp) = struct.unpack_from("!IH", self.rd[2:])
- ipv4 = str(ipaddress.IPv4Address(self.admin_ipv4))
- self.repr_str = f"{self.as_number}:{self.assigned_sp}"
+ (self.admin_ipv4, self.assigned_sp) = struct.unpack_from(
+ "!4sH", self.rd[2:]
+ )
+ ipv4_str = socket.inet_ntoa(self.admin_ipv4)
+ self.repr_str = f"{ipv4_str}:{self.assigned_sp}"
elif rd_type == 2:
(self.four_bytes_as, self.assigned_sp) = struct.unpack_from(
diff --git a/tests/topotests/lib/fe_client.py b/tests/topotests/lib/fe_client.py
index 784f7d17eb..078df8cb33 100755
--- a/tests/topotests/lib/fe_client.py
+++ b/tests/topotests/lib/fe_client.py
@@ -78,8 +78,13 @@ GET_DATA_FLAG_STATE = 0x1
GET_DATA_FLAG_CONFIG = 0x2
GET_DATA_FLAG_EXACT = 0x4
-MSG_NOTIFY_FMT = "=B7x"
+MSG_NOTIFY_FMT = "=BB6x"
NOTIFY_FIELD_RESULT_TYPE = 0
+NOTIFY_FIELD_OP = 1
+NOTIFY_OP_NOTIFICATION = 0
+NOTIFY_OP_REPLACE = 1
+NOTIFY_OP_DELETE = 2
+NOTIFY_OP_PATCH = 3
MSG_NOTIFY_SELECT_FMT = "=B7x"
@@ -363,10 +368,12 @@ class Session:
raise Exception(f"Received NON-NOTIFY Message: {mfixed}: {mdata}")
vsplit = mhdr[HDR_FIELD_VSPLIT]
+ result_type = mfixed[0]
+ op = mfixed[1]
assert mdata[vsplit - 1] == 0
assert mdata[-1] == 0
- # xpath = mdata[: vsplit - 1].decode("utf-8")
- return mdata[vsplit:-1].decode("utf-8")
+ xpath = mdata[: vsplit - 1].decode("utf-8")
+ return result_type, op, xpath, mdata[vsplit:-1].decode("utf-8")
else:
raise TimeoutError("Timeout waiting for notifications")
@@ -390,6 +397,9 @@ def __parse_args():
"-c", "--config-only", action="store_true", help="return config only"
)
parser.add_argument(
+ "--datastore", action="store_true", help="listen for datastore notifications"
+ )
+ parser.add_argument(
"-q", "--query", nargs="+", metavar="XPATH", help="xpath[s] to query"
)
parser.add_argument("-s", "--server", default=MPATH, help="path to server socket")
@@ -434,9 +444,31 @@ def __main():
if args.listen is not None:
i = args.notify_count
+ if args.listen:
+ sess.add_notify_select(True, args.listen)
while i > 0 or args.notify_count == 0:
- notif = sess.recv_notify(args.listen)
- print(notif)
+ result_type, op, xpath, notif = sess.recv_notify()
+ if op == NOTIFY_OP_NOTIFICATION:
+ if args.datastore:
+ logging.warning("ignoring non-datastore notification: %s", notif)
+ else:
+ print(notif)
+ elif not args.datastore:
+ logging.warning(
+ "ignoring datastore notification op: %s xpath: %s data: %s",
+ op,
+ xpath,
+ notif,
+ )
+ elif op == NOTIFY_OP_PATCH:
+ print(f"#OP=PATCH: {xpath}")
+ print(notif)
+ elif op == NOTIFY_OP_REPLACE:
+ print(f"#OP=REPLACE: {xpath}")
+ print(notif)
+ elif op == NOTIFY_OP_DELETE:
+ print(f"#OP=DELETE: {xpath}")
+ assert len(notif) == 0
i -= 1
diff --git a/tests/topotests/mgmt_oper/oper.py b/tests/topotests/mgmt_oper/oper.py
index bca452d011..6e1866382b 100644
--- a/tests/topotests/mgmt_oper/oper.py
+++ b/tests/topotests/mgmt_oper/oper.py
@@ -62,7 +62,7 @@ def disable_debug(router):
router.vtysh_cmd("no debug northbound callbacks configuration")
-@retry(retry_timeout=30, initial_wait=1)
+@retry(retry_timeout=30, initial_wait=0.1)
def _do_oper_test(tgen, qr, seconds_left=None):
r1 = tgen.gears["r1"].net
@@ -113,6 +113,7 @@ def _do_oper_test(tgen, qr, seconds_left=None):
"-------DIFF---------\n%s\n---------DIFF----------",
pprint.pformat(cmpout),
)
+ cmpout = str(cmpout)
else:
cmpout = tt_json_cmp(ojson, ejson, exact=True)
if cmpout and ejson_alt is not None:
@@ -186,6 +187,20 @@ def addrgen(a, count, step=1):
@retry(retry_timeout=30, initial_wait=0.1)
+def check_kernel_net(r1, net, vrf):
+ addr = ipaddress.ip_network(net)
+ vrfstr = f" vrf {vrf}" if vrf else ""
+ if addr.version == 6:
+ kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}")
+ else:
+ kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}")
+
+ nentries = len(re.findall("\n", kernel))
+ logging.info("checking kernel routing table%s: (%s entries)", vrfstr, nentries)
+ assert str(net) in kernel, f"Failed to find '{net}' in {nentries} entries"
+
+
+@retry(retry_timeout=30, initial_wait=0.1)
def check_kernel_32(r1, start_addr, count, vrf, step=1):
start = ipaddress.ip_address(start_addr)
vrfstr = f" vrf {vrf}" if vrf else ""
diff --git a/tests/topotests/mgmt_oper/test_oper.py b/tests/topotests/mgmt_oper/test_oper.py
index 23529bc75e..0d346b5b7c 100644
--- a/tests/topotests/mgmt_oper/test_oper.py
+++ b/tests/topotests/mgmt_oper/test_oper.py
@@ -15,7 +15,7 @@ import math
import pytest
from lib.topogen import Topogen
-from oper import check_kernel_32, do_oper_test
+from oper import check_kernel_32, check_kernel_net, do_oper_test
pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
@@ -85,10 +85,17 @@ def test_oper(tgen):
]
r1 = tgen.gears["r1"].net
+
check_kernel_32(r1, "11.11.11.11", 1, "")
check_kernel_32(r1, "12.12.12.12", 1, "")
check_kernel_32(r1, "13.13.13.13", 1, "red")
check_kernel_32(r1, "14.14.14.14", 1, "red")
+
+ check_kernel_net(r1, "2001:1111::/64", "")
+ check_kernel_net(r1, "2002:2222::/64", "")
+ check_kernel_net(r1, "2003:333::/64", "red")
+ check_kernel_net(r1, "2004:4444::/64", "red")
+
do_oper_test(tgen, query_results)
diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c
index 516743acab..019404d7cc 100644
--- a/tools/gen_northbound_callbacks.c
+++ b/tools/gen_northbound_callbacks.c
@@ -15,12 +15,13 @@
#include "yang.h"
#include "northbound.h"
-static bool static_cbs;
+static bool f_static_cbs;
+static bool f_new_cbs;
static void __attribute__((noreturn)) usage(int status)
{
extern const char *__progname;
- fprintf(stderr, "usage: %s [-h] [-s] [-p path]* MODULE\n", __progname);
+ fprintf(stderr, "usage: %s [-h] [-n] [-s] [-p path]* MODULE\n", __progname);
exit(status);
}
@@ -111,6 +112,14 @@ static struct nb_callback_info nb_config_write = {
.arguments = "struct vty *vty, const struct lyd_node *dnode, bool show_defaults",
};
+static struct nb_callback_info nb_oper_get = {
+ .operation = NB_CB_GET_ELEM,
+ .return_type = "enum nb_error ",
+ .return_value = "NB_OK",
+ .arguments =
+ "const struct nb_node *nb_node, const void *parent_list_entry, struct lyd_node *parent",
+};
+
static void replace_hyphens_by_underscores(char *str)
{
char *p;
@@ -120,6 +129,14 @@ static void replace_hyphens_by_underscores(char *str)
*p++ = '_';
}
+static const char *__operation_name(enum nb_cb_operation operation)
+{
+ if (f_new_cbs && operation == NB_CB_GET_ELEM)
+ return "get";
+ else
+ return nb_cb_operation_name(operation);
+}
+
static void generate_callback_name(const struct lysc_node *snode,
enum nb_cb_operation operation, char *buffer,
size_t size)
@@ -143,7 +160,7 @@ static void generate_callback_name(const struct lysc_node *snode,
strlcat(buffer, snode->name, size);
strlcat(buffer, "_", size);
}
- strlcat(buffer, nb_cb_operation_name(operation), size);
+ strlcat(buffer, __operation_name(operation), size);
list_delete(&snodes);
replace_hyphens_by_underscores(buffer);
@@ -208,17 +225,25 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg)
return YANG_ITER_CONTINUE;
}
- for (struct nb_callback_info *cb = &nb_callbacks[0];
- cb->operation != -1; cb++) {
+ for (struct nb_callback_info *cb = &nb_callbacks[0]; cb->operation != -1; cb++) {
char cb_name[BUFSIZ];
if (cb->optional
|| !nb_cb_operation_is_valid(cb->operation, snode))
continue;
+ if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && snode->nodetype == LYS_LEAFLIST)
+ continue;
+
generate_callback_name(snode, cb->operation, cb_name,
sizeof(cb_name));
- generate_prototype(cb, cb_name);
+
+ if (cb->operation == NB_CB_GET_ELEM) {
+ if (f_new_cbs)
+ generate_prototype(&nb_oper_get, cb_name);
+ else
+ generate_prototype(cb, cb_name);
+ }
if (cb->need_config_write && need_config_write) {
generate_config_write_cb_name(snode, cb_name,
@@ -236,8 +261,8 @@ static int generate_prototypes(const struct lysc_node *snode, void *arg)
static void generate_callback(const struct nb_callback_info *ncinfo,
const char *cb_name)
{
- printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "",
- ncinfo->return_type, cb_name, ncinfo->arguments);
+ printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name,
+ ncinfo->arguments);
switch (ncinfo->operation) {
case NB_CB_CREATE:
@@ -266,8 +291,8 @@ static void generate_callback(const struct nb_callback_info *ncinfo,
static void generate_config_write_callback(const struct nb_callback_info *ncinfo,
const char *cb_name)
{
- printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "",
- ncinfo->return_type, cb_name, ncinfo->arguments);
+ printf("%s%s%s(%s)\n{\n", f_static_cbs ? "static " : "", ncinfo->return_type, cb_name,
+ ncinfo->arguments);
/* Add a comment, since these callbacks may not all be needed. */
printf("\t/* TODO: this cli callback is optional; the cli output may not need to be done at each node. */\n");
@@ -313,9 +338,18 @@ static int generate_callbacks(const struct lysc_node *snode, void *arg)
first = false;
}
+ if (f_new_cbs && cb->operation == NB_CB_GET_NEXT && snode->nodetype == LYS_LEAFLIST)
+ continue;
+
generate_callback_name(snode, cb->operation, cb_name,
sizeof(cb_name));
- generate_callback(cb, cb_name);
+
+ if (cb->operation == NB_CB_GET_ELEM) {
+ if (f_new_cbs)
+ generate_callback(&nb_oper_get, cb_name);
+ else
+ generate_callback(cb, cb_name);
+ }
if (cb->need_config_write && need_config_write) {
generate_config_write_cb_name(snode, cb_name,
@@ -371,12 +405,13 @@ static int generate_nb_nodes(const struct lysc_node *snode, void *arg)
printf("\t\t\t.cbs = {\n");
first = false;
}
+ if (f_new_cbs && cb->operation == NB_CB_GET_NEXT &&
+ snode->nodetype == LYS_LEAFLIST)
+ continue;
generate_callback_name(snode, cb->operation, cb_name,
sizeof(cb_name));
- printf("\t\t\t\t.%s = %s,\n",
- nb_cb_operation_name(cb->operation),
- cb_name);
+ printf("\t\t\t\t.%s = %s,\n", __operation_name(cb->operation), cb_name);
} else if (cb->need_config_write && need_config_write) {
if (first) {
yang_snode_get_path(snode,
@@ -417,11 +452,14 @@ int main(int argc, char *argv[])
int opt;
bool config_pass;
- while ((opt = getopt(argc, argv, "hp:s")) != -1) {
+ while ((opt = getopt(argc, argv, "hnp:s")) != -1) {
switch (opt) {
case 'h':
usage(EXIT_SUCCESS);
/* NOTREACHED */
+ case 'n':
+ f_new_cbs = true;
+ break;
case 'p':
if (stat(optarg, &st) == -1) {
fprintf(stderr,
@@ -438,7 +476,7 @@ int main(int argc, char *argv[])
*darr_append(search_paths) = darr_strdup(optarg);
break;
case 's':
- static_cbs = true;
+ f_static_cbs = true;
break;
default:
usage(EXIT_FAILURE);
@@ -477,7 +515,7 @@ int main(int argc, char *argv[])
printf("// SPDX-" "License-Identifier: GPL-2.0-or-later\n\n");
/* Generate callback prototypes. */
- if (!static_cbs) {
+ if (!f_static_cbs) {
printf("/* prototypes */\n");
yang_snodes_iterate(module->info, generate_prototypes, 0, NULL);
printf("\n");
diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang
index dcf204a956..90086d05a2 100644
--- a/yang/frr-test-module.yang
+++ b/yang/frr-test-module.yang
@@ -60,6 +60,9 @@ module frr-test-module {
leaf-list interface {
type frr-interface:interface-ref;
}
+ leaf-list interface-new {
+ type frr-interface:interface-ref;
+ }
}
container routes {
list route {
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index ec151360bd..d99010547f 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -3358,10 +3358,7 @@ int lib_vrf_zebra_filter_protocol_create(struct nb_cb_create_args *args)
const char *proto = yang_dnode_get_string(args->dnode, "protocol");
int rtype;
- if (strcasecmp(proto, "any") == 0)
- rtype = ZEBRA_ROUTE_MAX;
- else
- rtype = proto_name2num(proto);
+ rtype = proto_name2num(proto);
if (args->event == NB_EV_VALIDATE)
if (rtype < 0) {
@@ -3387,10 +3384,7 @@ int lib_vrf_zebra_filter_protocol_destroy(struct nb_cb_destroy_args *args)
yang_afi_safi_identity2value(afi_safi, &afi, &safi);
- if (strcasecmp(proto, "any") == 0)
- rtype = ZEBRA_ROUTE_MAX;
- else
- rtype = proto_name2num(proto);
+ rtype = proto_name2num(proto);
/* deleting an existing entry, it can't be invalid */
assert(rtype >= 0);
@@ -3418,10 +3412,7 @@ void lib_vrf_zebra_filter_protocol_apply_finish(
yang_afi_safi_identity2value(afi_safi, &afi, &safi);
- if (strcasecmp(proto, "any") == 0)
- rtype = ZEBRA_ROUTE_MAX;
- else
- rtype = proto_name2num(proto);
+ rtype = proto_name2num(proto);
/* finishing apply for a validated entry, it can't be invalid */
assert(rtype >= 0);
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
index 29bbf6023d..73ffa09c16 100644
--- a/zebra/zebra_routemap.c
+++ b/zebra/zebra_routemap.c
@@ -114,11 +114,6 @@ static void show_vrf_proto_rm(struct vty *vty, struct zebra_vrf *zvrf,
vty_out(vty, "%-24s : none\n", zebra_route_string(i));
}
- if (PROTO_RM_NAME(zvrf, af_type, i))
- vty_out(vty, "%-24s : %-10s\n", "any",
- PROTO_RM_NAME(zvrf, af_type, i));
- else
- vty_out(vty, "%-24s : none\n", "any");
}
static void show_vrf_nht_rm(struct vty *vty, struct zebra_vrf *zvrf,
@@ -1222,8 +1217,8 @@ route_map_result_t zebra_route_map_check(afi_t family, struct route_entry *re,
return RMAP_DENYMATCH;
}
if (!rmap) {
- rm_name = PROTO_RM_NAME(zvrf, family, ZEBRA_ROUTE_MAX);
- rmap = PROTO_RM_MAP(zvrf, family, ZEBRA_ROUTE_MAX);
+ rm_name = PROTO_RM_NAME(zvrf, family, ZEBRA_ROUTE_ALL);
+ rmap = PROTO_RM_MAP(zvrf, family, ZEBRA_ROUTE_ALL);
if (rm_name && !rmap)
return RMAP_DENYMATCH;