]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: conditional advertisement
authorMadhuri Kuruganti <k.madhuri@samsung.com>
Tue, 29 Sep 2020 09:46:04 +0000 (15:16 +0530)
committerMadhuri Kuruganti <k.madhuri@samsung.com>
Tue, 27 Oct 2020 10:45:36 +0000 (16:15 +0530)
Implemented as per the feature description given in the source link.

Descriprion:
The BGP conditional advertisement feature uses the non-exist-map or exist-map
and the advertise-map keywords of the neighbor advertise-map command in order
to track routes by the route prefix.

non-exist-map :
If a route prefix is not present in output of the non-exist-map command, then
the route specified by the advertise-map command is announced.

exist-map :
If a route prefix is present in output of the exist-map command, then the route
specified by the advertise-map command is announced.

The conditional BGP announcements are sent in addition to the normal
announcements that a BGP router sends to its peers.

The conditional advertisement process is triggered by the BGP scanner process,
which runs every 60 seconds. This means that the maximum time for the conditional
advertisement to take effect is 60 seconds. The conditional advertisement can take
effect sooner, depending on when the tracked route is removed from the BGP table
and when the next instance of the BGP scanner occurs.

Sample Configuration on DUT
---------------------------
Router2# show running-config
Building configuration...

Current configuration:
!
frr version 7.6-dev-MyOwnFRRVersion
frr defaults traditional
hostname router
log file /var/log/frr/bgpd.log
log syslog informational
hostname Router2
service integrated-vtysh-config
!
debug bgp updates in
debug bgp updates out
!
debug route-map
!
ip route 200.200.0.0/16 blackhole
ipv6 route 2001:db8::200/128 blackhole
!
interface enp0s9
 ip address 10.10.10.2/24
!
interface enp0s10
 ip address 10.10.20.2/24
!
interface lo
 ip address 2.2.2.2/24
 ipv6 address 2001:db8::2/128
!
router bgp 2
 bgp log-neighbor-changes
 no bgp ebgp-requires-policy
 neighbor 10.10.10.1 remote-as 1
 neighbor 10.10.20.3 remote-as 3
 !
 address-family ipv4 unicast
  network 2.2.2.0/24
  network 200.200.0.0/16
  neighbor 10.10.10.1 soft-reconfiguration inbound
  neighbor 10.10.10.1 advertise-map ADVERTISE non-exist-map CONDITION
  neighbor 10.10.20.3 soft-reconfiguration inbound
 exit-address-family
 !
 address-family ipv6 unicast
  network 2001:db8::2/128
  network 2001:db8::200/128
  neighbor 10.10.10.1 activate
  neighbor 10.10.10.1 soft-reconfiguration inbound
  neighbor 10.10.10.1 advertise-map ADVERTISE_6 non-exist-map CONDITION_6
  neighbor 10.10.20.3 activate
  neighbor 10.10.20.3 soft-reconfiguration inbound
 exit-address-family
!
access-list CONDITION seq 5 permit 3.3.3.0/24
access-list ADVERTISE seq 5 permit 2.2.2.0/24
access-list ADVERTISE seq 6 permit 200.200.0.0/16
access-list ADVERTISE seq 7 permit 20.20.0.0/16
!
ipv6 access-list ADVERTISE_6 seq 5 permit 2001:db8::2/128
ipv6 access-list CONDITION_6 seq 5 permit 2001:db8::3/128
!
route-map ADVERTISE permit 10
 match ip address ADVERTISE
!
route-map CONDITION permit 10
 match ip address CONDITION
!
route-map ADVERTISE_6 permit 10
 match ipv6 address ADVERTISE_6
!
route-map CONDITION_6 permit 10
 match ipv6 address CONDITION_6
!
line vty
!
end
Router2#

Withdraw when non-exist-map prefixes present in BGP table:
----------------------------------------------------------
Router2# show ip bgp all wide

For address family: IPv4 Unicast
BGP table version is 8, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 1.1.1.0/24                                   10.10.10.1                                     0             0 1 i
*> 2.2.2.0/24                                   0.0.0.0                                        0         32768 i
*> 3.3.3.0/24                                   10.10.20.3                                     0             0 3 i
*> 200.200.0.0/16                               0.0.0.0                                        0         32768 i

Displayed  4 routes and 4 total paths

For address family: IPv6 Unicast
BGP table version is 8, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 2001:db8::1/128                              fe80::a00:27ff:fecb:ad57                       0             0 1 i
*> 2001:db8::2/128                              ::                                             0         32768 i
*> 2001:db8::3/128                              fe80::a00:27ff:fe76:6738                       0             0 3 i
*> 2001:db8::200/128                            ::                                             0         32768 i

Displayed  4 routes and 4 total paths
Router2#

Router2# show ip bgp neighbors 10.10.10.1
BGP neighbor is 10.10.10.1, remote AS 1, local AS 2, external link

!--- Output suppressed.

 For address family: IPv4 Unicast
  Update group 9, subgroup 5
  Packet Queue length 0
  Inbound soft reconfiguration allowed
  Community attribute sent to this neighbor(all)
  Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Withdraw
  1 accepted prefixes

 For address family: IPv6 Unicast
  Update group 10, subgroup 6
  Packet Queue length 0
  Inbound soft reconfiguration allowed
  Community attribute sent to this neighbor(all)
  Condition NON_EXIST, Condition-map *CONDITION_6, Advertise-map *ADVERTISE_6, status: Withdraw
  1 accepted prefixes

!--- Output suppressed.

Router2#

Here 2.2.2.0/24 & 200.200.0.0/16 (prefixes in advertise-map) are withdrawn
by conditional advertisement scanner as the prefix(3.3.3.0/24) specified
by non-exist-map is present in BGP table.

Router2# show ip bgp all neighbors 10.10.10.1 advertised-routes wide

For address family: IPv4 Unicast
BGP table version is 8, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 1.1.1.0/24                                   0.0.0.0                                                      0 1 i
*> 3.3.3.0/24                                   0.0.0.0                                                      0 3 i

Total number of prefixes 2

For address family: IPv6 Unicast
BGP table version is 8, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 2001:db8::1/128                              ::                                                           0 1 i
*> 2001:db8::3/128                              ::                                                           0 3 i
*> 2001:db8::200/128                            ::                                             0         32768 i

Total number of prefixes 3
Router2#

Advertise when non-exist-map prefixes not present in BGP table:
---------------------------------------------------------------
After Removing 3.3.3.0/24 (prefix present in non-exist-map),
2.2.2.0/24 & 200.200.0.0/16 (prefixes present in advertise-map) are advertised

Router2# show ip bgp all wide

For address family: IPv4 Unicast
BGP table version is 9, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 1.1.1.0/24                                   10.10.10.1                                     0             0 1 i
*> 2.2.2.0/24                                   0.0.0.0                                        0         32768 i
*> 200.200.0.0/16                               0.0.0.0                                        0         32768 i

Displayed  3 routes and 3 total paths

For address family: IPv6 Unicast
BGP table version is 9, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 2001:db8::1/128                              fe80::a00:27ff:fecb:ad57                       0             0 1 i
*> 2001:db8::2/128                              ::                                             0         32768 i
*> 2001:db8::200/128                            ::                                             0         32768 i

Displayed  3 routes and 3 total paths
Router2#

Router2# show ip bgp neighbors 10.10.10.1

!--- Output suppressed.

 For address family: IPv4 Unicast
  Update group 9, subgroup 5
  Packet Queue length 0
  Inbound soft reconfiguration allowed
  Community attribute sent to this neighbor(all)
  Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Advertise
  1 accepted prefixes

 For address family: IPv6 Unicast
  Update group 10, subgroup 6
  Packet Queue length 0
  Inbound soft reconfiguration allowed
  Community attribute sent to this neighbor(all)
  Condition NON_EXIST, Condition-map *CONDITION_6, Advertise-map *ADVERTISE_6, status: Advertise
  1 accepted prefixes

