]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_route.c
Merge pull request #5717 from pguibert6WIND/flowspec_issue_redistribute
[mirror_frr.git] / bgpd / bgp_route.c
index bd920740c0bed15fba0260d128d3a978d0ac00e1..66aa0da745767e61f693751474783650d0d94c79 100644 (file)
 #include "workqueue.h"
 #include "queue.h"
 #include "memory.h"
+#include "srv6.h"
 #include "lib/json.h"
 #include "lib_errors.h"
-
+#include "zclient.h"
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_table.h"
 #include "bgpd/bgp_route.h"
@@ -89,7 +90,8 @@
 /* Extern from bgp_dump.c */
 extern const char *bgp_origin_str[];
 extern const char *bgp_origin_long_str[];
-
+const char *get_afi_safi_str(afi_t afi,
+               safi_t safi, bool for_json);
 /* PMSI strings. */
 #define PMSI_TNLTYPE_STR_NO_INFO "No info"
 #define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO
@@ -295,6 +297,85 @@ struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path)
        return path;
 }
 
+/* This function sets flag BGP_NODE_SELECT_DEFER based on condition */
+static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete)
+{
+       struct peer *peer;
+       struct bgp_path_info *old_pi, *nextpi;
+       bool set_flag = 0;
+       struct bgp *bgp = NULL;
+       struct bgp_table *table = NULL;
+       afi_t afi = 0;
+       safi_t safi = 0;
+       char buf[PREFIX2STR_BUFFER];
+
+       /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added
+        * then the route selection is deferred
+        */
+       if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER) && (!delete))
+               return 0;
+
+       if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) {
+               if (BGP_DEBUG(update, UPDATE_OUT)) {
+                       prefix2str(&rn->p, buf, PREFIX2STR_BUFFER);
+                       zlog_debug("Route %s is in workqueue and being processed, not deferred.",
+                               buf);
+               }
+               return 0;
+       }
+
+       table = bgp_node_table(rn);
+       if (table) {
+               bgp = table->bgp;
+               afi = table->afi;
+               safi = table->safi;
+       }
+
+       for (old_pi = bgp_node_get_bgp_path_info(rn);
+            (old_pi != NULL) && (nextpi = old_pi->next, 1); old_pi = nextpi) {
+               if (CHECK_FLAG(old_pi->flags, BGP_PATH_SELECTED))
+                       continue;
+
+               /* Route selection is deferred if there is a stale path which
+                * which indicates peer is in restart mode
+                */
+               if (CHECK_FLAG(old_pi->flags, BGP_PATH_STALE) &&
+                               (old_pi->sub_type == BGP_ROUTE_NORMAL)) {
+                       set_flag = 1;
+               } else {
+                       /* If the peer is graceful restart capable and peer is
+                        * restarting mode, set the flag BGP_NODE_SELECT_DEFER
+                        */
+                       peer = old_pi->peer;
+                       if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) &&
+                           BGP_PEER_RESTARTING_MODE(peer) &&
+                           (old_pi && old_pi->sub_type == BGP_ROUTE_NORMAL)) {
+                               set_flag = 1;
+                       }
+               }
+               if (set_flag)
+                       break;
+       }
+
+       /* Set the flag BGP_NODE_SELECT_DEFER if route selection deferral timer
+        * is active
+        */
+       if (set_flag && table) {
+               if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) {
+                       SET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER);
+                       prefix2str(&rn->p, buf, PREFIX2STR_BUFFER);
+                       if (rn->rt_node == NULL)
+                               rn->rt_node = listnode_add(
+                                       bgp->gr_info[afi][safi].route_list, rn);
+                       if (BGP_DEBUG(update, UPDATE_OUT))
+                               zlog_debug("DEFER route %s, rn %p, node %p",
+                                               buf, rn, rn->rt_node);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
 void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi)
 {
        struct bgp_path_info *top;
@@ -310,6 +391,7 @@ void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi)
        bgp_path_info_lock(pi);
        bgp_lock_node(rn);
        peer_lock(pi->peer); /* bgp_path_info peer reference */
+       bgp_node_set_defer_flag(rn, false);
 }
 
 /* Do the actual removal of info from RIB, for use by bgp_process
@@ -1695,7 +1777,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
        }
 
        /* For modify attribute, copy it to temporary structure. */
-       bgp_attr_dup(attr, piattr);
+       *attr = *piattr;
 
        /* If local-preference is not set. */
        if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED)
