diff options
| -rw-r--r-- | .github/workflows/docker-daily-master.yml | 7 | ||||
| -rw-r--r-- | .github/workflows/docker-stable.yml | 7 | ||||
| -rw-r--r-- | alpine/APKBUILD.in | 2 | ||||
| -rw-r--r-- | bgpd/bgp_nexthop.c | 470 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 2 | ||||
| -rw-r--r-- | bgpd/subdir.am | 1 | ||||
| -rw-r--r-- | doc/developer/cli.rst | 5 | ||||
| -rw-r--r-- | doc/developer/subdir.am | 1 | ||||
| -rw-r--r-- | doc/developer/workflow.rst | 18 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 19 | ||||
| -rw-r--r-- | doc/user/subdir.am | 2 | ||||
| -rw-r--r-- | lib/command.c | 4 | ||||
| -rw-r--r-- | lib/elf_py.c | 2 | ||||
| -rw-r--r-- | lib/xref.h | 13 | ||||
| -rw-r--r-- | ospfd/ospf_packet.c | 7 | ||||
| -rw-r--r-- | zebra/dplane_fpm_nl.c | 9 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 9 | ||||
| -rw-r--r-- | zebra/rt_netlink.h | 2 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.c | 6 | ||||
| -rw-r--r-- | zebra/zserv.c | 68 | ||||
| -rw-r--r-- | zebra/zserv.h | 15 |
21 files changed, 515 insertions, 154 deletions
diff --git a/.github/workflows/docker-daily-master.yml b/.github/workflows/docker-daily-master.yml index 5b521a870d..90bd23678d 100644 --- a/.github/workflows/docker-daily-master.yml +++ b/.github/workflows/docker-daily-master.yml @@ -38,8 +38,9 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_ROBOT_TOKEN }} - name: Build and push uses: docker/build-push-action@v3 @@ -47,6 +48,6 @@ jobs: context: . file: ./docker/alpine/Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/frr:master + tags: ${{ secrets.QUAY_USERNAME }}/frr:master build-args: PKGVER=${{ steps.vars.outputs.date }} platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/.github/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml index 1e069a722e..ad69e0e415 100644 --- a/.github/workflows/docker-stable.yml +++ b/.github/workflows/docker-stable.yml @@ -39,8 +39,9 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_ROBOT_TOKEN }} - name: Build and push uses: docker/build-push-action@v3 @@ -48,6 +49,6 @@ jobs: context: . file: ./docker/alpine/Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/frr:v${{ steps.vars.outputs.frr_version }} + tags: ${{ secrets.QUAY_USERNAME }}/frr:v${{ steps.vars.outputs.frr_version }} build-args: PKGVER=${{ steps.vars.outputs.date }} platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 3aad9549b5..fef7a61ccc 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -10,7 +10,7 @@ depends="json-c c-ares iproute2 python3 bash" makedepends="ncurses-dev net-snmp-dev gawk texinfo perl acct autoconf automake bash binutils bison bsd-compat-headers build-base c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs - expat fakeroot flex fortify-headers gdbm git gmp isl json-c-dev kmod + expat fakeroot flex fortify-headers gdbm git gmp json-c-dev kmod lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs libltdl libressl libssh2 libstdc++ libtool libuuid diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 6bbdbdc1a9..d9822ee974 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -23,6 +23,7 @@ #include "command.h" #include "thread.h" #include "prefix.h" +#include "lib/json.h" #include "zclient.h" #include "stream.h" #include "network.h" @@ -731,17 +732,68 @@ bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, return false; } +static void bgp_show_bgp_path_info_flags(uint32_t flags, json_object *json) +{ + json_object *json_flags = NULL; + + if (!json) + return; + + json_flags = json_object_new_object(); + json_object_boolean_add(json_flags, "igpChanged", + CHECK_FLAG(flags, BGP_PATH_IGP_CHANGED)); + json_object_boolean_add(json_flags, "damped", + CHECK_FLAG(flags, BGP_PATH_DAMPED)); + json_object_boolean_add(json_flags, "history", + CHECK_FLAG(flags, BGP_PATH_HISTORY)); + json_object_boolean_add(json_flags, "bestpath", + CHECK_FLAG(flags, BGP_PATH_SELECTED)); + json_object_boolean_add(json_flags, "valid", + CHECK_FLAG(flags, BGP_PATH_VALID)); + json_object_boolean_add(json_flags, "attrChanged", + CHECK_FLAG(flags, BGP_PATH_ATTR_CHANGED)); + json_object_boolean_add(json_flags, "deterministicMedCheck", + CHECK_FLAG(flags, BGP_PATH_DMED_CHECK)); + json_object_boolean_add(json_flags, "deterministicMedSelected", + CHECK_FLAG(flags, BGP_PATH_DMED_SELECTED)); + json_object_boolean_add(json_flags, "stale", + CHECK_FLAG(flags, BGP_PATH_STALE)); + json_object_boolean_add(json_flags, "removed", + CHECK_FLAG(flags, BGP_PATH_REMOVED)); + json_object_boolean_add(json_flags, "counted", + CHECK_FLAG(flags, BGP_PATH_COUNTED)); + json_object_boolean_add(json_flags, "multipath", + CHECK_FLAG(flags, BGP_PATH_MULTIPATH)); + json_object_boolean_add(json_flags, "multipathChanged", + CHECK_FLAG(flags, BGP_PATH_MULTIPATH_CHG)); + json_object_boolean_add(json_flags, "ribAttributeChanged", + CHECK_FLAG(flags, BGP_PATH_RIB_ATTR_CHG)); + json_object_boolean_add(json_flags, "nexthopSelf", + CHECK_FLAG(flags, BGP_PATH_ANNC_NH_SELF)); + json_object_boolean_add(json_flags, "linkBandwidthChanged", + CHECK_FLAG(flags, BGP_PATH_LINK_BW_CHG)); + json_object_boolean_add(json_flags, "acceptOwn", + CHECK_FLAG(flags, BGP_PATH_ACCEPT_OWN)); + json_object_object_add(json, "flags", json_flags); +} + static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, - struct bgp_nexthop_cache *bnc) + struct bgp_nexthop_cache *bnc, + json_object *json) { struct bgp_dest *dest; struct bgp_path_info *path; - int afi; + afi_t afi; safi_t safi; struct bgp_table *table; struct bgp *bgp_path; + json_object *paths = NULL; + json_object *json_path = NULL; - vty_out(vty, " Paths:\n"); + if (json) + paths = json_object_new_array(); + else + vty_out(vty, " Paths:\n"); LIST_FOREACH (path, &(bnc->paths), nh_thread) { dest = path->net; assert(dest && bgp_dest_table(dest)); @@ -750,6 +802,25 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, safi = table->safi; bgp_path = table->bgp; + if (json) { + json_path = json_object_new_object(); + json_object_string_add(json_path, "afi", afi2str(afi)); + json_object_string_add(json_path, "safi", + safi2str(safi)); + json_object_string_addf(json_path, "prefix", "%pBD", + dest); + if (dest->pdest) + json_object_string_addf( + json_path, "rd", "%pRD", + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest)); + json_object_string_add( + json_path, "vrf", + vrf_id_to_name(bgp_path->vrf_id)); + bgp_show_bgp_path_info_flags(path->flags, json_path); + json_object_array_add(paths, json_path); + continue; + } if (dest->pdest) vty_out(vty, " %d/%d %pBD RD %pRD %s flags 0x%x\n", afi, safi, dest, @@ -760,14 +831,86 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", afi, safi, dest, bgp_path->name_pretty, path->flags); } + if (json) + json_object_object_add(json, "paths", paths); } static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, - struct bgp_nexthop_cache *bnc) + struct bgp_nexthop_cache *bnc, + json_object *json) { struct nexthop *nexthop; + json_object *json_gates = NULL; + json_object *json_gate = NULL; + if (json) + json_gates = json_object_new_array(); for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { + if (json) { + json_gate = json_object_new_object(); + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV6: + json_object_string_addf(json_gate, "ip", "%pI6", + &nexthop->gate.ipv6); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_addf(json_gate, "ip", "%pI6", + &nexthop->gate.ipv6); + json_object_string_add( + json_gate, "interfaceName", + ifindex2ifname( + bnc->ifindex ? bnc->ifindex + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + json_object_string_addf(json_gate, "ip", "%pI4", + &nexthop->gate.ipv4); + break; + case NEXTHOP_TYPE_IFINDEX: + json_object_string_add( + json_gate, "interfaceName", + ifindex2ifname( + bnc->ifindex ? bnc->ifindex + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_addf(json_gate, "ip", "%pI4", + &nexthop->gate.ipv4); + json_object_string_add( + json_gate, "interfaceName", + ifindex2ifname( + bnc->ifindex ? bnc->ifindex + : nexthop->ifindex, + bgp->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + json_object_boolean_true_add(json_gate, + "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add(json_gate, + "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add( + json_gate, "adminProhibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add( + json_gate, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } + json_object_array_add(json_gates, json_gate); + continue; + } switch (nexthop->type) { case NEXTHOP_TYPE_IPV6: vty_out(vty, " gate %pI6\n", &nexthop->gate.ipv6); @@ -803,97 +946,190 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, nexthop->type); } } + if (json) + 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) + struct bgp_nexthop_cache *bnc, bool specific, + json_object *json) { char buf[PREFIX2STR_BUFFER]; time_t tbuf; struct peer *peer; + json_object *json_last_update = NULL; + json_object *json_nexthop = NULL; peer = (struct peer *)bnc->nht_info; - if (bnc->srte_color) - vty_out(vty, " SR-TE color %u -", bnc->srte_color); + if (json) + json_nexthop = json_object_new_object(); + if (bnc->srte_color) { + if (json) + json_object_int_add(json_nexthop, "srteColor", + bnc->srte_color); + else + vty_out(vty, " SR-TE color %u -", bnc->srte_color); + } + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, buf, sizeof(buf)); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { - vty_out(vty, " %s valid [IGP metric %d], #paths %d", - inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, - buf, sizeof(buf)), - bnc->metric, bnc->path_count); - if (peer) - vty_out(vty, ", peer %s", peer->host); - if (bnc->is_evpn_gwip_nexthop) - vty_out(vty, " EVPN Gateway IP"); - vty_out(vty, "\n"); - bgp_show_nexthops_detail(vty, bgp, bnc); + if (json) { + json_object_boolean_true_add(json_nexthop, "valid"); + json_object_boolean_true_add(json_nexthop, "complete"); + json_object_int_add(json_nexthop, "igpMetric", + bnc->metric); + json_object_int_add(json_nexthop, "pathCount", + bnc->path_count); + if (peer) + json_object_string_add(json_nexthop, "peer", + peer->host); + if (bnc->is_evpn_gwip_nexthop) + json_object_boolean_true_add(json_nexthop, + "isEvpnGatewayIp"); + } else { + vty_out(vty, " %s valid [IGP metric %d], #paths %d", + buf, bnc->metric, bnc->path_count); + if (peer) + vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + } + bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop); } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) { - vty_out(vty, - " %s overlay index unresolved [IGP metric %d], #paths %d", - inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, - buf, sizeof(buf)), - bnc->metric, bnc->path_count); - if (bnc->is_evpn_gwip_nexthop) - vty_out(vty, " EVPN Gateway IP"); - vty_out(vty, "\n"); - bgp_show_nexthops_detail(vty, bgp, bnc); + if (json) { + json_object_boolean_true_add(json_nexthop, "valid"); + json_object_boolean_false_add(json_nexthop, "complete"); + json_object_int_add(json_nexthop, "igpMetric", + bnc->metric); + json_object_int_add(json_nexthop, "pathCount", + bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + json_object_boolean_true_add(json_nexthop, + "isEvpnGatewayIp"); + } else { + vty_out(vty, + " %s overlay index unresolved [IGP metric %d], #paths %d", + buf, bnc->metric, bnc->path_count); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + } + bgp_show_nexthops_detail(vty, bgp, bnc, json_nexthop); } else { - vty_out(vty, " %s invalid, #paths %d", - inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, - buf, sizeof(buf)), - bnc->path_count); - if (peer) - vty_out(vty, ", peer %s", peer->host); - if (bnc->is_evpn_gwip_nexthop) - vty_out(vty, " EVPN Gateway IP"); - vty_out(vty, "\n"); - if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) - vty_out(vty, " Must be Connected\n"); - if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) - vty_out(vty, " Is not Registered\n"); + if (json) { + json_object_boolean_false_add(json_nexthop, "valid"); + json_object_boolean_false_add(json_nexthop, "complete"); + json_object_int_add(json_nexthop, "pathCount", + bnc->path_count); + if (peer) + json_object_string_add(json_nexthop, "peer", + peer->host); + if (bnc->is_evpn_gwip_nexthop) + json_object_boolean_true_add(json_nexthop, + "isEvpnGatewayIp"); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) + json_object_boolean_false_add(json_nexthop, + "isConnected"); + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) + json_object_boolean_false_add(json_nexthop, + "isRegistered"); + } else { + vty_out(vty, " %s invalid, #paths %d", buf, + bnc->path_count); + if (peer) + vty_out(vty, ", peer %s", peer->host); + if (bnc->is_evpn_gwip_nexthop) + vty_out(vty, " EVPN Gateway IP"); + vty_out(vty, "\n"); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) + vty_out(vty, " Must be Connected\n"); + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) + vty_out(vty, " Is not Registered\n"); + } } tbuf = time(NULL) - (monotime(NULL) - bnc->last_update); - vty_out(vty, " Last update: %s", ctime(&tbuf)); + if (json) { + if (!specific) { + json_last_update = json_object_new_object(); + json_object_int_add(json_last_update, "epoch", tbuf); + json_object_string_add(json_last_update, "string", + ctime(&tbuf)); + json_object_object_add(json_nexthop, "lastUpdate", + json_last_update); + } else { + json_object_int_add(json_nexthop, "lastUpdate", tbuf); + } + } else { + vty_out(vty, " Last update: %s", ctime(&tbuf)); + } /* show paths dependent on nexthop, if needed. */ if (specific) - bgp_show_nexthop_paths(vty, bgp, bnc); + bgp_show_nexthop_paths(vty, bgp, bnc, json_nexthop); + if (json) + json_object_object_add(json, buf, json_nexthop); } static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, - bool import_table) + bool import_table, json_object *json, afi_t afi, + bool detail) { struct bgp_nexthop_cache *bnc; - afi_t afi; struct bgp_nexthop_cache_head(*tree)[AFI_MAX]; + json_object *json_afi = NULL; + bool found = false; - if (import_table) - vty_out(vty, "Current BGP import check cache:\n"); - else - vty_out(vty, "Current BGP nexthop cache:\n"); + if (!json) { + if (import_table) + vty_out(vty, "Current BGP import check cache:\n"); + else + vty_out(vty, "Current BGP nexthop cache:\n"); + } if (import_table) tree = &bgp->import_check_table; else tree = &bgp->nexthop_cache_table; + + if (afi == AFI_IP || afi == AFI_IP6) { + if (json) + json_afi = json_object_new_object(); + frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) { + bgp_show_nexthop(vty, bgp, bnc, detail, json_afi); + found = true; + } + if (found && json) + json_object_object_add( + json, (afi == AFI_IP) ? "ipv4" : "ipv6", + json_afi); + 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, false); + 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); } } static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, - const char *nhopip_str, - bool import_table) + const char *nhopip_str, bool import_table, + json_object *json, afi_t afi, bool detail) { struct bgp *bgp; - if (name) + if (name && !strmatch(name, VRF_DEFAULT_NAME)) bgp = bgp_lookup_by_name(name); else bgp = bgp_get_default(); if (!bgp) { - vty_out(vty, "%% No such BGP instance exist\n"); + if (!json) + vty_out(vty, "%% No such BGP instance exist\n"); return CMD_WARNING; } @@ -902,46 +1138,69 @@ 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)) { - vty_out(vty, "nexthop address is malformed\n"); + if (!json) + 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(); frr_each (bgp_nexthop_cache, &(*tree)[family2afi(nhop.family)], bnc) { if (prefix_cmp(&bnc->prefix, &nhop)) continue; - bgp_show_nexthop(vty, bgp, bnc, true); + bgp_show_nexthop(vty, bgp, bnc, true, json_afi); found = true; } - if (!found) + if (json) + json_object_object_add( + json, + (family2afi(nhop.family) == AFI_IP) ? "ipv4" + : "ipv6", + json_afi); + if (!found && !json) vty_out(vty, "nexthop %s does not have entry\n", nhopip_str); } else - bgp_show_nexthops(vty, bgp, import_table); + bgp_show_nexthops(vty, bgp, import_table, json, afi, detail); return CMD_SUCCESS; } -static void bgp_show_all_instances_nexthops_vty(struct vty *vty) +static void bgp_show_all_instances_nexthops_vty(struct vty *vty, + json_object *json, afi_t afi, + bool detail) { struct listnode *node, *nnode; struct bgp *bgp; + const char *inst_name; + json_object *json_instance = NULL; for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { - vty_out(vty, "\nInstance %s:\n", - (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); - bgp_show_nexthops(vty, bgp, false); + inst_name = (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + ? VRF_DEFAULT_NAME + : bgp->name; + if (json) + json_instance = json_object_new_object(); + 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); } } -DEFUN (show_ip_bgp_nexthop, +#include "bgpd/bgp_nexthop_clippy.c" + +DEFPY (show_ip_bgp_nexthop, show_ip_bgp_nexthop_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop [<A.B.C.D|X:X::X:X>] [detail]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME$vrf] nexthop [<A.B.C.D|X:X::X:X>$nhop] [<ipv4$afi [A.B.C.D$nhop]|ipv6$afi [X:X::X:X$nhop]>] [detail$detail] [json$uj]", SHOW_STR IP_STR BGP_STR @@ -949,54 +1208,85 @@ DEFUN (show_ip_bgp_nexthop, "BGP nexthop table\n" "IPv4 nexthop address\n" "IPv6 nexthop address\n" - "Show detailed information\n") + "BGP nexthop IPv4 table\n" + "IPv4 nexthop address\n" + "BGP nexthop IPv6 table\n" + "IPv6 nexthop address\n" + "Show detailed information\n" + JSON_STR) { - int idx = 0; - int nh_idx = 0; - char *vrf = NULL; - char *nhop_ip = NULL; + int rc = 0; + json_object *json = NULL; + afi_t afiz = AFI_UNSPEC; + + if (uj) + json = json_object_new_object(); - if (argv_find(argv, argc, "view", &idx) - || argv_find(argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; + if (afi) + afiz = bgp_vty_afi_from_str(afi); - if (argv_find(argv, argc, "A.B.C.D", &nh_idx) - || argv_find(argv, argc, "X:X::X:X", &nh_idx)) - nhop_ip = argv[nh_idx]->arg; + rc = show_ip_bgp_nexthop_table(vty, vrf, nhop_str, false, json, afiz, + detail); - return show_ip_bgp_nexthop_table(vty, vrf, nhop_ip, false); + if (uj) + vty_json(vty, json); + + return rc; } -DEFUN (show_ip_bgp_import_check, +DEFPY (show_ip_bgp_import_check, show_ip_bgp_import_check_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] import-check-table [detail]", + "show [ip] bgp [<view|vrf> VIEWVRFNAME$vrf] import-check-table [detail$detail] [json$uj]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR "BGP import check table\n" - "Show detailed information\n") + "Show detailed information\n" + JSON_STR) { - int idx = 0; - char *vrf = NULL; + int rc = 0; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + rc = show_ip_bgp_nexthop_table(vty, vrf, NULL, true, json, AFI_UNSPEC, + detail); - if (argv_find(argv, argc, "view", &idx) - || argv_find(argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; + if (uj) + vty_json(vty, json); - return show_ip_bgp_nexthop_table(vty, vrf, NULL, true); + return rc; } -DEFUN (show_ip_bgp_instance_all_nexthop, +DEFPY (show_ip_bgp_instance_all_nexthop, show_ip_bgp_instance_all_nexthop_cmd, - "show [ip] bgp <view|vrf> all nexthop", + "show [ip] bgp <view|vrf> all nexthop [<ipv4|ipv6>$afi] [detail$detail] [json$uj]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_ALL_HELP_STR - "BGP nexthop table\n") + "BGP nexthop table\n" + "BGP IPv4 nexthop table\n" + "BGP IPv6 nexthop table\n" + "Show detailed information\n" + JSON_STR) { - bgp_show_all_instances_nexthops_vty(vty); + json_object *json = NULL; + afi_t afiz = AFI_UNSPEC; + + if (uj) + json = json_object_new_object(); + + if (afi) + afiz = bgp_vty_afi_from_str(afi); + + bgp_show_all_instances_nexthops_vty(vty, json, afiz, detail); + + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 4bb77b5952..0ef939875a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -394,7 +394,7 @@ static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete) void bgp_path_info_add_with_caller(const char *name, struct bgp_dest *dest, struct bgp_path_info *pi) { - frrtrace(2, frr_bgp, bgp_path_info_add, dest, pi, name); + frrtrace(3, frr_bgp, bgp_path_info_add, dest, pi, name); struct bgp_path_info *top; top = bgp_dest_get_bgp_path_info(dest); diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 88f53da35e..c1f5a61438 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -215,6 +215,7 @@ clippy_scan += \ bgpd/bgp_routemap.c \ bgpd/bgp_rpki.c \ bgpd/bgp_vty.c \ + bgpd/bgp_nexthop.c \ # end nodist_bgpd_bgpd_SOURCES = \ diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index d51f06d118..5d1dda06df 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -106,6 +106,11 @@ convention. Please do not scatter individual CLI commands in the middle of source files; instead expose the necessary functions in a header and place the command definition in a ``*_vty.[ch]`` file. +.. note:: + + Please see :ref:`cli-workflow` for requirements when creating CLI commands + (e.g., JSON structure and formatting). + Definition Grammar ^^^^^^^^^^^^^^^^^^ FRR uses its own grammar for defining CLI commands. The grammar draws from diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 9cf14a1966..b4c752a473 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -71,6 +71,7 @@ EXTRA_DIST += \ doc/developer/draft-zebra-00.ms \ doc/developer/ldpd-basic-test-setup.md \ doc/developer/release-announcement-template.md \ + doc/developer/_static/overrides.css \ # end DEVBUILD = doc/developer/_build diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index b0d320b5ea..06a2ccbc0a 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -1308,6 +1308,8 @@ is to correctly set up `LD_LIBRARY_PATH` so that libraries from the build tree are used. (On some systems, `libtool` is also available from PATH, but this is not always the case.) +.. _cli-workflow: + CLI changes ----------- @@ -1374,9 +1376,23 @@ the development mailing list / public Slack instance. JSON Output ^^^^^^^^^^^ -* All JSON keys are to be camelCased, with no spaces +New JSON output in FRR needs to be backed by schema, in particular a YANG model. +When adding new JSON, first search for an existing YANG model, either in FRR or +a standard model (e.g., IETF) and use that model as the basis for any JSON +structure and *especially* for key names and canonical values formats. + +If no YANG model exists to support the JSON then an FRR YANG model needs to be +added to or created to support the JSON format. + +* All JSON keys are to be ``camelCased``, with no spaces. YANG modules almost + always use ``kebab-case`` (i.e., all lower case with hyphens to separate + words), so these identifiers need to be mapped to ``camelCase`` by removing + the hyphen (or symbol) and capitalizing the following letter, for + example "router-id" becomes "routerId" * Commands which output JSON should produce ``{}`` if they have nothing to display +* In general JSON commands include a ``json`` keyword typically at the end of + the CLI command (e.g., ``show ip ospf json``) Use of const ^^^^^^^^^^^^ diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index f751eb3a75..0c51ce2d21 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -4492,6 +4492,25 @@ Displaying Update Group Information Display Information about update-group events in FRR. +Displaying Nexthop Information +-------------------- +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop ipv4 [A.B.C.D] [detail] [json] + +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop ipv6 [X:X::X:X] [detail] [json] + +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop [<A.B.C.D|X:X::X:X>] [detail] [json] + +.. clicmd:: show [ip] bgp <view|vrf> all nexthop [json] + + Display information about nexthops to bgp neighbors. If a certain nexthop is + specified, also provides information about paths associated with the nexthop. + With detail option provides information about gates of each nexthop. + +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] import-check-table [detail] [json] + + Display information about nexthops from table that is used to check network's + existence in the rib for network statements. + Segment-Routing IPv6 -------------------- diff --git a/doc/user/subdir.am b/doc/user/subdir.am index e07c9b6dc3..706e1ea449 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -58,6 +58,8 @@ user_RSTFILES = \ EXTRA_DIST += \ $(user_RSTFILES) \ doc/user/Useful_Sysctl_Settings.md \ + doc/user/_static/overrides.css \ + doc/user/_static/overrides.js \ # end USERBUILD = doc/user/_build diff --git a/lib/command.c b/lib/command.c index 1fae32a04a..6d023142ab 100644 --- a/lib/command.c +++ b/lib/command.c @@ -502,7 +502,7 @@ static int config_write_host(struct vty *vty) else if (cputime_threshold != 5000000) #endif vty_out(vty, "service cputime-warning %lu\n", - cputime_threshold); + cputime_threshold / 1000); if (!walltime_threshold) vty_out(vty, "no service walltime-warning\n"); @@ -512,7 +512,7 @@ static int config_write_host(struct vty *vty) else if (walltime_threshold != 5000000) #endif vty_out(vty, "service walltime-warning %lu\n", - walltime_threshold); + walltime_threshold / 1000); if (host.advanced) vty_out(vty, "service advanced-vty\n"); diff --git a/lib/elf_py.c b/lib/elf_py.c index 75d2d6007f..7c503cfb9d 100644 --- a/lib/elf_py.c +++ b/lib/elf_py.c @@ -293,7 +293,7 @@ static PyObject *elfreloc_getsection(PyObject *self, PyObject *args) if (!w->es) Py_RETURN_NONE; - if (w->symidx == 0) { + if (!w->symvalid || w->symidx == 0) { size_t idx = 0; Elf_Scn *scn; diff --git a/lib/xref.h b/lib/xref.h index 0e3f00f690..37242bd79e 100644 --- a/lib/xref.h +++ b/lib/xref.h @@ -208,8 +208,19 @@ extern const struct xref * const __stop_xref_array[1] DSO_LOCAL; * some build issue with it just add -DFRR_XREF_NO_NOTE to your build flags * to disable it. */ -#ifdef FRR_XREF_NO_NOTE +#if defined(FRR_XREF_NO_NOTE) || defined(__mips64) #define XREF_NOTE "" + +/* mips64 note: MIPS64 (regardless of endianness, both mips64 & mips64el) + * does not have a 64-bit PC-relative relocation type. Unfortunately, a + * 64-bit PC-relative relocation is exactly what the below asm magic emits. + * Therefore, the xref ELF note is permanently disabled on MIPS64. + * + * For some context, refer to https://reviews.llvm.org/D80390 + * + * As noted above, xref extraction still works through the section header + * path, so no functionality is lost. + */ #else #if __SIZEOF_POINTER__ == 4 diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 0cb9d02725..b22fe5d99b 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1787,9 +1787,10 @@ static struct list *ospf_ls_upd_list_lsa(struct ospf_neighbor *nbr, continue; } } else if (IS_OPAQUE_LSA(lsah->type)) { - flog_warn(EC_OSPF_PACKET, - "LSA[Type%d:%pI4]: Opaque capability mismatch?", - lsah->type, &lsah->id); + flog_warn( + EC_OSPF_PACKET, + "LSA[Type%d:%pI4] from %pI4: Opaque capability mismatch?", + lsah->type, &lsah->id, &lsah->adv_router); continue; } diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 337113988e..af75ddf742 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -604,7 +604,10 @@ static void fpm_read(struct thread *t) hdr, 0, false, ctx) != 1) { dplane_ctx_fini(&ctx); stream_pulldown(fnc->ibuf); - return; + /* + * Let's continue to read other messages + * Even if we ignore this one. + */ } break; default: @@ -863,7 +866,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_NH_DELETE: rv = netlink_nexthop_msg_encode(RTM_DELNEXTHOP, ctx, nl_buf, - sizeof(nl_buf)); + sizeof(nl_buf), true); if (rv <= 0) { zlog_err("%s: netlink_nexthop_msg_encode failed", __func__); @@ -875,7 +878,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_NH_INSTALL: case DPLANE_OP_NH_UPDATE: rv = netlink_nexthop_msg_encode(RTM_NEWNEXTHOP, ctx, nl_buf, - sizeof(nl_buf)); + sizeof(nl_buf), true); if (rv <= 0) { zlog_err("%s: netlink_nexthop_msg_encode failed", __func__); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 33fe8db99e..10725665e8 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2501,7 +2501,7 @@ static bool _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, */ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, - void *buf, size_t buflen) + void *buf, size_t buflen, bool fpm) { struct { struct nlmsghdr n; @@ -2528,9 +2528,10 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, /* * Nothing to do if the kernel doesn't support nexthop objects or - * we dont want to install this type of NHG + * we dont want to install this type of NHG, but FPM may possible to + * handle this. */ - if (!kernel_nexthops_supported()) { + if (!fpm && !kernel_nexthops_supported()) { if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG) zlog_debug( "%s: nhg_id %u (%s): kernel nexthops not supported, ignoring", @@ -2850,7 +2851,7 @@ static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx, return -1; } - return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen); + return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen, false); } enum netlink_msg_status diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index fd2b79a2bf..b67169d6f0 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -84,7 +84,7 @@ extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_nexthop_read(struct zebra_ns *zns); extern ssize_t netlink_nexthop_msg_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, - void *buf, size_t buflen); + void *buf, size_t buflen, bool fpm); extern ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, size_t buflen); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f1c7debe11..12fec158b0 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3511,6 +3511,12 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json = json_object_new_object(); json_object_string_add(json, "advertiseGatewayMacip", zvrf->advertise_gw_macip ? "Yes" : "No"); + json_object_string_add(json, "advertiseSviMacip", + zvrf->advertise_svi_macip ? "Yes" + : "No"); + json_object_string_add(json, "advertiseSviMac", + zebra_evpn_mh_do_adv_svi_mac() ? "Yes" + : "No"); json_object_int_add(json, "numVnis", num_vnis); json_object_int_add(json, "numL2Vnis", num_l2vnis); json_object_int_add(json, "numL3Vnis", num_l3vnis); diff --git a/zebra/zserv.c b/zebra/zserv.c index d4fa6dadae..2024f34534 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -232,14 +232,16 @@ static void zserv_write(struct thread *thread) struct stream *msg; uint32_t wcmd = 0; struct stream_fifo *cache; + uint64_t time_now = monotime(NULL); /* If we have any data pending, try to flush it first */ switch (buffer_flush_all(client->wb, client->sock)) { case BUFFER_ERROR: goto zwrite_fail; case BUFFER_PENDING: - atomic_store_explicit(&client->last_write_time, monotime(NULL), - memory_order_relaxed); + frr_with_mutex (&client->stats_mtx) { + client->last_write_time = time_now; + } zserv_client_event(client, ZSERV_CLIENT_WRITE); return; case BUFFER_EMPTY: @@ -273,20 +275,19 @@ static void zserv_write(struct thread *thread) case BUFFER_ERROR: goto zwrite_fail; case BUFFER_PENDING: - atomic_store_explicit(&client->last_write_time, monotime(NULL), - memory_order_relaxed); + frr_with_mutex (&client->stats_mtx) { + client->last_write_time = time_now; + } zserv_client_event(client, ZSERV_CLIENT_WRITE); return; case BUFFER_EMPTY: break; } - atomic_store_explicit(&client->last_write_cmd, wcmd, - memory_order_relaxed); - - atomic_store_explicit(&client->last_write_time, monotime(NULL), - memory_order_relaxed); - + frr_with_mutex (&client->stats_mtx) { + client->last_write_cmd = wcmd; + client->last_write_time = time_now; + } return; zwrite_fail: @@ -433,11 +434,13 @@ static void zserv_read(struct thread *thread) } if (p2p < p2p_orig) { + uint64_t time_now = monotime(NULL); + /* update session statistics */ - atomic_store_explicit(&client->last_read_time, monotime(NULL), - memory_order_relaxed); - atomic_store_explicit(&client->last_read_cmd, hdr.command, - memory_order_relaxed); + frr_with_mutex (&client->stats_mtx) { + client->last_read_time = time_now; + client->last_read_cmd = hdr.command; + } /* publish read packets on client's input queue */ frr_with_mutex (&client->ibuf_mtx) { @@ -631,6 +634,7 @@ static void zserv_client_free(struct zserv *client) buffer_free(client->wb); /* Free buffer mutexes */ + pthread_mutex_destroy(&client->stats_mtx); pthread_mutex_destroy(&client->obuf_mtx); pthread_mutex_destroy(&client->ibuf_mtx); @@ -751,14 +755,13 @@ static struct zserv *zserv_client_create(int sock) client->obuf_fifo = stream_fifo_new(); client->ibuf_work = stream_new(stream_size); client->obuf_work = stream_new(stream_size); + client->connect_time = monotime(NULL); pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); + pthread_mutex_init(&client->stats_mtx, NULL); client->wb = buffer_new(0); TAILQ_INIT(&(client->gr_info_queue)); - atomic_store_explicit(&client->connect_time, monotime(NULL), - memory_order_relaxed); - /* Initialize flags */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) @@ -1019,8 +1022,14 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "------------------------ \n"); vty_out(vty, "FD: %d \n", client->sock); - connect_time = (time_t) atomic_load_explicit(&client->connect_time, - memory_order_relaxed); + frr_with_mutex (&client->stats_mtx) { + connect_time = client->connect_time; + last_read_time = client->last_read_time; + last_write_time = client->last_write_time; + + last_read_cmd = client->last_read_cmd; + last_write_cmd = client->last_write_cmd; + } vty_out(vty, "Connect Time: %s \n", zserv_time_buf(&connect_time, cbuf, ZEBRA_TIME_BUF)); @@ -1041,16 +1050,6 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) "Client will %sbe notified about the status of its routes.\n", client->notify_owner ? "" : "Not "); - last_read_time = (time_t)atomic_load_explicit(&client->last_read_time, - memory_order_relaxed); - last_write_time = (time_t)atomic_load_explicit(&client->last_write_time, - memory_order_relaxed); - - last_read_cmd = atomic_load_explicit(&client->last_read_cmd, - memory_order_relaxed); - last_write_cmd = atomic_load_explicit(&client->last_write_cmd, - memory_order_relaxed); - vty_out(vty, "Last Msg Rx Time: %s \n", zserv_time_buf(&last_read_time, rbuf, ZEBRA_TIME_BUF)); vty_out(vty, "Last Msg Tx Time: %s \n", @@ -1184,12 +1183,11 @@ static void zebra_show_client_brief(struct vty *vty, struct zserv *client) char wbuf[ZEBRA_TIME_BUF]; time_t connect_time, last_read_time, last_write_time; - connect_time = (time_t)atomic_load_explicit(&client->connect_time, - memory_order_relaxed); - last_read_time = (time_t)atomic_load_explicit(&client->last_read_time, - memory_order_relaxed); - last_write_time = (time_t)atomic_load_explicit(&client->last_write_time, - memory_order_relaxed); + frr_with_mutex (&client->stats_mtx) { + connect_time = client->connect_time; + last_read_time = client->last_read_time; + last_write_time = client->last_write_time; + } if (client->instance || client->session_id) snprintfrr(client_string, sizeof(client_string), "%s[%u:%u]", diff --git a/zebra/zserv.h b/zebra/zserv.h index de784e382a..cfc33f9169 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -215,16 +215,21 @@ struct zserv { * relative to last_read_time. */ + pthread_mutex_t stats_mtx; + /* BEGIN covered by stats_mtx */ + /* monotime of client creation */ - _Atomic uint64_t connect_time; + uint64_t connect_time; /* monotime of last message received */ - _Atomic uint64_t last_read_time; + uint64_t last_read_time; /* monotime of last message sent */ - _Atomic uint64_t last_write_time; + uint64_t last_write_time; /* command code of last message read */ - _Atomic uint64_t last_read_cmd; + uint64_t last_read_cmd; /* command code of last message written */ - _Atomic uint64_t last_write_cmd; + uint64_t last_write_cmd; + + /* END covered by stats_mtx */ /* * Number of instances configured with |