!--- Output suppressed.

Router2#
Router2# show ip bgp all neighbors 10.10.10.1 advertised-routes wide

For address family: IPv4 Unicast
BGP table version is 9, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 1.1.1.0/24                                   0.0.0.0                                                      0 1 i
*> 2.2.2.0/24                                   0.0.0.0                                        0         32768 i
*> 200.200.0.0/16                               0.0.0.0                                        0         32768 i

Total number of prefixes 3

For address family: IPv6 Unicast
BGP table version is 9, local router ID is 2.2.2.2, vrf id 0
Default local pref 100, local AS 2
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete

   Network                                      Next Hop                                  Metric LocPrf Weight Path
*> 2001:db8::1/128                              ::                                                           0 1 i
*> 2001:db8::2/128                              ::                                             0         32768 i
*> 2001:db8::200/128                            ::                                             0         32768 i

Total number of prefixes 3
Router2#

Signed-off-by: Madhuri Kuruganti <k.madhuri@samsung.com>
12 files changed:
bgpd/bgp_conditional_adv.c [new file with mode: 0644]
bgpd/bgp_conditional_adv.h [new file with mode: 0644]
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_updgrp.c
bgpd/bgp_updgrp_adv.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
bgpd/subdir.am
doc/user/bgp.rst

diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c
new file mode 100644 (file)
index 0000000..f38ef90
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * BGP Conditional advertisement
+ * Copyright (C) 2020  Samsung Research Institute Bangalore.
+ *                     Madhurilatha Kuruganti
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "bgpd/bgp_conditional_adv.h"
+
+const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
+
+/* We just need bgp_dest node matches with filter prefix. So no need to
+ * traverse each path here.
+ */
+struct bgp_dest *bgp_dest_matches_filter_prefix(struct bgp_table *table,
+                                               struct filter *filter)
+{
+       uint32_t check_addr;
+       uint32_t check_mask;
+       struct in_addr mask;
+       struct bgp_dest *dest = NULL;
+       struct bgp_path_info *pi = NULL;
+       const struct prefix *dest_p = NULL;
+       struct filter_cisco *cfilter = NULL;
+       struct filter_zebra *zfilter = NULL;
+
+       if (filter->cisco) {
+               cfilter = &filter->u.cfilter;
+               for (dest = bgp_table_top(table); dest;
+                    dest = bgp_route_next(dest)) {
+                       dest_p = (struct prefix *)bgp_dest_get_prefix(dest);
+                       if (!dest_p)
+                               continue;
+                       pi = bgp_dest_get_bgp_path_info(dest);
+                       if (!pi)
+                               continue;
+                       check_addr = dest_p->u.prefix4.s_addr
+                                    & ~cfilter->addr_mask.s_addr;
+                       if (memcmp(&check_addr, &cfilter->addr.s_addr,
+                                  sizeof(check_addr))
+                           != 0)
+                               continue;
+                       if (cfilter->extended) {
+                               masklen2ip(dest_p->prefixlen, &mask);
+                               check_mask = mask.s_addr
+                                            & ~cfilter->mask_mask.s_addr;
+                               if (memcmp(&check_mask, &cfilter->mask.s_addr,
+                                          sizeof(check_mask))
+                                   != 0)
+                                       continue;
+                       }
+                       return dest;
+               }
+       } else {
+               zfilter = &filter->u.zfilter;
+               for (dest = bgp_table_top(table); dest;
+                    dest = bgp_route_next(dest)) {
+                       dest_p = bgp_dest_get_prefix(dest);
+                       if (!dest_p)
+                               continue;
+                       pi = bgp_dest_get_bgp_path_info(dest);
+                       if (!pi)
+                               continue;
+                       if ((zfilter->prefix.family != dest_p->family)
+                           || (zfilter->exact
+                               && (zfilter->prefix.prefixlen
+                                   != dest_p->prefixlen)))
+                               continue;
+                       else if (!prefix_match(&zfilter->prefix, dest_p))
+                               continue;
+                       else
+                               return dest;
+               }
+       }
+       return NULL;
+}
+
+enum route_map_cmd_result_t
+bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
+                                    struct route_map *rmap)
+{
+       afi_t afi;
+       struct access_list *alist = NULL;
+       struct filter *alist_filter = NULL;
+       struct bgp_dest *dest = NULL;
+       struct route_map_rule *match = NULL;
+       enum route_map_cmd_result_t ret = RMAP_NOOP;
+
+       if (!is_rmap_valid(rmap))
+               return ret;
+
+       /* If several match commands are configured, all must succeed for a
+        * given route in order for that route to match the clause (logical AND)
+        */
+       for (match = rmap->head->match_list.head; match; match = match->next) {
+
+               if (!match->cmd || !match->cmd->str || !match->value)
+                       continue;
+
+               ret = RMAP_NOMATCH;
+
+               afi = get_afi_from_match_rule(match->cmd->str);
+               if (afi == AFI_MAX)
+                       return ret;
+
+               alist = access_list_lookup(afi, (char *)match->value);
+               if (!alist)
+                       return ret;
+
+               /* If a match command refers to several objects in one
+                * command either of them should match (i.e logical OR)
+                */
+               FOREACH_ACCESS_LIST_FILTER(alist, alist_filter) {
+                       dest = bgp_dest_matches_filter_prefix(table,
+                                                             alist_filter);
+                       if (!dest)
+                               continue;
+
+                       ret = RMAP_MATCH;
+                       break;
+               }
+               /* None of the access-list's filter prefix of this Match rule is
+                * not matched with BGP table.
+                * So we do not need to process the remaining match rules
+                */
+               if (ret != RMAP_MATCH)
+                       break;
+       }
+
+       /* route-map prefix not matched with prefixes in BGP table */
+       return ret;
+}
+
+bool bgp_conditional_adv_routes(struct peer *peer, afi_t afi, safi_t safi,
+                               struct bgp_table *table, struct route_map *rmap,
+                               bool advertise)
+{
+       int addpath_capable;
+       afi_t match_afi;
+       bool ret = false;
+       bool route_advertised = false;
+       struct peer_af *paf = NULL;
+       struct bgp_dest *dest = NULL;
+       struct access_list *alist = NULL;
+       struct filter *alist_filter = NULL;
+       struct route_map_rule *match = NULL;
+       struct update_subgroup *subgrp = NULL;
+
+       paf = peer_af_find(peer, afi, safi);
+       if (!paf)
+               return ret;
+
+       subgrp = PAF_SUBGRP(paf);
+       /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
+       if (!subgrp)
+               return ret;
+
+       if (!is_rmap_valid(rmap))
+               return ret;
+
+       addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+       /* If several match commands are configured, all must succeed for a
+        * given route in order for that route to match the clause (i.e. logical
+        * AND). But we are skipping this rule and advertising if match rule is
+        * valid and access-lists are having valid prefix - To be discussed
+        */
+       for (match = rmap->head->match_list.head; match; match = match->next) {
+
+               if (!match->cmd || !match->cmd->str || !match->value)
+                       continue;
+
+               match_afi = get_afi_from_match_rule(match->cmd->str);
+               if (match_afi == AFI_MAX)
+                       continue;
+
+               alist = access_list_lookup(match_afi, (char *)match->value);
+               if (!alist)
+                       continue;
+
+               if (safi == SAFI_LABELED_UNICAST)
+                       safi = SAFI_UNICAST;
+
+               /* If a match command refers to several objects in one
+                * command either of them should match (i.e logical OR)
+                */
+               FOREACH_ACCESS_LIST_FILTER(alist, alist_filter) {
+                       dest = bgp_dest_matches_filter_prefix(table,
+                                                             alist_filter);
+                       if (!dest)
+                               continue;
+
+                       ret = advertise_dest_routes(subgrp, dest, peer, afi,
+                                                   safi, addpath_capable,
+                                                   advertise);
+
+                       /* Atleast one route advertised */
+                       if (!route_advertised && ret)
+                               route_advertised = true;
+               }
+       }
+       return route_advertised;
+}
+
+/* Handler of conditional advertisement timer event.
+ * Each route in the condition-map is evaluated.
+ */
+static int bgp_conditional_adv_timer(struct thread *t)
+{
+       afi_t afi;
+       safi_t safi;
+       int pfx_rcd_safi;
+       struct bgp *bgp = NULL;
+       struct peer *peer = NULL;
+       struct peer_af *paf = NULL;
+       struct bgp_table *table = NULL;
+       struct bgp_filter *filter = NULL;
+       struct listnode *node, *nnode = NULL;
+       struct update_subgroup *subgrp = NULL;
+       enum route_map_cmd_result_t ret, prev_ret;
+       bool route_advertised = false;
+       bool adv_withdrawn = false;
+       int adv_conditional = 0;
+
+       bgp = THREAD_ARG(t);
+       assert(bgp);
+
+       bgp->t_condition_check = NULL;
+       thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
+                        CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
+
+       /* loop through each peer and advertise or withdraw routes if
+        * advertise-map is configured and prefix(es) in condition-map
+        * does exist(exist-map)/not exist(non-exist-map) in BGP table based on
+        * condition(exist-map or non-exist map)
+        */
+       FOREACH_AFI_SAFI (afi, safi) {
+               if (strmatch(get_afi_safi_str(afi, safi, true), "Unknown"))
+                       continue;
+
+               /* labeled-unicast routes are installed in the unicast table
+                * so in order to display the correct PfxRcd value we must
+                * look at SAFI_UNICAST
+                */
+               pfx_rcd_safi =
+                       (safi == SAFI_LABELED_UNICAST) ? SAFI_UNICAST : safi;
+
+               table = bgp->rib[afi][pfx_rcd_safi];
+               if (!table)
+                       continue;
+
+               /* Process conditional advertisement for each peer */
+               for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+                       if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+                               continue;
+                       if (!peer->afc[afi][safi])
+                               continue;
+
+                       filter = &peer->filter[afi][safi];
+
+                       if ((!filter->advmap.aname) || (!filter->advmap.cname)
+                           || (!filter->advmap.amap) || (!filter->advmap.cmap))
+                               continue;
+
+                       /* cmap (route-map attached to exist-map or
+                        * non-exist-map) map validation
+                        */
+                       adv_withdrawn = false;
+                       adv_conditional = 0;
+
+                       ret = bgp_check_rmap_prefixes_in_bgp_table(table,
+                                                       filter->advmap.cmap);
+                       prev_ret =
+                               peer->advmap_info[afi][safi].cmap_prev_status;
+
+                       switch (ret) {
+                       case RMAP_NOOP:
+                               if (prev_ret == RMAP_NOOP)
+                                       continue;
+
+                               peer->advmap_info[afi][safi].cmap_prev_status =
+                                       ret;
+                               if (filter->advmap.status)
+                                       continue;
+
+                               /* advertise previously withdrawn routes */
+                               adv_withdrawn = true;
+                               break;
+
+                       case RMAP_MATCH:
+                               /* Handle configuration changes */
+                               if (peer->advmap_info[afi][safi]
+                                           .config_change) {
+                                       /* If configuration(ACL filetr prefixes)
+                                        * is changed and if the advertise-map
+                                        * filter previous status was withdraw
+                                        * then we need to advertise the
+                                        * previously withdrawn routes.
+                                        * Nothing to do if the filter status
+                                        * was advertise.
+                                        */
+                                       if ((prev_ret != RMAP_NOOP)
+                                           && !filter->advmap.status)
+                                               adv_withdrawn = true;
+
+                                       adv_conditional =
+                                               (filter->advmap.condition
+                                                == CONDITION_EXIST)
+                                                       ? NLRI
+                                                       : WITHDRAW;
+                                       peer->advmap_info[afi][safi]
+                                               .config_change = false;
+                               } else {
+                                       if (prev_ret != RMAP_MATCH)
+                                               adv_conditional =
+                                                       (filter->advmap
+                                                                .condition
+                                                        == CONDITION_EXIST)
+                                                               ? NLRI
+                                                               : WITHDRAW;
+                               }
+                               peer->advmap_info[afi][safi].cmap_prev_status =
+                                       ret;
+                               break;
+
+                       case RMAP_NOMATCH:
+                               /* Handle configuration changes */
+                               if (peer->advmap_info[afi][safi]
+                                           .config_change) {
+                                       /* If configuration(ACL filetr prefixes)
+                                        * is changed and if the advertise-map
+                                        * filter previous status was withdraw
+                                        * then we need to advertise the
+                                        * previously withdrawn routes.
+                                        * Nothing to do if the filter status
+                                        * was advertise.
+                                        */
+                                       if ((prev_ret != RMAP_NOOP)
+                                           && !filter->advmap.status)
+                                               adv_withdrawn = true;
+
+                                       adv_conditional =
+                                               (filter->advmap.condition
+                                                == CONDITION_EXIST)
+                                                       ? WITHDRAW
+                                                       : NLRI;
+                                       peer->advmap_info[afi][safi]
+                                               .config_change = false;
+                               } else {
+                                       if (prev_ret != RMAP_NOMATCH)
+                                               adv_conditional =
+                                                       (filter->advmap
+                                                                .condition
+                                                        == CONDITION_EXIST)
+                                                               ? WITHDRAW
+                                                               : NLRI;
+                               }
+                               peer->advmap_info[afi][safi].cmap_prev_status =
+                                       ret;
+                               break;
+
+                       case RMAP_OKAY:
+                       case RMAP_ERROR:
+                       default:
+                               break;
+                       }
+
+                       /* amap (route-map attached to advertise-map)
+                        * validation.
+                        */
+                       ret = is_rmap_valid(filter->advmap.amap) ? RMAP_MATCH
+                                                                : RMAP_NOOP;
+                       prev_ret =
+                               peer->advmap_info[afi][safi].amap_prev_status;
+
+                       if (ret == RMAP_NOOP) {
+                               if (prev_ret == RMAP_NOOP) {
+                                       if (!adv_withdrawn)
+                                               continue;
+                                       /* Should not reach here. */
+                               }
+                               if (filter->advmap.status && !adv_withdrawn)
+                                       continue;
+                       }
+
+                       /* Derive conditional advertisement status from
+                        * condition and return value of condition-map
+                        * validation.
+                        */
+                       if (adv_conditional == NLRI)
+                               filter->advmap.status = true;
+                       else if (adv_conditional == WITHDRAW)
+                               filter->advmap.status = false;
+                       else {
+                               /* no change in advertise status. So, only
+                                * previously withdrawn routes will be
+                                * advertised if needed.
+                                */
+                       }
+
+                       if (adv_withdrawn) {
+                               paf = peer_af_find(peer, afi, safi);
+                               if (paf) {
+                                       update_subgroup_split_peer(paf, NULL);
+                                       subgrp = paf->subgroup;
+                                       if (subgrp && subgrp->update_group)
+                                               subgroup_announce_table(
+                                                       paf->subgroup, NULL);
+                               }
+                       }
+                       if (adv_conditional) {
+                               route_advertised = bgp_conditional_adv_routes(
+                                       peer, afi, safi, table,
+                                       filter->advmap.amap,
+                                       filter->advmap.status);
+
+                               /* amap_prev_status is only to check whether we
+                                * have announced any routes(advertise/withdraw)
+                                * or not. filter->advmap.status will have the
+                                * actual filter status
+                                */
+                               peer->advmap_info[afi][safi].amap_prev_status =
+                                       route_advertised ? RMAP_MATCH
+                                                        : RMAP_NOOP;
+                       }
+               }
+       }
+       return 0;
+}
+
+void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
+{
+       struct bgp *bgp = peer->bgp;
+
+       assert(bgp);
+
+       /* This flag is used to monitor conditional routes status in BGP table,
+        * and advertise/withdraw routes only when there is a change in BGP
+        * table w.r.t conditional routes
+        */
+       peer->advmap_info[afi][safi].amap_prev_status = RMAP_NOOP;
+       peer->advmap_info[afi][safi].cmap_prev_status = RMAP_NOOP;
+       peer->advmap_info[afi][safi].config_change = true;
+
+       /* advertise-map is already configured on atleast one of its
+        * neighbors (AFI/SAFI). So just increment the counter.
+        */
+       if (++bgp->condition_filter_count > 1)
+               return;
+
+       /* Register for conditional routes polling timer */
+       thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
+                        CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
+}
+
+void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
+{
+       struct bgp *bgp = peer->bgp;
+
+       assert(bgp);
+
+       /* advertise-map is not configured on any of its neighbors or
+        * it is configured on more than one neighbor(AFI/SAFI).
+        * So there's nothing to do except decrementing the counter.
+        */
+       if (--bgp->condition_filter_count != 0)
+               return;
+
+       /* Last filter removed. So cancel conditional routes polling thread. */
+       THREAD_OFF(bgp->t_condition_check);
+}
diff --git a/bgpd/bgp_conditional_adv.h b/bgpd/bgp_conditional_adv.h
new file mode 100644 (file)
index 0000000..cd85795
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * BGP Conditional advertisement
+ * Copyright (C) 2020  Samsung Research Institute Bangalore.
+ *                     Madhurilatha Kuruganti
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_CONDITION_ADV_H
+#define _FRR_BGP_CONDITION_ADV_H
+#include <zebra.h>
+#include "prefix.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_updgrp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Polling time for monitoring condition-map routes in route table */
+#define CONDITIONAL_ROUTES_POLL_TIME 60
+
+#define FOREACH_ACCESS_LIST_FILTER(alist, filter)                              \
+       for (filter = alist->head; filter; filter = filter->next)
+
+static inline bool is_rmap_valid(struct route_map *rmap)
+{
+       if (!rmap || !rmap->head)
+               return false;
+
+       /* Doesn't make sense to configure advertise
+        * or condition map in deny/any clause.
+        */
+       if (rmap->head->type != RMAP_PERMIT)
+               return false;
+
+       /* If a match command is not present, all routes match the clause */
+       if (!rmap->head->match_list.head)
+               return false;
+
+       return true;
+}
+
+static inline afi_t get_afi_from_match_rule(const char *str)
+{
+       if (!strcmp(str, "ip address"))
+               return AFI_IP;
+       else if (!strcmp(str, "ipv6 address"))
+               return AFI_IP6;
+       else
+               return AFI_MAX;
+}
+
+static inline bool advertise_dest_routes(struct update_subgroup *subgrp,
+                                        struct bgp_dest *dest,
+                                        struct peer *peer, afi_t afi,
+                                        safi_t safi, int addpath_capable,
+                                        bool advertise)
+{
+       struct attr attr;
+       struct bgp_path_info *pi = NULL;
+       const struct prefix *dest_p = NULL;
+       bool route_advertised = false;
+
+       dest_p = (struct prefix *)bgp_dest_get_prefix(dest);
+       if (!dest_p)
+               return route_advertised;
+
+       for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+               if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
+                   || (addpath_capable
+                       && bgp_addpath_tx_path(peer->addpath_type[afi][safi],
+                                              pi))) {
+
+                       /* Skip route-map checks in subgroup_announce_check
+                        * while executing from the conditional advertise
+                        * scanner process. otherwise when route-map is also
+                        * configured on same peer, routes in advertise-map
+                        * may not be advertised as expected.
+                        */
+                       if (advertise
+                           && subgroup_announce_check(dest, pi, subgrp, dest_p,
+                                                      &attr, true)) {
+                               bgp_adj_out_set_subgroup(dest, subgrp, &attr,
+                                                        pi);
+                               route_advertised = true;
+                       } else {
+                               /* If default originate is enabled for the
+                                * peer, do not send explicit withdraw.
+                                * This will prevent deletion of default route
+                                * advertised through default originate.
+                                */
+                               if (CHECK_FLAG(peer->af_flags[afi][safi],
+                                              PEER_FLAG_DEFAULT_ORIGINATE)
+                                   && is_default_prefix(dest_p))
+                                       break;
+
+                               bgp_adj_out_unset_subgroup(
+                                       dest, subgrp, 1,
+                                       bgp_addpath_id_for_peer(
+                                               peer, afi, safi,
+                                               &pi->tx_addpath));
+                               route_advertised = true;
+                       }
+               }
+       }
+       return route_advertised;
+}
+
+struct bgp_dest *bgp_dest_matches_filter_prefix(struct bgp_table *table,
+                                               struct filter *filter);
+extern enum route_map_cmd_result_t
+bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
+                                    struct route_map *rmap);
+extern void bgp_conditional_adv_enable(struct peer *peer, afi_t afi,
+                                      safi_t safi);
+extern void bgp_conditional_adv_disable(struct peer *peer, afi_t afi,
+                                       safi_t safi);
+extern bool bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
+                                      safi_t safi, struct bgp_table *table,
+                                      struct route_map *rmap, bool advertise);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_BGP_CONDITION_ADV_H */
index b637191d103508205a129640bcdbf39c1d4c1b68..e64cf4a5417c7cbe1e787b97f9dcadf1d2693520 100644 (file)
@@ -1671,7 +1671,8 @@ static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr)
 
 bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
                             struct update_subgroup *subgrp,