@@ -1815,7 +1897,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
                if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
                    && !bgp_flag_check(bgp,
                                       BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
-                       bgp_attr_dup(&dummy_attr, attr);
+                       dummy_attr = *attr;
                        rmap_path.attr = &dummy_attr;
                }
 
@@ -1854,6 +1936,16 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
                if (!bgp_outbound_policy_exists(peer, filter))
                        return 0;
 
+       /* draft-ietf-idr-deprecate-as-set-confed-set
+        * Filter routes having AS_SET or AS_CONFED_SET in the path.
+        * Eventually, This document (if approved) updates RFC 4271
+        * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types,
+        * and obsoletes RFC 6472.
+        */
+       if (peer->bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
+               if (aspath_check_as_sets(attr->aspath))
+                       return 0;
+
        if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
                if (peer->sort == BGP_PEER_IBGP
                    || peer->sort == BGP_PEER_CONFED) {
@@ -1963,6 +2055,30 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
        return 1;
 }
 
+static int bgp_route_select_timer_expire(struct thread *thread)
+{
+       struct afi_safi_info *info;
+       afi_t afi;
+       safi_t safi;
+       struct bgp *bgp;
+
+       info = THREAD_ARG(thread);
+       afi = info->afi;
+       safi = info->safi;
+       bgp = info->bgp;
+
+       if (BGP_DEBUG(update, UPDATE_OUT))
+               zlog_debug("afi %d, safi %d : route select timer expired",
+                               afi, safi);
+
+       bgp->gr_info[afi][safi].t_route_select = NULL;
+
+       XFREE(MTYPE_TMP, info);
+
+       /* Best path selection */
+       return bgp_best_path_select_defer(bgp, afi, safi);
+}
+
 void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
                        struct bgp_maxpaths_cfg *mpath_cfg,
                        struct bgp_path_info_pair *result, afi_t afi,
@@ -2366,6 +2482,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
                           afi2str(afi), safi2str(safi));
        }
 
+       /* The best path calculation for the route is deferred if
+        * BGP_NODE_SELECT_DEFER is set
+        */
+       if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) {
+               if (BGP_DEBUG(update, UPDATE_OUT))
+                       zlog_debug("SELECT_DEFER falg set for route %p", rn);
+               return;
+       }
+
        /* Best path selection. */
        bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new,
                           afi, safi);
@@ -2593,6 +2718,65 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
        return;
 }
 
+/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */
+int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+       struct bgp_node *rn;
+       int cnt = 0;
+       struct afi_safi_info *thread_info;
+       struct listnode *node = NULL, *nnode = NULL;
+
+       if (bgp->gr_info[afi][safi].t_route_select)
+               BGP_TIMER_OFF(bgp->gr_info[afi][safi].t_route_select);
+
+       if (BGP_DEBUG(update, UPDATE_OUT)) {
+               zlog_debug("%s: processing route for %s : cnt %d",
+                          __func__, get_afi_safi_str(afi, safi, false),
+                               listcount(bgp->gr_info[afi][safi].route_list));
+       }
+
+       /* Process the route list */
+       node = listhead(bgp->gr_info[afi][safi].route_list);
+       while (node) {
+               rn = listgetdata(node);
+               nnode = node->next;
+               list_delete_node(bgp->gr_info[afi][safi].route_list, node);
+               rn->rt_node = NULL;
+
+               if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) {
+                       UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER);
+                       bgp_process_main_one(bgp, rn, afi, safi);
+                       cnt++;
+                       if (cnt >= BGP_MAX_BEST_ROUTE_SELECT)
+                               break;
+               }
+               node = nnode;
+       }
+
+       /* Send EOR message when all routes are processed */
+       if (list_isempty(bgp->gr_info[afi][safi].route_list)) {
+               bgp_send_delayed_eor(bgp);
+               /* Send route processing complete message to RIB */
+               bgp_zebra_update(afi, safi, bgp->vrf_id,
+                               ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE);
+               return 0;
+       }
+
+       thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info));
+
+       thread_info->afi = afi;
+       thread_info->safi = safi;
+       thread_info->bgp = bgp;
+
+       /* If there are more routes to be processed, start the
+        * selection timer
+        */
+       thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info,
+                       BGP_ROUTE_SELECT_DELAY,
+                       &bgp->gr_info[afi][safi].t_route_select);
+       return 0;
+}
+
 static wq_item_status bgp_process_wq(struct work_queue *wq, void *data)
 {
        struct bgp_process_queue *pqnode = data;
@@ -2671,6 +2855,16 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi)
        if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED))
                return;
 