-                            const struct prefix *p, struct attr *attr)
+                            const struct prefix *p, struct attr *attr,
+                            bool skip_rmap_check)
 {
        struct bgp_filter *filter;
        struct peer *from;
@@ -1986,7 +1987,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
        bgp_peer_as_override(bgp, afi, safi, peer, attr);
 
        /* Route map & unsuppress-map apply. */
-       if (ROUTE_MAP_OUT_NAME(filter) || bgp_path_suppressed(pi)) {
+       if (!skip_rmap_check
+           && (ROUTE_MAP_OUT_NAME(filter)
+               || (pi->extra && pi->extra->suppress))) {
                struct bgp_path_info rmap_path = {0};
                struct bgp_path_info_extra dummy_rmap_path_extra = {0};
                struct attr dummy_attr = {0};
@@ -2484,7 +2487,8 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp,
        /* Announcement to the subgroup.  If the route is filtered withdraw it.
         */
        if (selected) {
-               if (subgroup_announce_check(dest, selected, subgrp, p, &attr))
+               if (subgroup_announce_check(dest, selected, subgrp, p, &attr,
+                                           false))
                        bgp_adj_out_set_subgroup(dest, subgrp, &attr, selected);
                else
                        bgp_adj_out_unset_subgroup(dest, subgrp, 1,
index 4a4959298a15ef776f6987cceb130deb0d25ac72..f770f9cb8dd92a2e1bb1c4aa3185a803199c5027 100644 (file)
@@ -454,6 +454,14 @@ struct bgp_aggregate {
 #define UNSUPPRESS_MAP_NAME(F)  ((F)->usmap.name)
 #define UNSUPPRESS_MAP(F)       ((F)->usmap.map)
 
+#define ADVERTISE_MAP_NAME(F)  ((F)->advmap.aname)
+#define ADVERTISE_MAP(F)       ((F)->advmap.amap)
+
+#define ADVERTISE_CONDITION(F) ((F)->advmap.condition)
+
+#define CONDITION_MAP_NAME(F)  ((F)->advmap.cname)
+#define CONDITION_MAP(F)       ((F)->advmap.cmap)
+
 /* path PREFIX (addpath rxid NUMBER) */
 #define PATH_ADDPATH_STR_BUFFER PREFIX2STR_BUFFER + 32
 
@@ -671,7 +679,8 @@ extern void subgroup_process_announce_selected(struct update_subgroup *subgrp,
 extern bool subgroup_announce_check(struct bgp_dest *dest,
                                    struct bgp_path_info *pi,
                                    struct update_subgroup *subgrp,
-                                   const struct prefix *p, struct attr *attr);
+                                   const struct prefix *p, struct attr *attr,
+                                   bool skip_rmap_check);
 
 extern void bgp_peer_clear_node_queue_drain_immediate(struct peer *peer);
 extern void bgp_process_queues_drain_immediate(void);
index c65574393657c598ce244b299375bad8297c3301..c8504cfc8f532a9869d461d20060f5e971f49d68 100644 (file)
@@ -3695,6 +3695,18 @@ static void bgp_route_map_process_peer(const char *rmap_name,
        if (filter->usmap.name && (strcmp(rmap_name, filter->usmap.name) == 0))
                filter->usmap.map = map;
 
+       if (filter->advmap.aname
+           && (strcmp(rmap_name, filter->advmap.aname) == 0)) {
+               filter->advmap.amap = map;
+               peer->advmap_info[afi][safi].config_change = true;
+       }
+
+       if (filter->advmap.cname
+           && (strcmp(rmap_name, filter->advmap.cname) == 0)) {
+               filter->advmap.cmap = map;
+               peer->advmap_info[afi][safi].config_change = true;
+       }
+
        if (peer->default_rmap[afi][safi].name
            && (strcmp(rmap_name, peer->default_rmap[afi][safi].name) == 0))
                peer->default_rmap[afi][safi].map = map;
index 21eca78abc6623edeb9c117bc0d4fcbb9aabc0d9..2788a8ea4ff48583938f819487ef1c12d5e4ba93 100644 (file)
@@ -195,6 +195,19 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
                        MTYPE_BGP_FILTER_NAME, UNSUPPRESS_MAP_NAME(srcfilter));
                UNSUPPRESS_MAP(dstfilter) = UNSUPPRESS_MAP(srcfilter);
        }
+
+       if (ADVERTISE_MAP_NAME(srcfilter)) {
+               ADVERTISE_MAP_NAME(dstfilter) = XSTRDUP(
+                       MTYPE_BGP_FILTER_NAME, ADVERTISE_MAP_NAME(srcfilter));
+               ADVERTISE_MAP(dstfilter) = ADVERTISE_MAP(srcfilter);
+               ADVERTISE_CONDITION(dstfilter) = ADVERTISE_CONDITION(srcfilter);
+       }
+
+       if (CONDITION_MAP_NAME(srcfilter)) {
+               CONDITION_MAP_NAME(dstfilter) = XSTRDUP(
+                       MTYPE_BGP_FILTER_NAME, CONDITION_MAP_NAME(srcfilter));
+               CONDITION_MAP(dstfilter) = CONDITION_MAP(srcfilter);
+       }
 }
 
 /**
@@ -218,6 +231,10 @@ static void conf_release(struct peer *src, afi_t afi, safi_t safi)
 
        XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->usmap.name);
 
+       XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->advmap.aname);
+
+       XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->advmap.cname);
+
        XFREE(MTYPE_BGP_PEER_HOST, src->host);
 }
 
@@ -353,6 +370,11 @@ static unsigned int updgrp_hash_key_make(const void *p)
                                        strlen(filter->usmap.name), SEED1),
                                  key);
 
+       if (filter->advmap.aname)
+               key = jhash_1word(jhash(filter->advmap.aname,
+                                       strlen(filter->advmap.aname), SEED1),
+                                 key);
+
        if (peer->default_rmap[afi][safi].name)
                key = jhash_1word(
                        jhash(peer->default_rmap[afi][safi].name,
@@ -481,6 +503,12 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2)
                && strcmp(fl1->usmap.name, fl2->usmap.name)))
                return false;
 
+       if ((fl1->advmap.aname && !fl2->advmap.aname)
+           || (!fl1->advmap.aname && fl2->advmap.aname)
+           || (fl1->advmap.aname && fl2->advmap.aname
+               && strcmp(fl1->advmap.aname, fl2->advmap.aname)))
+               return false;
+
        if ((pe1->default_rmap[afi][safi].name
             && !pe2->default_rmap[afi][safi].name)
            || (!pe1->default_rmap[afi][safi].name
index 2e59bb3a5f634ee34b3922eeea628080a8251daa..ae1359ae467adefc315ba4d9de51d3fa1c1390d5 100644 (file)
@@ -642,7 +642,8 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
                                           peer->addpath_type[afi][safi],
                                           ri))) {
                                if (subgroup_announce_check(dest, ri, subgrp,
-                                                           dest_p, &attr))
+                                                           dest_p, &attr,
+                                                           false))
                                        bgp_adj_out_set_subgroup(dest, subgrp,
                                                                 &attr, ri);
                                else {
@@ -827,7 +828,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
                                        if (subgroup_announce_check(
                                                    dest, pi, subgrp,
                                                    bgp_dest_get_prefix(dest),
-                                                   &attr))
+                                                   &attr, false))
                                                bgp_adj_out_set_subgroup(
                                                        dest, subgrp, &attr,
                                                        pi);
index 087836425b28f7f06026d05fe10bbf8d936b1e50..480e3172af554f9a90fc59cd81da1190f35556a3 100644 (file)
@@ -7043,6 +7043,132 @@ ALIAS_HIDDEN(no_neighbor_filter_list, no_neighbor_filter_list_hidden_cmd,
             "Filter incoming routes\n"
             "Filter outgoing routes\n")
 
+/* Set advertise-map to the peer. */
+static int peer_advertise_map_set_vty(struct vty *vty, const char *ip_str,
+                                     afi_t afi, safi_t safi,
+                                     const char *advertise_str, bool condition,
+                                     const char *condition_str)
+{
+       int ret = CMD_WARNING_CONFIG_FAILED;
+       struct peer *peer;
+       struct route_map *advertise_map;
+       struct route_map *condition_map;
+
+       peer = peer_and_group_lookup_vty(vty, ip_str);
+       if (!peer)
+               return ret;
+
+       condition_map = route_map_lookup_warn_noexist(vty, condition_str);
+       advertise_map = route_map_lookup_warn_noexist(vty, advertise_str);
+
+       ret = peer_advertise_map_set(peer, afi, safi, advertise_str,
+                                    advertise_map, condition, condition_str,
+                                    condition_map);
+
+       return bgp_vty_return(vty, ret);
+}
+
+static int peer_advertise_map_unset_vty(struct vty *vty, const char *ip_str,
+                                       afi_t afi, safi_t safi,
+                                       const char *advertise_str,
+                                       bool condition,
+                                       const char *condition_str)
+{
+       int ret = CMD_WARNING_CONFIG_FAILED;
+       struct peer *peer;
+       struct route_map *advertise_map;
+       struct route_map *condition_map;
+
+
+       peer = peer_and_group_lookup_vty(vty, ip_str);
+       if (!peer)
+               return ret;
+
+       condition_map = route_map_lookup_warn_noexist(vty, condition_str);
+       advertise_map = route_map_lookup_warn_noexist(vty, advertise_str);
+
+       ret = peer_advertise_map_unset(peer, afi, safi, advertise_str,
+                                      advertise_map, condition, condition_str,
+                                      condition_map);
+
+       return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_advertise_map,
+       neighbor_advertise_map_cmd,
+       "neighbor <A.B.C.D|X:X::X:X|WORD> advertise-map WORD <exist-map WORD|non-exist-map WORD>",
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Route-map to conditionally advertise routes\n"
+       "Name of advertise map\n"
+       "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+       "Name of the exist map\n"
+       "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+       "Name of the non exist map\n")
+{
+       int idx = 0;
+       int idx_peer = 1;
+       int idx_advertise_word = 3;
+       int idx_condition_word = 5;
+       bool condition = CONDITION_EXIST;
+
+       if (argv_find(argv, argc, "non-exist-map", &idx))
+               condition = CONDITION_NON_EXIST;
+
+       return peer_advertise_map_set_vty(
+               vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+               argv[idx_advertise_word]->arg, condition,
+               argv[idx_condition_word]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_advertise_map, neighbor_advertise_map_hidden_cmd,
+            "neighbor <A.B.C.D|X:X::X:X|WORD> advertise-map WORD <exist-map WORD|non-exist-map WORD>",
+            NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+            "Route-map to conditionally advertise routes\n"
+            "Name of advertise map\n"
+            "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+            "Name of the exist map\n"
+            "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+            "Name of the non exist map\n")
+
+DEFUN (no_neighbor_advertise_map,
+       no_neighbor_advertise_map_cmd,
+       "no neighbor <A.B.C.D|X:X::X:X|WORD> advertise-map WORD <exist-map WORD|non-exist-map WORD>",
+       NO_STR
+       NEIGHBOR_STR
+       NEIGHBOR_ADDR_STR2
+       "Route-map to conditionally advertise routes\n"
+       "Name of advertise map\n"
+       "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+       "Name of the exist map\n"
+       "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+       "Name of the non exist map\n")
+{
+       int idx = 0;
+       int idx_peer = 2;
+       int idx_advertise_word = 4;
+       int idx_condition_word = 6;
+       bool condition = CONDITION_EXIST;
+
+       if (argv_find(argv, argc, "non-exist-map", &idx))
+               condition = CONDITION_NON_EXIST;
+
+       return peer_advertise_map_unset_vty(
+               vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+               argv[idx_advertise_word]->arg, condition,
+               argv[idx_condition_word]->arg);
+}
+
+ALIAS_HIDDEN(no_neighbor_advertise_map, no_neighbor_advertise_map_hidden_cmd,
+            "no neighbor <A.B.C.D|X:X::X:X|WORD> advertise-map WORD <exist-map WORD|non-exist-map WORD>",
+            NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+            "Route-map to conditionally advertise routes\n"
+            "Name of advertise map\n"
+            "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+            "Name of the exist map\n"
+            "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+            "Name of the non exist map\n")
+
 /* Set route-map to the peer. */
 static int peer_route_map_set_vty(struct vty *vty, const char *ip_str,
                                  afi_t afi, safi_t safi, const char *name_str,
@@ -11281,6 +11407,19 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
                                filter->usmap.map ? "*" : "",
                                filter->usmap.name);
 
+               /* advertise-map */
+               if (filter->advmap.aname && filter->advmap.cname)
+                       vty_out(vty,
+                               "  Condition %s, Condition-map %s%s, Advertise-map %s%s, status: %s\n",
+                               filter->advmap.condition ? "EXIST"
+                                                        : "NON_EXIST",
+                               filter->advmap.cmap ? "*" : "",
+                               filter->advmap.cname,
+                               filter->advmap.amap ? "*" : "",
+                               filter->advmap.aname,
+                               filter->advmap.status ? "Advertise"
+                                                     : "Withdraw");
+
                /* Receive prefix count */
                vty_out(vty, "  %u accepted prefixes\n",
                        p->pcount[afi][safi]);
@@ -14996,6 +15135,10 @@ static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
                return !!(filter->map[direct].name);
        case PEER_FT_UNSUPPRESS_MAP:
                return !!(filter->usmap.name);
+       case PEER_FT_ADVERTISE_MAP:
+               return !!(filter->advmap.aname
+                         && ((filter->advmap.condition == direct)
+                             && filter->advmap.cname));
        default:
                return false;
        }
@@ -15181,6 +15324,18 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
                vty_out(vty, "  neighbor %s unsuppress-map %s\n", addr,
                        filter->usmap.name);
 
+       /* advertise-map : always applied in OUT direction*/
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ADVERTISE_MAP,
+                                  CONDITION_NON_EXIST))
+               vty_out(vty,
+                       "  neighbor %s advertise-map %s non-exist-map %s\n",
+                       addr, filter->advmap.aname, filter->advmap.cname);
+
+       if (peergroup_filter_check(peer, afi, safi, PEER_FT_ADVERTISE_MAP,
+                                  CONDITION_EXIST))
+               vty_out(vty, "  neighbor %s advertise-map %s exist-map %s\n",
+                       addr, filter->advmap.aname, filter->advmap.cname);
+
        /* filter-list. */
        if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
                                   FILTER_IN))
@@ -17385,6 +17540,26 @@ void bgp_vty_init(void)
        install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
        install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
 
+       /* "neighbor advertise-map" commands. */
+       install_element(BGP_NODE, &neighbor_advertise_map_hidden_cmd);
+       install_element(BGP_NODE, &no_neighbor_advertise_map_hidden_cmd);
+       install_element(BGP_IPV4_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV4_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_IPV4M_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV4M_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_IPV4L_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV4L_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6M_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6M_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6L_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_IPV6L_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_VPNV4_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_VPNV4_NODE, &no_neighbor_advertise_map_cmd);
+       install_element(BGP_VPNV6_NODE, &neighbor_advertise_map_cmd);
+       install_element(BGP_VPNV6_NODE, &no_neighbor_advertise_map_cmd);
+
        /* neighbor maximum-prefix-out commands. */
        install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd);
        install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd);
index be3322e468148cd5c259eaf1d89a3b00cb2c41d7..7fbad67e938e14cb2ad2663b6dccea27f7de045d 100644 (file)
@@ -54,6 +54,7 @@
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_errors.h"
 #include "bgpd/bgp_community.h"
+#include "bgpd/bgp_conditional_adv.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_clist.h"
@@ -6585,6 +6586,192 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi)
        return 0;
 }
 