+       /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to
+        * the workqueue
+        */
+       if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) {
+               if (BGP_DEBUG(update, UPDATE_OUT))
+                       zlog_debug("BGP_NODE_SELECT_DEFER set for route %p",
+                                   rn);
+               return;
+       }
+
        if (wq == NULL)
                return;
 
@@ -2834,13 +3028,41 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
 void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi,
                    struct peer *peer, afi_t afi, safi_t safi)
 {
+
+       struct bgp *bgp = NULL;
+       bool delete_route = false;
+
        bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, safi);
 
-       if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+       if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
                bgp_path_info_delete(rn, pi); /* keep historical info */
 
-       hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true);
+               /* If the selected path is removed, reset BGP_NODE_SELECT_DEFER
+                * flag
+                */
+               if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+                       delete_route = true;
+               else
+                       if (bgp_node_set_defer_flag(rn, true) < 0)
+                               delete_route = true;
+               if (delete_route) {
+                       if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) {
+                               UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER);
+                               bgp = pi->peer->bgp;
+                               if ((rn->rt_node) &&
+                                       (bgp->gr_info[afi][safi]
+                                       .route_list)) {
+                                       list_delete_node(
+                                               bgp->gr_info[afi][safi]
+                                               .route_list,
+                                               rn->rt_node);
+                                       rn->rt_node = NULL;
+                               }
+                       }
+               }
+       }
 
+       hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true);
        bgp_process(peer->bgp, rn, afi, safi);
 }
 
@@ -3155,7 +3377,20 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                        goto filtered;
                }
 
-       bgp_attr_dup(&new_attr, attr);
+       /* draft-ietf-idr-deprecate-as-set-confed-set
+        * Filter routes having AS_SET or AS_CONFED_SET in the path.
+        * Eventually, This document (if approved) updates RFC 4271
+        * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types,
+        * and obsoletes RFC 6472.
+        */
+       if (peer->bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
+               if (aspath_check_as_sets(attr->aspath)) {
+                       reason =
+                               "as-path contains AS_SET or AS_CONFED_SET type;";
+                       goto filtered;
+               }
+
+       new_attr = *attr;
 
        /* Apply incoming route-map.
         * NB: new_attr may now contain newly allocated values from route-map
@@ -3279,6 +3514,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                                if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) {
                                        bgp_path_info_unset_flag(
                                                rn, pi, BGP_PATH_STALE);
+                                       bgp_node_set_defer_flag(rn, false);
                                        bgp_process(bgp, rn, afi, safi);
                                }
                        }
@@ -3314,8 +3550,10 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                }
 
                /* graceful restart STALE flag unset. */
-               if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
+               if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) {
                        bgp_path_info_unset_flag(rn, pi, BGP_PATH_STALE);
+                       bgp_node_set_defer_flag(rn, false);
+               }
 
                /* The attribute is changed. */
                bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED);
@@ -3408,6 +3646,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                                bgp_set_valid_label(&extra->label[0]);
                }
 
+               /* Update SRv6 SID */
+               if (attr->srv6_l3vpn) {
+                       extra = bgp_path_info_extra_get(pi);
+                       if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) {
+                               sid_copy(&extra->sid[0],
+                                        &attr->srv6_l3vpn->sid);
+                               extra->num_sids = 1;
+                       }
+               } else if (attr->srv6_vpn) {
+                       extra = bgp_path_info_extra_get(pi);
+                       if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) {
+                               sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+                               extra->num_sids = 1;
+                       }
+               }
+
 #if ENABLE_BGP_VNC
                if ((afi == AFI_IP || afi == AFI_IP6)
                    && (safi == SAFI_UNICAST)) {
@@ -3587,6 +3841,18 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,
                        bgp_set_valid_label(&extra->label[0]);
        }
 