+/* Set advertise-map to the peer but do not process peer route updates here.  *
+ * Hold filter changes until the conditional routes polling thread is called  *
+ * AS we need to advertise/withdraw prefixes (in advertise-map) based on the  *
+ * condition (exist-map/non-exist-map) and routes(specified in condition-map) *
+ * in BGP table. So do not call peer_on_policy_change() here, only create     *
+ * polling timer thread, update filters and increment condition_filter_count.
+ */
+int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
+                          const char *advertise_name,
+                          struct route_map *advertise_map, bool condition,
+                          const char *condition_name,
+                          struct route_map *condition_map)
+{
+       bool filter_exists = false;
+       struct peer *member;
+       struct bgp_filter *filter;
+       struct listnode *node, *nnode;
+
+       /* Set configuration on peer. */
+       filter = &peer->filter[afi][safi];
+
+       if (filter->advmap.aname) {
+               /* advertise-map filter is already configured on this peer */
+               filter_exists = true;
+
+               XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
+               XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
+               filter->advmap.condition = CONDITION_NON_EXIST;
+       }
+
+       route_map_counter_decrement(filter->advmap.amap);
+       filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, advertise_name);
+       filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, condition_name);
+       filter->advmap.amap = advertise_map;
+       filter->advmap.cmap = condition_map;
+       filter->advmap.condition = condition;
+       route_map_counter_increment(advertise_map);
+       peer->advmap_info[afi][safi].config_change = true;
+
+       /* Check if handling a regular peer. */
+       if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+               /* Set override-flag and process peer route updates. */
+               SET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
+                        PEER_FT_ADVERTISE_MAP);
+
+               /* Hold peer_on_policy_change() until timer thread is called */
+
+               /* To increment condition_filter_count and/or create timer */
+               if (!filter_exists) {
+                       filter->advmap.status = true;
+                       bgp_conditional_adv_enable(peer, afi, safi);
+               }
+               /* Skip peer-group mechanics for regular peers. */
+               return 0;
+       }
+
+       /*
+        * Set configuration on all peer-group members, unless they are
+        * explicitely overriding peer-group configuration.
+        */
+       for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+               /* Skip peers with overridden configuration. */
+               if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
+                              PEER_FT_ADVERTISE_MAP))
+                       continue;
+
+               /* Set configuration on peer-group member. */
+               filter = &member->filter[afi][safi];
+               if (filter->advmap.aname) {
+                       /* advertise-map filter is already configured. */
+                       filter_exists = true;
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
+                       filter->advmap.condition = CONDITION_NON_EXIST;
+               }
+               route_map_counter_decrement(filter->advmap.amap);
+               filter->advmap.aname =
+                       XSTRDUP(MTYPE_BGP_FILTER_NAME, advertise_name);
+               filter->advmap.amap = advertise_map;
+               filter->advmap.cname =
+                       XSTRDUP(MTYPE_BGP_FILTER_NAME, condition_name);
+               filter->advmap.cmap = condition_map;
+               filter->advmap.condition = condition;
+               route_map_counter_increment(advertise_map);
+
+               /* Hold peer_on_policy_change() until timer thread is called */
+
+               /* increment condition_filter_count, create timer if 1st one */
+               if (!filter_exists) {
+                       filter->advmap.status = true;
+                       bgp_conditional_adv_enable(member, afi, safi);
+               }
+       }
+
+       return 0;
+}
+
+/* Unset advertise-map from the peer. */
+int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
+                            const char *advertise_name,
+                            struct route_map *advertise_map, bool condition,
+                            const char *condition_name,
+                            struct route_map *condition_map)
+{
+       bool filter_exists = false;
+       struct peer *member;
+       struct bgp_filter *filter;
+       struct listnode *node, *nnode;
+
+       /* Unset override-flag unconditionally. */
+       UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
+                  PEER_FT_ADVERTISE_MAP);
+
+       /* Inherit configuration from peer-group if peer is member. */
+       if (peer_group_active(peer)) {
+               PEER_STR_ATTR_INHERIT(peer, peer->group,
+                                     filter[afi][safi].advmap.aname,
+                                     MTYPE_BGP_FILTER_NAME);
+               PEER_ATTR_INHERIT(peer, peer->group,
+                                 filter[afi][safi].advmap.amap);
+       } else {
+               /* Otherwise remove configuration from peer. */
+               filter = &peer->filter[afi][safi];
+               if (filter->advmap.aname) {
+                       /* advertise-map filter is already configured. */
+                       filter_exists = true;
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
+               }
+               route_map_counter_decrement(filter->advmap.amap);
+               filter->advmap.aname = NULL;
+               filter->advmap.amap = NULL;
+               filter->advmap.cname = NULL;
+               filter->advmap.cmap = NULL;
+               filter->advmap.condition = CONDITION_NON_EXIST;
+       }
+
+       /* Check if handling a regular peer. */
+       if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+               /* Process peer route updates. */
+               peer_on_policy_change(peer, afi, safi, 1);
+
+               /* decrement condition_filter_count delete timer if last one */
+               if (filter_exists)
+                       bgp_conditional_adv_disable(peer, afi, safi);
+
+               /* Skip peer-group mechanics for regular peers. */
+               return 0;
+       }
+
+       /*
+        * Remove configuration on all peer-group members, unless they are
+        * explicitely overriding peer-group configuration.
+        */
+       for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+               /* Skip peers with overridden configuration. */
+               if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
+                              PEER_FT_ADVERTISE_MAP))
+                       continue;
+
+               /* Remove configuration on peer-group member. */
+               filter = &member->filter[afi][safi];
+               if (filter->advmap.aname) {
+                       /* advertise-map filter is already configured. */
+                       filter_exists = true;
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
+                       XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
+               }
+               route_map_counter_decrement(filter->advmap.amap);
+               filter->advmap.aname = NULL;
+               filter->advmap.amap = NULL;
+               filter->advmap.cname = NULL;
+               filter->advmap.cmap = NULL;
+               filter->advmap.condition = CONDITION_NON_EXIST;
+
+               /* decrement condition_filter_count delete timer if last one */
+               if (filter_exists)
+                       bgp_conditional_adv_disable(peer, afi, safi);
+
+               /* Process peer route updates. */
+               peer_on_policy_change(member, afi, safi, 1);
+       }
+
+       return 0;
+}
+
 int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
                            uint32_t max, uint8_t threshold, int warning,
                            uint16_t restart, bool force)
index eae3d97730864fb0b87b149bceeaaad50dd8c48e..293583460f3fcadde69cdcac5d67b60af102f697 100644 (file)
@@ -681,6 +681,10 @@ struct bgp {
 
        /* Process Queue for handling routes */
        struct work_queue *process_queue;
+       
+       /* BGP Conditional advertisement */
+       int condition_filter_count;
+       struct thread *t_condition_check;
 
        QOBJ_FIELDS
 };
@@ -759,6 +763,10 @@ struct bgp_nexthop {
 #define BGP_GTSM_HOPS_DISABLED  0
 #define BGP_GTSM_HOPS_CONNECTED 1
 
+/* Advertise map */
+#define CONDITION_NON_EXIST    0
+#define CONDITION_EXIST 1
+
 #include "filter.h"
 
 /* BGP filter structure. */
@@ -792,6 +800,19 @@ struct bgp_filter {
                char *name;
                struct route_map *map;
        } usmap;
+
+       /* Advertise-map */
+       struct {
+               char *aname;
+               struct route_map *amap;
+
+               bool condition;
+
+               char *cname;
+               struct route_map *cmap;
+
+               bool status;
+       } advmap;
 };
 
 /* IBGP/EBGP identifier.  We also have a CONFED peer, which is to say,
@@ -1355,6 +1376,7 @@ struct peer {
 #define PEER_FT_PREFIX_LIST           (1U << 2) /* prefix-list */
 #define PEER_FT_ROUTE_MAP             (1U << 3) /* route-map */
 #define PEER_FT_UNSUPPRESS_MAP        (1U << 4) /* unsuppress-map */