+       /* Update SRv6 SID */
+       if (safi == SAFI_MPLS_VPN) {
+               extra = bgp_path_info_extra_get(new);
+               if (attr->srv6_l3vpn) {
+                       sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid);
+                       extra->num_sids = 1;
+               } else if (attr->srv6_vpn) {
+                       sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+                       extra->num_sids = 1;
+               }
+       }
+
        /* Update Overlay Index */
        if (afi == AFI_L2VPN) {
                overlay_index_update(new->attr,
@@ -4334,6 +4600,9 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
 
 int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 {
+       if (peer->sort == BGP_PEER_IBGP)
+               return 1;
+
        if (peer->sort == BGP_PEER_EBGP
            && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter)
                || FILTER_LIST_OUT_NAME(filter)
@@ -4344,6 +4613,9 @@ int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 
 int bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
 {
+       if (peer->sort == BGP_PEER_IBGP)
+               return 1;
+
        if (peer->sort == BGP_PEER_EBGP
            && (ROUTE_MAP_IN_NAME(filter) || PREFIX_LIST_IN_NAME(filter)
                || FILTER_LIST_IN_NAME(filter)
@@ -4486,7 +4758,8 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
                        if (pnt + BGP_ADDPATH_ID_LEN >= lim)
                                return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
 
-                       addpath_id = ntohl(*((uint32_t *)pnt));
+                       memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+                       addpath_id = ntohl(addpath_id);
                        pnt += BGP_ADDPATH_ID_LEN;
                }
 
@@ -6392,6 +6665,7 @@ void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p,
 /* Aggregate route attribute. */
 #define AGGREGATE_SUMMARY_ONLY 1
 #define AGGREGATE_AS_SET       1
+#define AGGREGATE_AS_UNSET     0
 
 static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str,
                               afi_t afi, safi_t safi)
@@ -6494,6 +6768,7 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi,
        struct prefix p;
        struct bgp_node *rn;
        struct bgp_aggregate *aggregate;
+       uint8_t as_set_new = as_set;
 
        /* Convert string to prefix structure. */
        ret = str2prefix(prefix_str, &p);
@@ -6528,7 +6803,27 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi,
        /* Make aggregate address structure. */
        aggregate = bgp_aggregate_new();
        aggregate->summary_only = summary_only;
-       aggregate->as_set = as_set;
+
+       /* Network operators MUST NOT locally generate any new
+        * announcements containing AS_SET or AS_CONFED_SET. If they have
+        * announced routes with AS_SET or AS_CONFED_SET in them, then they
+        * SHOULD withdraw those routes and re-announce routes for the
+        * aggregate or component prefixes (i.e., the more-specific routes
+        * subsumed by the previously aggregated route) without AS_SET
+        * or AS_CONFED_SET in the updates.
+        */
+       if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) {
+               if (as_set == AGGREGATE_AS_SET) {
+                       as_set_new = AGGREGATE_AS_UNSET;
+                       zlog_warn(
+                               "%s: Ignoring as-set because `bgp reject-as-sets` is enabled.\n",
+                               __func__);
+                       vty_out(vty,
+                               "Ignoring as-set because `bgp reject-as-sets` is enabled.\n");
+               }
+       }
+
+       aggregate->as_set = as_set_new;
        aggregate->safi = safi;
 
        if (rmap) {
@@ -6563,8 +6858,8 @@ DEFUN (aggregate_address,
        argv_find(argv, argc, "A.B.C.D/M", &idx);
        char *prefix = argv[idx]->arg;
        char *rmap = NULL;
-       int as_set =
-               argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0;
+       int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET
+                                                          : AGGREGATE_AS_UNSET;
        idx = 0;
        int summary_only = argv_find(argv, argc, "summary-only", &idx)
                                   ? AGGREGATE_SUMMARY_ONLY
@@ -6598,8 +6893,8 @@ DEFUN (aggregate_address_mask,
        char *mask = argv[idx + 1]->arg;
        bool rmap_found;
        char *rmap = NULL;
-       int as_set =
-               argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0;
+       int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET
+                                                          : AGGREGATE_AS_UNSET;
        idx = 0;
        int summary_only = argv_find(argv, argc, "summary-only", &idx)
                                   ? AGGREGATE_SUMMARY_ONLY
@@ -6687,8 +6982,8 @@ DEFUN (ipv6_aggregate_address,
        char *prefix = argv[idx]->arg;
        char *rmap = NULL;
        bool rmap_found;
-       int as_set =
-               argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0;
+       int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET
+                                                          : AGGREGATE_AS_UNSET;
 
        idx = 0;
        int sum_only = argv_find(argv, argc, "summary-only", &idx)
@@ -6784,7 +7079,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
                struct attr attr_new;
 
                /* Copy attribute for modification. */
-               bgp_attr_dup(&attr_new, &attr);
+               attr_new = attr;
 
                if (red->redist_metric_flag)
                        attr_new.med = red->redist_metric;
@@ -7103,9 +7398,10 @@ static void route_vty_short_status_out(struct vty *vty,
                vty_out(vty, " ");
 }
 
-static char *bgp_nexthop_fqdn(struct peer *peer)
+static char *bgp_nexthop_hostname(struct peer *peer, struct attr *attr)
 {
-       if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME))
+       if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)
+           && !(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))
                return peer->hostname;
        return NULL;
 }
@@ -7115,7 +7411,7 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                   struct bgp_path_info *path, int display, safi_t safi,
                   json_object *json_paths)
 {
-       struct attr *attr;
+       struct attr *attr = path->attr;
        json_object *json_path = NULL;
        json_object *json_nexthops = NULL;
        json_object *json_nexthop_global = NULL;
@@ -7127,7 +7423,7 @@ void route_vty_out(struct vty *vty, struct prefix *p,
        bool nexthop_othervrf = false;
        vrf_id_t nexthop_vrfid = VRF_DEFAULT;
        const char *nexthop_vrfname = VRF_DEFAULT_NAME;
-       char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
+       char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr);
 
        if (json_paths)
                json_path = json_object_new_object();
@@ -7145,9 +7441,6 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                route_vty_out_route(p, vty, json_path);
        }
 
-       /* Print attribute */
-       attr = path->attr;
-
        /*
         * If vrf id of nexthop is different from that of prefix,
         * set up printable string to append
@@ -7215,57 +7508,67 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(
-                               json_nexthop_global, "afi",
-                               nexthop_fqdn ? "fqdn"
-                                            : (af == AF_INET) ? "ip" : "ipv6");
-                       json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn"
-                                            : (af == AF_INET) ? "ip" : "ipv6",
-                               nexthop_fqdn ? nexthop_fqdn : nexthop);
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              nexthop);
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
+                       json_object_string_add(json_nexthop_global, "afi",
+                                              (af == AF_INET) ? "ipv4"
+                                                              : "ipv6");
                        json_object_boolean_true_add(json_nexthop_global,
                                                     "used");
                } else
                        vty_out(vty, "%s%s",
-                               nexthop_fqdn ? nexthop_fqdn : nexthop,
+                               nexthop_hostname ? nexthop_hostname : nexthop,
                                vrf_id_str);
        } else if (safi == SAFI_EVPN) {
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn ? nexthop_fqdn
-                                            : inet_ntoa(attr->nexthop));
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv4");
                        json_object_boolean_true_add(json_nexthop_global,
                                                     "used");
                } else
                        vty_out(vty, "%-16s%s",
-                               nexthop_fqdn ?: inet_ntoa(attr->nexthop),
+                               nexthop_hostname ? nexthop_hostname
+                                                : inet_ntoa(attr->nexthop),
                                vrf_id_str);
        } else if (safi == SAFI_FLOWSPEC) {
                if (attr->nexthop.s_addr != 0) {
                        if (json_paths) {
                                json_nexthop_global = json_object_new_object();
-                               json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(attr->nexthop));
+
                                json_object_string_add(json_nexthop_global,
                                                       "afi", "ipv4");
+                               json_object_string_add(
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+
                                json_object_boolean_true_add(
                                                        json_nexthop_global,
                                                             "used");
                        } else {
                                vty_out(vty, "%-16s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(attr->nexthop));
                        }
                }
@@ -7273,11 +7576,13 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
 
-                       json_object_string_add(json_nexthop_global,
-                                              nexthop_fqdn ? "fqdn" : "ip",
-                                              nexthop_fqdn
-                                              ? nexthop_fqdn
-                                              : inet_ntoa(attr->nexthop));
+                       json_object_string_add(json_nexthop_global, "ip",
+                                              inet_ntoa(attr->nexthop));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
 
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv4");
@@ -7287,8 +7592,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                        char buf[BUFSIZ];
 
                        snprintf(buf, sizeof(buf), "%s%s",
-                                nexthop_fqdn ? nexthop_fqdn
-                                             : inet_ntoa(attr->nexthop),
+                                nexthop_hostname ? nexthop_hostname
+                                                 : inet_ntoa(attr->nexthop),
                                 vrf_id_str);
                        vty_out(vty, "%-16s", buf);
                }
@@ -7302,13 +7607,15 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                if (json_paths) {
                        json_nexthop_global = json_object_new_object();
                        json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_global,
-                                                   buf, BUFSIZ));
+                               json_nexthop_global, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, BUFSIZ));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv6");
                        json_object_string_add(json_nexthop_global, "scope",
@@ -7321,14 +7628,16 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                            || (path->peer->conf_if)) {
                                json_nexthop_ll = json_object_new_object();
                                json_object_string_add(
-                                       json_nexthop_ll,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntop(
-                                                         AF_INET6,
-                                                         &attr->mp_nexthop_local,
-                                                         buf, BUFSIZ));
+                                       json_nexthop_ll, "ip",
+                                       inet_ntop(AF_INET6,
+                                                 &attr->mp_nexthop_local, buf,
+                                                 BUFSIZ));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_ll, "hostname",
+                                               nexthop_hostname);
+
                                json_object_string_add(json_nexthop_ll, "afi",
                                                       "ipv6");
                                json_object_string_add(json_nexthop_ll, "scope",
@@ -7368,8 +7677,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                                } else {
                                        len = vty_out(
                                                vty, "%s%s",
-                                               nexthop_fqdn
-                                                       ? nexthop_fqdn
+                                               nexthop_hostname
+                                                       ? nexthop_hostname
                                                        : inet_ntop(
                                                                  AF_INET6,
                                                                  &attr->mp_nexthop_local,
@@ -7385,8 +7694,8 @@ void route_vty_out(struct vty *vty, struct prefix *p,
                        } else {
                                len = vty_out(
                                        vty, "%s%s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntop(
                                                          AF_INET6,
                                                          &attr->mp_nexthop_global,
@@ -8217,7 +8526,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        char buf[INET6_ADDRSTRLEN];
        char buf1[BUFSIZ];
        char buf2[EVPN_ROUTE_STRLEN];
-       struct attr *attr;
+       struct attr *attr = path->attr;
        int sockunion_vty_out(struct vty *, union sockunion *);
        time_t tbuf;
        json_object *json_bestpath = NULL;
@@ -8242,7 +8551,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        bool nexthop_self =
                CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
        int i;
-       char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer);
+       char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr);
 
        if (json_paths) {
                json_path = json_object_new_object();
@@ -8295,8 +8604,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                }
        }
 
-       attr = path->attr;
-
        /* Line1 display AS-path, Aggregator */
        if (attr->aspath) {
                if (json_paths) {
@@ -8384,32 +8691,35 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
                if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
                    || safi == SAFI_EVPN) {
-                       if (json_paths)
+                       if (json_paths) {
                                json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(
-                                                       attr->mp_nexthop_global_in));
-                       else
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->mp_nexthop_global_in));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+                       } else
                                vty_out(vty, "    %s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(
-                                                       attr->mp_nexthop_global_in));
+                                                         attr->mp_nexthop_global_in));
                } else {
-                       if (json_paths)
+                       if (json_paths) {
                                json_object_string_add(
-                                       json_nexthop_global,
-                                       nexthop_fqdn ? "fqdn" : "ip",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
-                                               : inet_ntoa(attr->nexthop));
-                       else
+                                       json_nexthop_global, "ip",
+                                       inet_ntoa(attr->nexthop));
+
+                               if (nexthop_hostname)
+                                       json_object_string_add(
+                                               json_nexthop_global, "hostname",
+                                               nexthop_hostname);
+                       } else
                                vty_out(vty, "    %s",
-                                       nexthop_fqdn
-                                               ? nexthop_fqdn
+                                       nexthop_hostname
+                                               ? nexthop_hostname
                                                : inet_ntoa(attr->nexthop));
                }
 