+#define PEER_FT_ADVERTISE_MAP         (1U << 5) /* advertise-map */
 
        /* ORF Prefix-list */
        struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX];
@@ -1448,6 +1470,13 @@ struct peer {
        /* Sender side AS path loop detection. */
        bool as_path_loop_detection;
 
+       /* Conditional advertisement */
+       struct {
+               bool config_change;
+               enum route_map_cmd_result_t amap_prev_status;
+               enum route_map_cmd_result_t cmap_prev_status;
+       } advmap_info[AFI_MAX][SAFI_MAX];
+
        QOBJ_FIELDS
 };
 DECLARE_QOBJ_TYPE(peer)
@@ -1939,11 +1968,23 @@ extern int peer_unsuppress_map_set(struct peer *peer, afi_t afi, safi_t safi,
                                   const char *name,
                                   struct route_map *route_map);
 
+extern int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
+                                 const char *advertise_name,
+                                 struct route_map *advertise_map,
+                                 bool condition, const char *condition_name,
+                                 struct route_map *condition_map);
+
 extern int peer_password_set(struct peer *, const char *);
 extern int peer_password_unset(struct peer *);
 
 extern int peer_unsuppress_map_unset(struct peer *, afi_t, safi_t);
 
+extern int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
+                                   const char *advertise_name,
+                                   struct route_map *advertise_map,
+                                   bool condition, const char *condition_name,
+                                   struct route_map *condition_map);
+
 extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t,
                                   uint8_t, int, uint16_t, bool force);
 extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t);
index 3cb32b1f081ee22c0b0baa20775ba7a8c5e2cf36..ea60b921d1b5c5888b078b3e98dabdf560172fd8 100644 (file)
@@ -61,6 +61,7 @@ bgpd_libbgp_a_SOURCES = \
        bgpd/bgp_bfd.c \
        bgpd/bgp_clist.c \
        bgpd/bgp_community.c \
+       bgpd/bgp_conditional_adv.c \
        bgpd/bgp_damp.c \
        bgpd/bgp_debug.c \
        bgpd/bgp_dump.c \
@@ -137,6 +138,7 @@ noinst_HEADERS += \
        bgpd/bgp_bfd.h \
        bgpd/bgp_clist.h \
        bgpd/bgp_community.h \
+       bgpd/bgp_conditional_adv.h \
        bgpd/bgp_damp.h \
        bgpd/bgp_debug.h \
        bgpd/bgp_dump.h \
index 79e5afde7cdd370850dcdfb965ac74a47b9bf806..f79d33a26066164116fbf4efcd2c745cc7796aa8 100644 (file)
@@ -2595,6 +2595,182 @@ This makes it possible to separate not only layer 3 networks like VRF-lite netwo
 Also, VRF netns based make possible to separate layer 2 networks on separate VRF
 instances.
 