@@ -8419,21 +8729,23 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
        } else {
                if (json_paths) {
                        json_object_string_add(
-                               json_nexthop_global,
-                               nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_global,
-                                                   buf, INET6_ADDRSTRLEN));
+                               json_nexthop_global, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+                                         buf, INET6_ADDRSTRLEN));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_global,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_global, "afi",
                                               "ipv6");
                        json_object_string_add(json_nexthop_global, "scope",
                                               "global");
                } else {
                        vty_out(vty, "    %s",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
+                               nexthop_hostname
+                                       ? nexthop_hostname
                                        : inet_ntop(AF_INET6,
                                                    &attr->mp_nexthop_global,
                                                    buf, INET6_ADDRSTRLEN));
@@ -8605,12 +8917,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                if (json_paths) {
                        json_nexthop_ll = json_object_new_object();
                        json_object_string_add(
-                               json_nexthop_ll, nexthop_fqdn ? "fqdn" : "ip",
-                               nexthop_fqdn
-                                       ? nexthop_fqdn
-                                       : inet_ntop(AF_INET6,
-                                                   &attr->mp_nexthop_local,
-                                                   buf, INET6_ADDRSTRLEN));
+                               json_nexthop_ll, "ip",
+                               inet_ntop(AF_INET6, &attr->mp_nexthop_local,
+                                         buf, INET6_ADDRSTRLEN));
+
+                       if (nexthop_hostname)
+                               json_object_string_add(json_nexthop_ll,
+                                                      "hostname",
+                                                      nexthop_hostname);
+
                        json_object_string_add(json_nexthop_ll, "afi", "ipv6");
                        json_object_string_add(json_nexthop_ll, "scope",
                                               "link-local");
@@ -8922,6 +9237,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
                        vty_out(vty, "      Remote label: %d\n", label);
        }
 