+.. _bgp-conditional-advertisement:
+
+BGP Conditional Advertisement
+-----------------------------
+The BGP conditional advertisement feature uses the ``non-exist-map`` or the
+``exist-map`` and the ``advertise-map`` keywords of the neighbor advertise-map
+command in order to track routes by the route prefix.
+
+``non-exist-map``
+   1. If a route prefix is not present in the output of non-exist-map command,
+      then advertise the route specified by the advertise-map command.
+
+   2. If a route prefix is present in the output of non-exist-map command,
+      then do not advertise the route specified by the addvertise-map command.
+
+``exist-map``
+   1. If a route prefix is present in the output of exist-map command,
+      then advertise the route specified by the advertise-map command.
+
+   2. If a route prefix is not present in the output of exist-map command,
+      then do not advertise the route specified by the advertise-map command.
+
+This feature is useful when some prefixes are advertised to one of its peers
+only if the information from the other peer is not present (due to failure in
+peering session or partial reachability etc).
+
+The conditional BGP announcements are sent in addition to the normal
+announcements that a BGP router sends to its peer.
+
+The conditional advertisement process is triggered by the BGP scanner process,
+which runs every 60 seconds. This means that the maximum time for the conditional
+advertisement to take effect is 60 seconds. The conditional advertisement can take
+effect depending on when the tracked route is removed from the BGP table and
+when the next instance of the BGP scanner occurs.
+
+.. index:: [no] neighbor A.B.C.D advertise-map NAME [exist-map|non-exist-map] NAME
+.. clicmd:: [no] neighbor A.B.C.D advertise-map NAME [exist-map|non-exist-map] NAME
+
+   This command enables BGP scanner process to monitor route specified by
+   exist-map or non-exist-map command in BGP table and conditionally advertises
+   the route specified by advertise-map command.
+
+Sample Configuration
+^^^^^^^^^^^^^^^^^^^^^
+.. code-block:: frr
+
+   interface lo
+   ip address 2.2.2.2/24
+   !
+   router bgp 2
+    bgp log-neighbor-changes
+    no bgp ebgp-requires-policy
+    neighbor 10.10.10.1 remote-as 1
+    neighbor 10.10.20.3 remote-as 3
+    !
+    address-family ipv4 unicast
+     network 2.2.2.0/24
+     network 20.20.0.0/16
+     neighbor 10.10.10.1 soft-reconfiguration inbound
+     neighbor 10.10.10.1 advertise-map ADVERTISE non-exist-map CONDITION
+     neighbor 10.10.20.3 soft-reconfiguration inbound
+    exit-address-family
+   !
+   access-list CONDITION seq 5 permit 3.3.3.0/24
+   access-list ADVERTISE seq 5 permit 2.2.2.0/24
+   !
+   route-map ADVERTISE permit 10
+    match ip address ADVERTISE
+   !
+   route-map CONDITION permit 10
+    match ip address CONDITION
+   !
+
+Sample Output
+^^^^^^^^^^^^^
+
+When 3.3.3.0/24 route is in R2'2 BGP rable, 2.2.2/0/24 is not adevrtised to R1.
+
+.. code-block:: frr
+
+   Router2# show ip bgp
+   BGP table version is 24, local router ID is 128.16.16.1, vrf id 0
+   Default local pref 100, local AS 2
+   Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+                  i internal, r RIB-failure, S Stale, R Removed
+   Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+   Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network             Next Hop            Metric LocPrf Weight Path
+   *> 1.1.1.0/24       10.10.10.1               0             0 1 i
+   *> 2.2.2.0/24       0.0.0.0                  0         32768 i
+   *> 3.3.3.0/24       10.10.20.3               0             0 3 i
+   *> 20.20.0.0/16     0.0.0.0                  0         32768 i
+
+   Displayed  4 routes and 4 total paths
+   Router2#
+
+   Router2# show ip bgp neighbors 10.10.10.1
+
+   !--- Output suppressed.
+
+   For address family: IPv4 Unicast
+   Update group 5, subgroup 1
+   Packet Queue length 0
+   Inbound soft reconfiguration allowed
+   Community attribute sent to this neighbor(all)
+   Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Withdraw
+   1 accepted prefixes
+
+   !--- Output suppressed.
+
+   Router2# show ip bgp neighbors 10.10.10.1 advertised-routes
+   BGP table version is 24, local router ID is 128.16.16.1, vrf id 0
+   Default local pref 100, local AS 2
+   Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+                  i internal, r RIB-failure, S Stale, R Removed
+   Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+   Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network          Next Hop            Metric LocPrf Weight Path
+   *> 1.1.1.0/24       0.0.0.0                                0 1 i
+   *> 3.3.3.0/24       0.0.0.0                                0 3 i
+   *> 20.20.0.0/16     0.0.0.0                  0         32768 i
+
+   Total number of prefixes 3
+   Router2#
+
+When 3.3.3.0/24 route is not in R2'2 BGP rable, 2.2.2/0/24 is adevrtised to R1.
+
+.. code-block:: frr
+
+   Router2# show ip bgp
+   BGP table version is 25, local router ID is 128.16.16.1, vrf id 0
+   Default local pref 100, local AS 2
+   Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+                  i internal, r RIB-failure, S Stale, R Removed
+   Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+   Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network             Next Hop            Metric LocPrf Weight Path
+   *> 1.1.1.0/24       10.10.10.1               0             0 1 i
+   *> 2.2.2.0/24       0.0.0.0                  0         32768 i
+   *> 20.20.0.0/16     0.0.0.0                  0         32768 i
+
+   Displayed  3 routes and 3 total paths
+   Router2#
+
+   Router2# show ip bgp neighbors 10.10.10.1
+
+   !--- Output suppressed.
+
+   For address family: IPv4 Unicast
+   Update group 5, subgroup 1
+   Packet Queue length 0
+   Inbound soft reconfiguration allowed
+   Community attribute sent to this neighbor(all)
+   Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Advertise
+   1 accepted prefixes
+
+   !--- Output suppressed.
+
+   Router2# show ip bgp neighbors 10.10.10.1 advertised-routes
+   BGP table version is 25, local router ID is 128.16.16.1, vrf id 0
+   Default local pref 100, local AS 2
+   Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
+                  i internal, r RIB-failure, S Stale, R Removed
+   Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+   Origin codes:  i - IGP, e - EGP, ? - incomplete
+
+   Network             Next Hop            Metric LocPrf Weight Path
+   *> 1.1.1.0/24       0.0.0.0                                0 1 i
+   *> 2.2.2.0/24       0.0.0.0                  0         32768 i
+   *> 20.20.0.0/16     0.0.0.0                  0         32768 i
+
+   Total number of prefixes 3
+
 .. _bgp-debugging:
 
 Debugging