+       /* Remote SID */
+       if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) {
+               inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf));
+               if (json_paths)
+                       json_object_string_add(json_path, "remoteSid", buf);
+               else
+                       vty_out(vty, "      Remote SID: %s\n", buf);
+       }
+
        /* Label Index */
        if (attr->label_index != BGP_INVALID_LABEL_INDEX) {
                if (json_paths)
@@ -9076,7 +9400,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
                                  const char *prefix, afi_t afi, safi_t safi,
                                  enum bgp_show_type type);
 static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
-                          afi_t afi, safi_t safi, enum bgp_show_type type);
+                          afi_t afi, safi_t safi, enum bgp_show_type type,
+                          bool use_json);
 static int bgp_show_community(struct vty *vty, struct bgp *bgp,
                              const char *comstr, int exact, afi_t afi,
                              safi_t safi, bool use_json);
@@ -9173,7 +9498,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                struct attr dummy_attr;
                                route_map_result_t ret;
 
-                               bgp_attr_dup(&dummy_attr, pi->attr);
+                               dummy_attr = *pi->attr;
 
                                path.peer = pi->peer;
                                path.attr = &dummy_attr;
@@ -9367,7 +9692,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                        vty_out(vty, ",\"%s\": ", buf2);
                        }
                        vty_out(vty, "%s",
-                               json_object_to_json_string(json_paths));
+                               json_object_to_json_string_ext(
+                                       json_paths, JSON_C_TO_STRING_PRETTY));
                        json_object_free(json_paths);
                        json_paths = NULL;
                        first = 0;
@@ -10393,7 +10719,7 @@ DEFUN (show_ip_bgp_route,
 
 DEFUN (show_ip_bgp_regexp,
        show_ip_bgp_regexp_cmd,
-       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX...",
+       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]",
        SHOW_STR
        IP_STR
        BGP_STR
@@ -10401,11 +10727,14 @@ DEFUN (show_ip_bgp_regexp,
        BGP_AFI_HELP_STR
        BGP_SAFI_WITH_LABEL_HELP_STR
        "Display routes matching the AS path regular expression\n"
-       "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
+       "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n"
+       JSON_STR)
 {
        afi_t afi = AFI_IP6;
        safi_t safi = SAFI_UNICAST;
        struct bgp *bgp = NULL;
+       bool uj = use_json(argc, argv);
+       char *regstr = NULL;
 
        int idx = 0;
        bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
@@ -10414,14 +10743,12 @@ DEFUN (show_ip_bgp_regexp,
                return CMD_WARNING;
 
        // get index of regex
-       argv_find(argv, argc, "regexp", &idx);
-       idx++;
+       if (argv_find(argv, argc, "REGEX", &idx))
+               regstr = argv[idx]->arg;
 
-       char *regstr = argv_concat(argv, argc, idx);
-       int rc = bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi,
-                                bgp_show_type_regexp);
-       XFREE(MTYPE_TMP, regstr);
-       return rc;
+       assert(regstr);
+       return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi,
+                                bgp_show_type_regexp, uj);
 }
 
 DEFUN (show_ip_bgp_instance_all,
@@ -10454,13 +10781,14 @@ DEFUN (show_ip_bgp_instance_all,
 }
 
 static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
-                          afi_t afi, safi_t safi, enum bgp_show_type type)
+                          afi_t afi, safi_t safi, enum bgp_show_type type,
+                          bool use_json)
 {
        regex_t *regex;
        int rc;
 
        if (!config_bgp_aspath_validate(regstr)) {
-               vty_out(vty, "Invalid character in as-path access-list %s\n",
+               vty_out(vty, "Invalid character in REGEX %s\n",
                        regstr);
                return CMD_WARNING_CONFIG_FAILED;
        }
@@ -10471,7 +10799,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
                return CMD_WARNING;
        }
 
-       rc = bgp_show(vty, bgp, afi, safi, type, regex, 0);
+       rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json);
        bgp_regex_free(regex);
        return rc;
 }
@@ -10876,56 +11204,76 @@ struct peer_pcounts {
        unsigned int count[PCOUNT_MAX];
        const struct peer *peer;
        const struct bgp_table *table;
+       safi_t safi;
 };
 
-static int bgp_peer_count_walker(struct thread *t)
+static void bgp_peer_count_proc(struct bgp_node *rn,
+                               struct peer_pcounts *pc)
 {
-       struct bgp_node *rn;
-       struct peer_pcounts *pc = THREAD_ARG(t);
+       const struct bgp_adj_in *ain;
+       const struct bgp_path_info *pi;
        const struct peer *peer = pc->peer;
 
-       for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) {
-               struct bgp_adj_in *ain;
-               struct bgp_path_info *pi;
-
-               for (ain = rn->adj_in; ain; ain = ain->next)
-                       if (ain->peer == peer)
-                               pc->count[PCOUNT_ADJ_IN]++;
+       for (ain = rn->adj_in; ain; ain = ain->next)
+               if (ain->peer == peer)
+                       pc->count[PCOUNT_ADJ_IN]++;
 
-               for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
+       for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
 
-                       if (pi->peer != peer)
-                               continue;
+               if (pi->peer != peer)
+                       continue;
 
-                       pc->count[PCOUNT_ALL]++;
+               pc->count[PCOUNT_ALL]++;
 
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED))
-                               pc->count[PCOUNT_DAMPED]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
-                               pc->count[PCOUNT_HISTORY]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
-                               pc->count[PCOUNT_REMOVED]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
-                               pc->count[PCOUNT_STALE]++;
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_VALID))
-                               pc->count[PCOUNT_VALID]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED))
+                       pc->count[PCOUNT_DAMPED]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+                       pc->count[PCOUNT_HISTORY]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+                       pc->count[PCOUNT_REMOVED]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
+                       pc->count[PCOUNT_STALE]++;
+               if (CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+                       pc->count[PCOUNT_VALID]++;
+               if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+                       pc->count[PCOUNT_PFCNT]++;
+
+               if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
+                       pc->count[PCOUNT_COUNTED]++;
+                       if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+                               flog_err(
+                                       EC_LIB_DEVELOPMENT,
+                                       "Attempting to count but flags say it is unusable");
+               } else {
                        if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                               pc->count[PCOUNT_PFCNT]++;
-
-                       if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
-                               pc->count[PCOUNT_COUNTED]++;
-                               if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                                       flog_err(
-                                               EC_LIB_DEVELOPMENT,
-                                               "Attempting to count but flags say it is unusable");
-                       } else {
-                               if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
-                                       flog_err(
-                                               EC_LIB_DEVELOPMENT,
-                                               "Not counted but flags say we should");
-                       }
+                               flog_err(
+                                       EC_LIB_DEVELOPMENT,
+                                       "Not counted but flags say we should");
                }
        }
+}
+
+static int bgp_peer_count_walker(struct thread *t)
+{
+       struct bgp_node *rn, *rm;
+       const struct bgp_table *table;
+       struct peer_pcounts *pc = THREAD_ARG(t);
+
+       if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP
+           || pc->safi == SAFI_EVPN) {
+               /* Special handling for 2-level routing tables. */
+               for (rn = bgp_table_top(pc->table); rn;
+                    rn = bgp_route_next(rn)) {
+                       table = bgp_node_get_bgp_table_info(rn);
+                       if (table != NULL)
+                               for (rm = bgp_table_top(table); rm;
+                                    rm = bgp_route_next(rm))
+                                       bgp_peer_count_proc(rm, pc);
+               }
+       } else
+               for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn))
+                       bgp_peer_count_proc(rn, pc);
+
        return 0;
 }
 
@@ -10959,6 +11307,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,
        memset(&pcounts, 0, sizeof(pcounts));
        pcounts.peer = peer;
        pcounts.table = peer->bgp->rib[afi][safi];
+       pcounts.safi = safi;
 
        /* in-place call via thread subsystem so as to record execution time
         * stats for the thread-walk (i.e. ensure this can't be blamed on
@@ -11316,7 +11665,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
                                        header2 = 0;
                                }
 
-                               bgp_attr_dup(&attr, ain->attr);
+                               attr = *ain->attr;
                                route_filtered = false;
 
                                /* Filter prefix using distribute list,
@@ -11418,7 +11767,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
                                                header2 = 0;
                                        }
 
-                                       bgp_attr_dup(&attr, adj->attr);
+                                       attr = *adj->attr;
                                        ret = bgp_output_modifier(
                                                peer, &rn->p, &attr, afi, safi,
                                                rmap_name);