]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #12636 from opensourcerouting/fix/bgp_accept-own_connected_routes
authorRuss White <russ@riw.us>
Tue, 17 Jan 2023 14:31:37 +0000 (09:31 -0500)
committerGitHub <noreply@github.com>
Tue, 17 Jan 2023 14:31:37 +0000 (09:31 -0500)
bgpd: Allow importing local routes with accept-own mechanism

61 files changed:
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_debug.c
bgpd/bgp_open.c
bgpd/bgp_route.c
bgpd/bgp_updgrp.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
doc/user/bfd.rst
doc/user/bgp.rst
lib/bfd.c
lib/bfd.h
lib/command.h
staticd/static_bfd.c [new file with mode: 0644]
staticd/static_debug.c
staticd/static_debug.h
staticd/static_nb.c
staticd/static_nb.h
staticd/static_nb_config.c
staticd/static_routes.c
staticd/static_routes.h
staticd/static_vty.c
staticd/static_zebra.c
staticd/subdir.am
tests/topotests/bfd_topo3/r3/bfd-static-down.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r3/bfd-static.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r3/staticd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r4/bfd-peers.json
tests/topotests/bfd_topo3/r4/bgpd.conf
tests/topotests/bfd_topo3/r4/staticd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r4/zebra.conf
tests/topotests/bfd_topo3/r5/bfd-peers.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r5/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r5/staticd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r5/zebra.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/bfd-peers.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/bfd-static-down.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/bfd-static.json [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/bfdd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/staticd.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/r6/zebra.conf [new file with mode: 0644]
tests/topotests/bfd_topo3/test_bfd_topo3.dot
tests/topotests/bfd_topo3/test_bfd_topo3.jpg
tests/topotests/bfd_topo3/test_bfd_topo3.py
tests/topotests/bgp_path_attribute_discard/__init__.py [new file with mode: 0644]
tests/topotests/bgp_path_attribute_discard/exabgp.env [new file with mode: 0644]
tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg [new file with mode: 0644]
tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_path_attribute_discard/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py [new file with mode: 0644]
tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
tests/topotests/route_scale/scale_test_common.py
tools/frr-reload.py
yang/frr-bfdd.yang
yang/frr-staticd.yang
zebra/zebra_ptm.h

index 908c024e9d31ee20ffe19f4136a8eadf81681451..4306d3bd121451f6a406e7d24354d63b86ea2dfc 100644 (file)
@@ -33,6 +33,7 @@
 #include "filter.h"
 #include "command.h"
 #include "srv6.h"
+#include "frrstr.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -1850,6 +1851,7 @@ stream_failure:
 /* Atomic aggregate. */
 static int bgp_attr_atomic(struct bgp_attr_parser_args *args)
 {
+       struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
        const bgp_size_t length = args->length;
 
@@ -1862,10 +1864,22 @@ static int bgp_attr_atomic(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto atomic_ignore;
+
        /* Set atomic aggregate flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+atomic_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Aggregator attribute */
@@ -1891,6 +1905,9 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto aggregator_ignore;
+
        if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV))
                aggregator_as = stream_getl(peer->curr);
        else
@@ -1917,6 +1934,15 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
        }
 
        return BGP_ATTR_PARSE_PROCEED;
+
+aggregator_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* New Aggregator attribute */
@@ -1937,6 +1963,9 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
                                          0);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto as4_aggregator_ignore;
+
        aggregator_as = stream_getl(peer->curr);
 
        *as4_aggregator_as = aggregator_as;
@@ -1960,6 +1989,15 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
        }
 
        return BGP_ATTR_PARSE_PROCEED;
+
+as4_aggregator_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
@@ -2079,6 +2117,9 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto community_ignore;
+
        bgp_attr_set_community(
                attr,
                community_parse((uint32_t *)stream_pnt(peer->curr), length));
@@ -2094,6 +2135,15 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
                                          args->total);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+community_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Originator ID attribute. */
@@ -2117,11 +2167,23 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto originator_id_ignore;
+
        attr->originator_id.s_addr = stream_get_ipv4(peer->curr);
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+originator_id_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Cluster list attribute. */
@@ -2144,6 +2206,9 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto cluster_list_ignore;
+
        bgp_attr_set_cluster(
                attr, cluster_parse((struct in_addr *)stream_pnt(peer->curr),
                                    length));
@@ -2154,6 +2219,15 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+cluster_list_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Multiprotocol reachability information parse. */
@@ -2413,6 +2487,9 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto large_community_ignore;
+
        bgp_attr_set_lcommunity(
                attr, lcommunity_parse(stream_pnt(peer->curr), length));
        /* XXX: fix ecommunity_parse to use stream API */
@@ -2423,6 +2500,15 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
                                          args->total);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+large_community_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Extended Community attribute. */
@@ -2514,6 +2600,9 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto ipv6_ext_community_ignore;
+
        ipv6_ecomm = ecommunity_parse_ipv6(
                stream_pnt(peer->curr), length,
                CHECK_FLAG(peer->flags,
@@ -2528,6 +2617,15 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
                                          args->total);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+ipv6_ext_community_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Parse Tunnel Encap attribute in an UPDATE */
@@ -3202,6 +3300,9 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args)
                goto aigp_ignore;
        }
 
+       if (peer->discard_attrs[args->type])
+               goto aigp_ignore;
+
        if (!bgp_attr_aigp_valid(s, length))
                goto aigp_ignore;
 
@@ -3212,6 +3313,10 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args)
 aigp_ignore:
        stream_forward_getp(peer->curr, length);
 
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
        return BGP_ATTR_PARSE_PROCEED;
 }
 
@@ -3230,6 +3335,9 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
+       if (peer->discard_attrs[args->type])
+               goto otc_ignore;
+
        attr->otc = stream_getl(peer->curr);
        if (!attr->otc) {
                flog_err(EC_BGP_ATTR_MAL_AS_PATH, "OTC attribute value is 0");
@@ -3240,6 +3348,15 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
 
        return BGP_ATTR_PARSE_PROCEED;
+
+otc_ignore:
+       stream_forward_getp(peer->curr, length);
+
+       if (bgp_debug_update(peer, NULL, NULL, 1))
+               zlog_debug("%pBP: Ignoring attribute %s", peer,
+                          lookup_msg(attr_str, args->type, NULL));
+
+       return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* BGP unknown attribute treatment. */
@@ -3263,6 +3380,14 @@ bgp_attr_unknown(struct bgp_attr_parser_args *args)
        /* Forward read pointer of input stream. */
        stream_forward_getp(peer->curr, length);
 
+       if (peer->discard_attrs[type]) {
+               if (bgp_debug_update(peer, NULL, NULL, 1))
+                       zlog_debug("%pBP: Ignoring attribute %s", peer,
+                                  lookup_msg(attr_str, args->type, NULL));
+
+               return BGP_ATTR_PARSE_PROCEED;
+       }
+
        /* If any of the mandatory well-known attributes are not recognized,
           then the Error Subcode is set to Unrecognized Well-known
           Attribute.  The Data field contains the unrecognized attribute
@@ -4970,3 +5095,62 @@ void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi,
        len = stream_get_endp(s) - cp - 2;
        stream_putw_at(s, cp, len);
 }
+
+void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer,
+                                   const char *discard_attrs)
+{
+       int i, num_attributes;
+       char **attributes;
+       afi_t afi;
+       safi_t safi;
+
+       if (discard_attrs) {
+               frrstr_split(discard_attrs, " ", &attributes, &num_attributes);
+
+               for (i = 0; i < BGP_ATTR_MAX; i++)
+                       peer->discard_attrs[i] = false;
+
+               for (i = 0; i < num_attributes; i++) {
+                       uint8_t attr_num = strtoul(attributes[i], NULL, 10);
+
+                       XFREE(MTYPE_TMP, attributes[i]);
+
+                       /* Some of the attributes, just can't be ignored. */
+                       if (attr_num == BGP_ATTR_ORIGIN ||
+                           attr_num == BGP_ATTR_AS_PATH ||
+                           attr_num == BGP_ATTR_NEXT_HOP ||
+                           attr_num == BGP_ATTR_MULTI_EXIT_DISC ||
+                           attr_num == BGP_ATTR_MP_REACH_NLRI ||
+                           attr_num == BGP_ATTR_MP_UNREACH_NLRI ||
+                           attr_num == BGP_ATTR_EXT_COMMUNITIES) {
+                               vty_out(vty,
+                                       "%% Can't discard path-attribute %s, ignoring.\n",
+                                       lookup_msg(attr_str, attr_num, NULL));
+                               continue;
+                       }
+
+                       /* Ignore local-pref, originator-id, cluster-list only
+                        * for eBGP.
+                        */
+                       if (peer->sort != BGP_PEER_EBGP &&
+                           (attr_num == BGP_ATTR_LOCAL_PREF ||
+                            attr_num == BGP_ATTR_ORIGINATOR_ID ||
+                            attr_num == BGP_ATTR_CLUSTER_LIST)) {
+                               vty_out(vty,
+                                       "%% Can discard path-attribute %s only for eBGP, ignoring.\n",
+                                       lookup_msg(attr_str, attr_num, NULL));
+                               continue;
+                       }
+
+                       peer->discard_attrs[attr_num] = true;
+               }
+               XFREE(MTYPE_TMP, attributes);
+
+               /* Configuring path attributes to be discarded will trigger
+                * an inbound Route Refresh to ensure that the routing table
+                * is up to date.
+                */
+               FOREACH_AFI_SAFI (afi, safi)
+                       peer_clear_soft(peer, afi, safi, BGP_CLEAR_SOFT_IN);
+       }
+}
index a34da1a6de7258990201d1b7f77f908cb09bd065..cb0bf4a43c1121d5d99fdd0a60ffa71ef00a631d 100644 (file)
@@ -414,6 +414,8 @@ extern unsigned int attrhash_key_make(const void *p);
 extern void attr_show_all(struct vty *vty);
 extern unsigned long int attr_count(void);
 extern unsigned long int attr_unknown_count(void);
+extern void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer,
+                                          const char *discard_attrs);
 
 /* Cluster list prototypes. */
 extern bool cluster_loop_check(struct cluster_list *cluster,
index 9e540b63cbcd1a65e51f456b08808668c2c11f37..0b939f6acabc962d6035dcf9781c8947d4295d4f 100644 (file)
@@ -144,7 +144,6 @@ static const struct message bgp_notify_open_msg[] = {
        {BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"},
        {BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"},
        {BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"},
-       {BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"},
        {BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
        {BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
        {BGP_NOTIFY_OPEN_ROLE_MISMATCH, "/Role Mismatch"},
@@ -158,7 +157,6 @@ static const struct message bgp_notify_update_msg[] = {
        {BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"},
        {BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"},
        {BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"},
-       {BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop"},
        {BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"},
        {BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
        {BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
index 79c54dd32d63da983a28dbde6dfd8b427a65b849..659e93bdfab9fb7c8f6d316a8ba9cf9706d72c33 100644 (file)
@@ -1129,13 +1129,6 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
        return 0;
 }
 
-static int bgp_auth_parse(struct peer *peer, size_t length)
-{
-       bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
-                       BGP_NOTIFY_OPEN_AUTH_FAILURE);
-       return -1;
-}
-
 static bool strict_capability_same(struct peer *peer)
 {
        int i, j;
@@ -1339,17 +1332,11 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
                        zlog_debug(
                                "%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
                                peer->host, opt_type,
-                               opt_type == BGP_OPEN_OPT_AUTH
-                                       ? "Authentication"
-                                       : opt_type == BGP_OPEN_OPT_CAP
-                                                 ? "Capability"
-                                                 : "Unknown",
+                               opt_type == BGP_OPEN_OPT_CAP ? "Capability"
+                                                            : "Unknown",
                                opt_length);
 
                switch (opt_type) {
-               case BGP_OPEN_OPT_AUTH:
-                       ret = bgp_auth_parse(peer, opt_length);
-                       break;
                case BGP_OPEN_OPT_CAP:
                        ret = bgp_capability_parse(peer, opt_length,
                                                   mp_capability, &error);
index 87871573f000437d062bb54bc8cff5fcc42907e2..40f29dac5c28771f2e0ec0e64f96ffdb621d78e2 100644 (file)
@@ -2188,12 +2188,12 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
        }
 
        /* AS path loop check. */
-       if (onlypeer && onlypeer->as_path_loop_detection
-           && aspath_loop_check(piattr->aspath, onlypeer->as)) {
+       if (peer->as_path_loop_detection &&
+           aspath_loop_check(piattr->aspath, peer->as)) {
                if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
                        zlog_debug(
                                "%pBP [Update:SEND] suppress announcement to peer AS %u that is part of AS path.",
-                               onlypeer, onlypeer->as);
+                               peer, peer->as);
                return false;
        }
 
index afd2107b48fbc7720a02b2d407d8e1f9c16c2645..9ca57f08daa55ffb2205a597c2f44aa7a67df4e2 100644 (file)
@@ -164,6 +164,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
        dst->change_local_as = src->change_local_as;
        dst->shared_network = src->shared_network;
        dst->local_role = src->local_role;
+       dst->as_path_loop_detection = src->as_path_loop_detection;
 
        if (src->soo[afi][safi]) {
                ecommunity_free(&dst->soo[afi][safi]);
@@ -360,6 +361,9 @@ static unsigned int updgrp_hash_key_make(const void *p)
        key = jhash_1word(peer->max_packet_size, key);
        key = jhash_1word(peer->pmax_out[afi][safi], key);
 
+       if (peer->as_path_loop_detection)
+               key = jhash_2words(peer->as, peer->as_path_loop_detection, key);
+
        if (peer->group)
                key = jhash_1word(jhash(peer->group->name,
                                        strlen(peer->group->name), SEED1),
@@ -454,12 +458,13 @@ static unsigned int updgrp_hash_key_make(const void *p)
                        (intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS),
                        (intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS));
                zlog_debug(
-                       "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u",
+                       "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u, as_path_loop_detection: %d",
                        peer, (uint32_t)peer->addpath_type[afi][safi],
                        CHECK_FLAG(peer->cap, PEER_UPDGRP_CAP_FLAGS),
                        CHECK_FLAG(peer->af_cap[afi][safi],
                                   PEER_UPDGRP_AF_CAP_FLAGS),
-                       peer->v_routeadv, peer->change_local_as);
+                       peer->v_routeadv, peer->change_local_as,
+                       peer->as_path_loop_detection);
                zlog_debug(
                        "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s",
                        peer, peer->max_packet_size, peer->pmax_out[afi][safi],
index 4acf4f76aa55002bb52bfca8680477803121464b..9724c3533a40d8155117ae3aaaaf8a91bf2f8e12 100644 (file)
@@ -8808,6 +8808,32 @@ DEFPY(
        return CMD_SUCCESS;
 }
 
+DEFPY(neighbor_path_attribute_discard,
+      neighbor_path_attribute_discard_cmd,
+      "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor path-attribute discard (1-255)...",
+      NEIGHBOR_STR
+      NEIGHBOR_ADDR_STR2
+      "Manipulate path attributes from incoming UPDATE messages\n"
+      "Drop specified attributes from incoming UPDATE messages\n"
+      "Attribute number\n")
+{
+       struct peer *peer;
+       int idx = 0;
+       const char *discard_attrs = NULL;
+
+       peer = peer_and_group_lookup_vty(vty, neighbor);
+       if (!peer)
+               return CMD_WARNING_CONFIG_FAILED;
+
+       argv_find(argv, argc, "(1-255)", &idx);
+       if (idx)
+               discard_attrs = argv_concat(argv, argc, idx);
+
+       bgp_path_attribute_discard_vty(vty, peer, discard_attrs);
+
+       return CMD_SUCCESS;
+}
+
 static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
                         struct ecommunity **list, bool is_rt6)
 {
@@ -17444,6 +17470,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
                vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
                        addr);
 
+       /* path-attribute discard */
+       char discard_attrs_str[BUFSIZ] = {0};
+       bool discard_attrs = bgp_path_attribute_discard(
+               peer, discard_attrs_str, sizeof(discard_attrs_str));
+
+       if (discard_attrs)
+               vty_out(vty, " neighbor %s path-attribute discard %s\n", addr,
+                       discard_attrs_str);
+
        if (!CHECK_FLAG(peer->peer_gr_new_status_flag,
                        PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) {
 
@@ -19559,6 +19594,9 @@ void bgp_vty_init(void)
        install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd);
        install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd);
 
+       /* "neighbor path-attribute discard" commands. */
+       install_element(BGP_NODE, &neighbor_path_attribute_discard_cmd);
+
        /* "neighbor passive" commands. */
        install_element(BGP_NODE, &neighbor_passive_cmd);
        install_element(BGP_NODE, &no_neighbor_passive_cmd);
index 9b4aa38d7af89a5b2c7df306ec9d8ad985580347..284321c36a12df5b45e444c0bd54a8f73c3ce9a9 100644 (file)
@@ -3576,7 +3576,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
        if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
                if (BGP_DEBUG(zebra, ZEBRA))
                        zlog_debug("%s: Registering BGP instance %s to zebra",
-                                  __func__, name);
+                                  __func__, bgp->name_pretty);
                bgp_zebra_instance_register(bgp);
        }
 
@@ -4203,6 +4203,25 @@ static void peer_drop_dynamic_neighbor(struct peer *peer)
                           peer->group->name, dncount);
 }
 
+bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size)
+{
+       if (!buf)
+               return false;
+
+       buf[0] = '\0';
+
+       for (unsigned int i = 0; i < BGP_ATTR_MAX; i++) {
+               if (peer->discard_attrs[i])
+                       snprintf(buf + strlen(buf), size - strlen(buf), "%s%d",
+                                (strlen(buf) > 0) ? " " : "", i);
+       }
+
+       if (strlen(buf) > 0)
+               return true;
+
+       return false;
+}
+
 /* If peer is configured at least one address family return 1. */
 bool peer_active(struct peer *peer)
 {
index 86979123146fa64519a300fb744bb6383c62345b..e0bf7e9416c25b23782e0b745f63e3695cf0fad0 100644 (file)
@@ -1806,6 +1806,10 @@ struct peer {
 
        bool shut_during_cfg;
 
+#define BGP_ATTR_MAX 255
+       /* Path attributes discard */
+       bool discard_attrs[BGP_ATTR_MAX];
+
        QOBJ_FIELDS;
 };
 DECLARE_QOBJ_TYPE(peer);
@@ -1887,7 +1891,6 @@ struct bgp_nlri {
 #define BGP_MSG_ROUTE_REFRESH_OLD              128
 
 /* BGP open optional parameter.  */
-#define BGP_OPEN_OPT_AUTH                        1
 #define BGP_OPEN_OPT_CAP                         2
 
 /* BGP4 attribute type codes.  */
@@ -1952,7 +1955,6 @@ struct bgp_nlri {
 #define BGP_NOTIFY_OPEN_BAD_PEER_AS              2
 #define BGP_NOTIFY_OPEN_BAD_BGP_IDENT            3
 #define BGP_NOTIFY_OPEN_UNSUP_PARAM              4
-#define BGP_NOTIFY_OPEN_AUTH_FAILURE             5
 #define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME          6
 #define BGP_NOTIFY_OPEN_UNSUP_CAPBL              7
 #define BGP_NOTIFY_OPEN_ROLE_MISMATCH           11
@@ -1964,7 +1966,6 @@ struct bgp_nlri {
 #define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR          4
 #define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR          5
 #define BGP_NOTIFY_UPDATE_INVAL_ORIGIN           6
-#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP          7
 #define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP         8
 #define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR           9
 #define BGP_NOTIFY_UPDATE_INVAL_NETWORK         10
@@ -2669,6 +2670,8 @@ extern void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi,
                                               safi_t safi);
 extern void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
                                  int outbound);
+extern bool bgp_path_attribute_discard(struct peer *peer, char *buf,
+                                      size_t size);
 #ifdef _FRR_ATTRIBUTE_PRINTFRR
 /* clang-format off */
 #pragma FRR printfrr_ext "%pBP" (struct peer *)
index 0eb1064519b598271fccaa181a28fe6cf8ed821c..1a42996771de6027c48c442b44f539f66fc67117 100644 (file)
@@ -353,6 +353,54 @@ The following commands are available inside the interface configuration node.
    that interface.
 
 
+.. _bfd-static-peer-config:
+
+BFD Static Route Monitoring Configuration
+-----------------------------------------
+
+A monitored static route conditions the installation to the RIB on the
+BFD session running state: when BFD session is up the route is installed
+to RIB, but when the BFD session is down it is removed from the RIB.
+
+The following commands are available inside the configuration node:
+
+.. clicmd:: ip route A.B.C.D/M A.B.C.D bfd [{multi-hop|source A.B.C.D|profile BFDPROF}]
+
+   Configure a static route for ``A.B.C.D/M`` using gateway ``A.B.C.D`` and use
+   the gateway address as BFD peer destination address.
+
+.. clicmd:: ipv6 route X:X::X:X/M [from X:X::X:X/M] X:X::X:X bfd [{multi-hop|source X:X::X:X|profile BFDPROF}]
+
+   Configure a static route for ``X:X::X:X/M`` using gateway
+   ``X:X::X:X`` and use the gateway address as BFD peer destination
+   address.
+
+The static routes when uninstalled will no longer show up in the output of
+the command ``show ip route`` or ``show ipv6 route``, instead we must use the
+BFD static route show command to see these monitored route status.
+
+.. clicmd:: show bfd static route [json]
+
+   Show all monitored static routes and their status.
+
+   Example output:
+
+   ::
+
+      Showing BFD monitored static routes:
+
+        Route groups:
+          rtg1 peer 172.16.0.1 (status: uninstalled):
+              2001:db8::100/128
+
+      Next hops:
+        VRF default IPv4 Unicast:
+            192.168.100.0/24 peer 172.16.0.1 (status: uninstalled)
+
+        VRF default IPv4 Multicast:
+
+        VRF default IPv6 Unicast:
+
 .. _bfd-configuration:
 
 Configuration
index 7f97491630a15f278ab619be47ff66a1c6cde1db..77b4d35afb8c9c423251f279fb0d5d427075140a 100644 (file)
@@ -1704,6 +1704,13 @@ Configuring Peers
 
    Default: disabled.
 
+.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> path-attribute discard (1-255)...
+
+   Drops specified path attributes from BGP UPDATE messages from the specified neighbor.
+
+   If you do not want specific attributes, you can drop them using this command, and
+   let the BGP proceed by ignoring those attributes.
+
 .. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> graceful-shutdown
 
    Mark all routes from this neighbor as less preferred by setting ``graceful-shutdown``
index 29b8d85d8d3a28c042af2d6336d5647eb6ab711a..ae402ec5cb8f6a3b119c8ab2a2689ae0da8cbf74 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
 #include "stream.h"
 #include "vrf.h"
 #include "zclient.h"
+#include "libfrr.h"
 #include "table.h"
 #include "vty.h"
 #include "bfd.h"
 
 DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info");
+DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache");
 
 /**
  * BFD protocol integration configuration.
@@ -47,6 +49,29 @@ enum bfd_session_event {
        BSE_INSTALL,
 };
 
+/**
+ * BFD source selection result cache.
+ *
+ * This structure will keep track of the result based on the destination
+ * prefix. When the result changes all related BFD sessions with automatic
+ * source will be updated.
+ */
+struct bfd_source_cache {
+       /** Address VRF belongs. */
+       vrf_id_t vrf_id;
+       /** Destination network address. */
+       struct prefix address;
+       /** Source selected. */
+       struct prefix source;
+       /** Is the source address valid? */
+       bool valid;
+       /** BFD sessions using this. */
+       size_t refcount;
+
+       SLIST_ENTRY(bfd_source_cache) entry;
+};
+SLIST_HEAD(bfd_source_list, bfd_source_cache);
+
 /**
  * Data structure to do the necessary tricks to hide the BFD protocol
  * integration internals.
@@ -82,6 +107,11 @@ struct bfd_session_params {
        /** BFD session installation state. */
        bool installed;
 
+       /** Automatic source selection. */
+       bool auto_source;
+       /** Currently selected source. */
+       struct bfd_source_cache *source_cache;
+
        /** Global BFD paramaters list. */
        TAILQ_ENTRY(bfd_session_params) entry;
 };
@@ -92,11 +122,15 @@ struct bfd_sessions_global {
         * without code duplication among daemons.
         */
        TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
+       /** BFD automatic source selection cache. */
+       struct bfd_source_list source_list;
 
        /** Pointer to FRR's event manager. */
        struct thread_master *tm;
        /** Pointer to zebra client data structure. */
        struct zclient *zc;
+       /** Zebra next hop tracking (NHT) client. */
+       struct zclient *nht_zclient;
 
        /** Debugging state. */
        bool debugging;
@@ -110,6 +144,34 @@ static struct bfd_sessions_global bsglobal;
 /** Global empty address for IPv4/IPv6. */
 static const struct in6_addr i6a_zero;
 
+/*
+ * Prototypes
+ */
+static void bfd_nht_zclient_connect(struct thread *thread);
+
+static void bfd_nht_zclient_connected(struct zclient *zclient);
+static int bfd_nht_update(ZAPI_CALLBACK_ARGS);
+
+static void bfd_source_cache_get(struct bfd_session_params *session);
+static void bfd_source_cache_put(struct bfd_session_params *session);
+
+static inline void
+bfd_source_cache_register(const struct bfd_source_cache *source)
+{
+       zclient_send_rnh(bsglobal.nht_zclient, ZEBRA_NEXTHOP_REGISTER,
+                        &source->address, SAFI_UNICAST, false, false,
+                        source->vrf_id);
+}
+
+static inline void
+bfd_source_cache_unregister(const struct bfd_source_cache *source)
+{
+       zclient_send_rnh(bsglobal.nht_zclient, ZEBRA_NEXTHOP_UNREGISTER,
+                        &source->address, SAFI_UNICAST, false, false,
+                        source->vrf_id);
+}
+
+
 /*
  * bfd_get_peer_info - Extract the Peer information for which the BFD session
  *                     went down from the message sent from Zebra to clients.
@@ -531,6 +593,8 @@ void bfd_sess_free(struct bfd_session_params **bsp)
        /* Remove from global list. */
        TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
 
+       bfd_source_cache_put(*bsp);
+
        /* Free the memory and point to NULL. */
        XFREE(MTYPE_BFD_INFO, (*bsp));
 }
@@ -565,6 +629,8 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
 
        /* If already installed, remove the old setting. */
        _bfd_sess_remove(bsp);
+       /* Address changed so we must reapply auto source. */
+       bfd_source_cache_put(bsp);
 
        bsp->args.family = AF_INET;
 
@@ -578,6 +644,9 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
 
        assert(dst);
        memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
+
+       if (bsp->auto_source)
+               bfd_source_cache_get(bsp);
 }
 
 void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
@@ -589,6 +658,8 @@ void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
 
        /* If already installed, remove the old setting. */
        _bfd_sess_remove(bsp);
+       /* Address changed so we must reapply auto source. */
+       bfd_source_cache_put(bsp);
 
        bsp->args.family = AF_INET6;
 
@@ -600,6 +671,9 @@ void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
 
        assert(dst);
        bsp->args.dst = *dst;
+
+       if (bsp->auto_source)
+               bfd_source_cache_get(bsp);
 }
 
 void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
@@ -646,8 +720,13 @@ void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
 
        /* If already installed, remove the old setting. */
        _bfd_sess_remove(bsp);
+       /* Address changed so we must reapply auto source. */
+       bfd_source_cache_put(bsp);
 
        bsp->args.vrf_id = vrf_id;
+
+       if (bsp->auto_source)
+               bfd_source_cache_get(bsp);
 }
 
 void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops)
@@ -677,6 +756,18 @@ void bfd_sess_set_timers(struct bfd_session_params *bsp,
        bsp->args.min_tx = min_tx;
 }
 
+void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable)
+{
+       if (bsp->auto_source == enable)
+               return;
+
+       bsp->auto_source = enable;
+       if (enable)
+               bfd_source_cache_get(bsp);
+       else
+               bfd_source_cache_put(bsp);
+}
+
 void bfd_sess_install(struct bfd_session_params *bsp)
 {
        bsp->lastev = BSE_INSTALL;
@@ -746,6 +837,11 @@ void bfd_sess_timers(const struct bfd_session_params *bsp,
        *min_tx = bsp->args.min_tx;
 }
 
+bool bfd_sess_auto_source(const struct bfd_session_params *bsp)
+{
+       return bsp->auto_source;
+}
+
 void bfd_sess_show(struct vty *vty, struct json_object *json,
                   struct bfd_session_params *bsp)
 {
@@ -950,10 +1046,51 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
        return 0;
 }
 
+/**
+ * Frees all allocated resources and stops any activity.
+ *
+ * Must be called after every BFD session has been successfully
+ * unconfigured otherwise this function will `free()` any available
+ * session causing existing pointers to dangle.
+ *
+ * This is just a comment, in practice it will be called by the FRR
+ * library late finish hook. \see `bfd_protocol_integration_init`.
+ */
+static int bfd_protocol_integration_finish(void)
+{
+       if (bsglobal.zc == NULL)
+               return 0;
+
+       while (!TAILQ_EMPTY(&bsglobal.bsplist)) {
+               struct bfd_session_params *session =
+                       TAILQ_FIRST(&bsglobal.bsplist);
+               bfd_sess_free(&session);
+       }
+
+       /*
+        * BFD source cache is linked to sessions, if all sessions are gone
+        * then the source cache must be empty.
+        */
+       if (!SLIST_EMPTY(&bsglobal.source_list))
+               zlog_warn("BFD integration source cache not empty");
+
+       zclient_stop(bsglobal.nht_zclient);
+       zclient_free(bsglobal.nht_zclient);
+
+       return 0;
+}
+
+static zclient_handler *const bfd_nht_handlers[] = {
+       [ZEBRA_NEXTHOP_UPDATE] = bfd_nht_update,
+};
+
 void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
 {
+       struct zclient_options bfd_nht_options = zclient_options_default;
+
        /* Initialize data structure. */
        TAILQ_INIT(&bsglobal.bsplist);
+       SLIST_INIT(&bsglobal.source_list);
 
        /* Copy pointers. */
        bsglobal.zc = zc;
@@ -964,6 +1101,18 @@ void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
 
        /* Send the client registration */
        bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
+
+       /* Start NHT client (for automatic source decisions). */
+       bsglobal.nht_zclient =
+               zclient_new(tm, &bfd_nht_options, bfd_nht_handlers,
+                           array_size(bfd_nht_handlers));
+       bsglobal.nht_zclient->sock = -1;
+       bsglobal.nht_zclient->privs = zc->privs;
+       bsglobal.nht_zclient->zebra_connected = bfd_nht_zclient_connected;
+       thread_add_timer(tm, bfd_nht_zclient_connect, bsglobal.nht_zclient, 1,
+                        &bsglobal.nht_zclient->t_connect);
+
+       hook_register(frr_fini, bfd_protocol_integration_finish);
 }
 
 void bfd_protocol_integration_set_debug(bool enable)
@@ -985,3 +1134,305 @@ bool bfd_protocol_integration_shutting_down(void)
 {
        return bsglobal.shutting_down;
 }
+
+/*
+ * BFD automatic source selection
+ *
+ * This feature will use the next hop tracking (NHT) provided by zebra
+ * to find out the source address by looking at the output interface.
+ *
+ * When the interface address / routing table change we'll be notified
+ * and be able to update the source address accordingly.
+ *
+ *     <daemon>                 zebra
+ *         |
+ * +-----------------+
+ * | BFD session set |
+ * | to auto source  |
+ * +-----------------+
+ *         |
+ *         \                 +-----------------+
+ *          -------------->  | Resolves        |
+ *                           | destination     |
+ *                           | address         |
+ *                           +-----------------+
+ *                                |
+ * +-----------------+            /
+ * | Sets resolved   | <----------
+ * | source address  |
+ * +-----------------+
+ */
+static bool
+bfd_source_cache_session_match(const struct bfd_source_cache *source,
+                              const struct bfd_session_params *session)
+{
+       const struct in_addr *address;
+       const struct in6_addr *address_v6;
+
+       if (session->args.vrf_id != source->vrf_id)
+               return false;
+       if (session->args.family != source->address.family)
+               return false;
+
+       switch (session->args.family) {
+       case AF_INET:
+               address = (const struct in_addr *)&session->args.dst;
+               if (address->s_addr != source->address.u.prefix4.s_addr)
+                       return false;
+               break;
+       case AF_INET6:
+               address_v6 = &session->args.dst;
+               if (memcmp(address_v6, &source->address.u.prefix6,
+                          sizeof(struct in6_addr)))
+                       return false;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+static struct bfd_source_cache *
+bfd_source_cache_find(vrf_id_t vrf_id, const struct prefix *prefix)
+{
+       struct bfd_source_cache *source;
+
+       SLIST_FOREACH (source, &bsglobal.source_list, entry) {
+               if (source->vrf_id != vrf_id)
+                       continue;
+               if (!prefix_same(&source->address, prefix))
+                       continue;
+
+               return source;
+       }
+
+       return NULL;
+}
+
+static void bfd_source_cache_get(struct bfd_session_params *session)
+{
+       struct bfd_source_cache *source;
+       struct prefix target = {};
+
+       switch (session->args.family) {
+       case AF_INET:
+               target.family = AF_INET;
+               target.prefixlen = IPV4_MAX_BITLEN;
+               memcpy(&target.u.prefix4, &session->args.dst,
+                      sizeof(struct in_addr));
+               break;
+       case AF_INET6:
+               target.family = AF_INET6;
+               target.prefixlen = IPV6_MAX_BITLEN;
+               memcpy(&target.u.prefix6, &session->args.dst,
+                      sizeof(struct in6_addr));
+               break;
+       default:
+               return;
+       }
+
+       source = bfd_source_cache_find(session->args.vrf_id, &target);
+       if (source) {
+               if (session->source_cache == source)
+                       return;
+
+               bfd_source_cache_put(session);
+               session->source_cache = source;
+               source->refcount++;
+               return;
+       }
+
+       source = XCALLOC(MTYPE_BFD_SOURCE, sizeof(*source));
+       prefix_copy(&source->address, &target);
+       source->vrf_id = session->args.vrf_id;
+       SLIST_INSERT_HEAD(&bsglobal.source_list, source, entry);
+
+       bfd_source_cache_put(session);
+       session->source_cache = source;
+       source->refcount = 1;
+
+       bfd_source_cache_register(source);
+
+       return;
+}
+
+static void bfd_source_cache_put(struct bfd_session_params *session)
+{
+       if (session->source_cache == NULL)
+               return;
+
+       session->source_cache->refcount--;
+       if (session->source_cache->refcount > 0) {
+               session->source_cache = NULL;
+               return;
+       }
+
+       bfd_source_cache_unregister(session->source_cache);
+       SLIST_REMOVE(&bsglobal.source_list, session->source_cache,
+                    bfd_source_cache, entry);
+       XFREE(MTYPE_BFD_SOURCE, session->source_cache);
+}
+
+/** Updates BFD running session if source address has changed. */
+static void
+bfd_source_cache_update_session(const struct bfd_source_cache *source,
+                               struct bfd_session_params *session)
+{
+       const struct in_addr *address;
+       const struct in6_addr *address_v6;
+
+       switch (session->args.family) {
+       case AF_INET:
+               address = (const struct in_addr *)&session->args.src;
+               if (memcmp(address, &source->source.u.prefix4,
+                          sizeof(struct in_addr)) == 0)
+                       return;
+
+               _bfd_sess_remove(session);
+               memcpy(&session->args.src, &source->source.u.prefix4,
+                      sizeof(struct in_addr));
+               break;
+       case AF_INET6:
+               address_v6 = &session->args.src;
+               if (memcmp(address_v6, &source->source.u.prefix6,
+                          sizeof(struct in6_addr)) == 0)
+                       return;
+
+               _bfd_sess_remove(session);
+               memcpy(&session->args.src, &source->source.u.prefix6,
+                      sizeof(struct in6_addr));
+               break;
+       default:
+               return;
+       }
+
+       bfd_sess_install(session);
+}
+
+static void
+bfd_source_cache_update_sessions(const struct bfd_source_cache *source)
+{
+       struct bfd_session_params *session;
+
+       if (!source->valid)
+               return;
+
+       TAILQ_FOREACH (session, &bsglobal.bsplist, entry) {
+               if (!session->auto_source)
+                       continue;
+               if (!bfd_source_cache_session_match(source, session))
+                       continue;
+
+               bfd_source_cache_update_session(source, session);
+       }
+}
+
+/**
+ * Try to translate next hop information into source address.
+ *
+ * \returns `true` if source changed otherwise `false`.
+ */
+static bool bfd_source_cache_update(struct bfd_source_cache *source,
+                                   const struct zapi_route *route)
+{
+       size_t nh_index;
+
+       for (nh_index = 0; nh_index < route->nexthop_num; nh_index++) {
+               const struct zapi_nexthop *nh = &route->nexthops[nh_index];
+               const struct interface *interface;
+               const struct connected *connected;
+               const struct listnode *node;
+
+               interface = if_lookup_by_index(nh->ifindex, nh->vrf_id);
+               if (interface == NULL) {
+                       zlog_err("next hop interface not found (index %d)",
+                                nh->ifindex);
+                       continue;
+               }
+
+               for (ALL_LIST_ELEMENTS_RO(interface->connected, node,
+                                         connected)) {
+                       if (source->address.family !=
+                           connected->address->family)
+                               continue;
+                       if (prefix_same(connected->address, &source->source))
+                               return false;
+                       /*
+                        * Skip link-local as it is only useful for single hop
+                        * and in that case no source is specified usually.
+                        */
+                       if (source->address.family == AF_INET6 &&
+                           IN6_IS_ADDR_LINKLOCAL(
+                                   &connected->address->u.prefix6))
+                               continue;
+
+                       prefix_copy(&source->source, connected->address);
+                       source->valid = true;
+                       return true;
+               }
+       }
+
+       memset(&source->source, 0, sizeof(source->source));
+       source->valid = false;
+       return false;
+}
+
+static void bfd_nht_zclient_connect(struct thread *thread)
+{
+       struct zclient *zclient = THREAD_ARG(thread);
+
+       if (bsglobal.debugging)
+               zlog_debug("BFD NHT zclient connection attempt");
+
+       if (zclient_start(zclient) == -1) {
+               if (bsglobal.debugging)
+                       zlog_debug("BFD NHT zclient connection failed");
+
+               thread_add_timer(bsglobal.tm, bfd_nht_zclient_connect, zclient,
+                                3, &zclient->t_connect);
+               return;
+       }
+
+       if (bsglobal.debugging)
+               zlog_debug("BFD NHT zclient connection succeeded");
+}
+
+static void bfd_nht_zclient_connected(struct zclient *zclient)
+{
+       struct bfd_source_cache *source;
+
+       if (bsglobal.debugging)
+               zlog_debug("BFD NHT zclient connected");
+
+       SLIST_FOREACH (source, &bsglobal.source_list, entry)
+               bfd_source_cache_register(source);
+}
+
+static int bfd_nht_update(ZAPI_CALLBACK_ARGS)
+{
+       struct bfd_source_cache *source;
+       struct zapi_route route;
+       struct prefix match;
+
+       if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &route)) {
+               zlog_warn("BFD NHT update decode failure");
+               return 0;
+       }
+       if (cmd != ZEBRA_NEXTHOP_UPDATE)
+               return 0;
+
+       if (bsglobal.debugging)
+               zlog_debug("BFD NHT update for %pFX", &route.prefix);
+
+       SLIST_FOREACH (source, &bsglobal.source_list, entry) {
+               if (source->vrf_id != route.vrf_id)
+                       continue;
+               if (!prefix_same(&match, &source->address))
+                       continue;
+               if (bfd_source_cache_update(source, &route))
+                       bfd_source_cache_update_sessions(source);
+       }
+
+       return 0;
+}
index 344ecc2c4d216e4fae283561981948e760b8149b..b7e4eea5f316097c8aba60bc972f3cac796ccc81 100644 (file)
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -221,6 +221,18 @@ void bfd_sess_set_timers(struct bfd_session_params *bsp,
                         uint8_t detection_multiplier, uint32_t min_rx,
                         uint32_t min_tx);
 
+/**
+ * Configures the automatic source selection for the BFD session.
+ *
+ * NOTE:
+ * Setting this configuration will override the IP source value set by
+ * `bfd_sess_set_ipv4_addrs` or `bfd_sess_set_ipv6_addrs`.
+ *
+ * \param bsp BFD session parameters
+ * \param enable BFD automatic source selection state.
+ */
+void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable);
+
 /**
  * Installs or updates the BFD session based on the saved session arguments.
  *
@@ -330,6 +342,11 @@ void bfd_sess_timers(const struct bfd_session_params *bsp,
                     uint8_t *detection_multiplier, uint32_t *min_rx,
                     uint32_t *min_tx);
 
+/**
+ * Gets the automatic source selection state.
+ */
+bool bfd_sess_auto_source(const struct bfd_session_params *bsp);
+
 /**
  * Show BFD session configuration and status. If `json` is provided (e.g. not
  * `NULL`) then information will be inserted in object, otherwise printed to
index 0f9715e81c1ca8c472d6ef6ea613734e0a4f64db..5cc40f71f6537b412340e66f497a1be241a4d4b2 100644 (file)
@@ -497,6 +497,13 @@ struct cmd_node {
 #define ROLE_STR                                                               \
        "Providing transit\nRoute server\nRS client\nUsing transit\nPublic/private peering\n"
 
+/* BFD protocol integration strings. */
+#define BFD_INTEGRATION_STR "BFD monitoring\n"
+#define BFD_INTEGRATION_MULTI_HOP_STR "Use BFD multi hop session\n"
+#define BFD_INTEGRATION_SOURCE_STR "Use source for BFD session\n"
+#define BFD_INTEGRATION_SOURCEV4_STR "Use IPv4 source for BFD session\n"
+#define BFD_INTEGRATION_SOURCEV6_STR "Use IPv4 source for BFD session\n"
+
 /* Prototypes. */
 extern void install_node(struct cmd_node *node);
 extern void install_default(enum node_type);
diff --git a/staticd/static_bfd.c b/staticd/static_bfd.c
new file mode 100644 (file)
index 0000000..fc8b518
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Static daemon BFD integration.
+ *
+ * Copyright (C) 2020-2022 Network Device Education Foundation, Inc. ("NetDEF")
+ *                         Rafael Zalamena
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/bfd.h"
+#include "lib/printfrr.h"
+#include "lib/srcdest_table.h"
+
+#include "staticd/static_routes.h"
+#include "staticd/static_zebra.h"
+#include "staticd/static_debug.h"
+
+#include "lib/openbsd-queue.h"
+
+/*
+ * Next hop BFD monitoring settings.
+ */
+static void static_next_hop_bfd_change(struct static_nexthop *sn,
+                                      const struct bfd_session_status *bss)
+{
+       switch (bss->state) {
+       case BSS_UNKNOWN:
+               /* FALLTHROUGH: no known state yet. */
+       case BSS_ADMIN_DOWN:
+               /* NOTHING: we or the remote end administratively shutdown. */
+               break;
+       case BSS_DOWN:
+               /* Peer went down, remove this next hop. */
+               DEBUGD(&static_dbg_bfd,
+                      "%s: next hop is down, remove it from RIB", __func__);
+               sn->path_down = true;
+               static_zebra_route_add(sn->pn, true);
+               break;
+       case BSS_UP:
+               /* Peer is back up, add this next hop. */
+               DEBUGD(&static_dbg_bfd, "%s: next hop is up, add it to RIB",
+                      __func__);
+               sn->path_down = false;
+               static_zebra_route_add(sn->pn, true);
+               break;
+       }
+}
+
+static void static_next_hop_bfd_updatecb(
+       __attribute__((unused)) struct bfd_session_params *bsp,
+       const struct bfd_session_status *bss, void *arg)
+{
+       static_next_hop_bfd_change(arg, bss);
+}
+
+static inline int
+static_next_hop_type_to_family(const struct static_nexthop *sn)
+{
+       switch (sn->type) {
+       case STATIC_IPV4_GATEWAY_IFNAME:
+       case STATIC_IPV6_GATEWAY_IFNAME:
+       case STATIC_IPV4_GATEWAY:
+       case STATIC_IPV6_GATEWAY:
+               if (sn->type == STATIC_IPV4_GATEWAY ||
+                   sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+                       return AF_INET;
+               else
+                       return AF_INET6;
+               break;
+       case STATIC_IFNAME:
+       case STATIC_BLACKHOLE:
+       default:
+               zlog_err("%s: invalid next hop type", __func__);
+               break;
+       }
+
+       return AF_UNSPEC;
+}
+
+void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+                                       const struct lyd_node *dnode)
+{
+       bool use_interface;
+       bool use_profile;
+       bool use_source;
+       bool onlink;
+       bool mhop;
+       int family;
+       struct ipaddr source;
+
+       use_interface = false;
+       use_source = yang_dnode_exists(dnode, "./source");
+       use_profile = yang_dnode_exists(dnode, "./profile");
+       onlink = yang_dnode_exists(dnode, "../onlink") &&
+                yang_dnode_get_bool(dnode, "../onlink");
+       mhop = yang_dnode_get_bool(dnode, "./multi-hop");
+
+
+       family = static_next_hop_type_to_family(sn);
+       if (family == AF_UNSPEC)
+               return;
+
+       if (sn->type == STATIC_IPV4_GATEWAY_IFNAME ||
+           sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+               use_interface = true;
+
+       /* Reconfigure or allocate new memory. */
+       if (sn->bsp == NULL)
+               sn->bsp = bfd_sess_new(static_next_hop_bfd_updatecb, sn);
+
+       /* Configure the session. */
+       if (use_source)
+               yang_dnode_get_ip(&source, dnode, "./source");
+
+       if (onlink || mhop == false)
+               bfd_sess_set_auto_source(sn->bsp, false);
+       else
+               bfd_sess_set_auto_source(sn->bsp, !use_source);
+
+       /* Configure the session.*/
+       if (family == AF_INET)
+               bfd_sess_set_ipv4_addrs(sn->bsp,
+                                       use_source ? &source.ip._v4_addr : NULL,
+                                       &sn->addr.ipv4);
+       else if (family == AF_INET6)
+               bfd_sess_set_ipv6_addrs(sn->bsp,
+                                       use_source ? &source.ip._v6_addr : NULL,
+                                       &sn->addr.ipv6);
+
+       bfd_sess_set_interface(sn->bsp, use_interface ? sn->ifname : NULL);
+
+       bfd_sess_set_profile(sn->bsp, use_profile ? yang_dnode_get_string(
+                                                           dnode, "./profile")
+                                                 : NULL);
+
+       bfd_sess_set_hop_count(sn->bsp, (onlink || mhop == false) ? 1 : 254);
+
+       /* Install or update the session. */
+       bfd_sess_install(sn->bsp);
+
+       /* Update current path status. */
+       sn->path_down = (bfd_sess_status(sn->bsp) != BSS_UP);
+}
+
+void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn)
+{
+       bfd_sess_free(&sn->bsp);
+
+       /* Reset path status. */
+       sn->path_down = false;
+}
+
+void static_next_hop_bfd_source(struct static_nexthop *sn,
+                               const struct ipaddr *source)
+{
+       int family;
+
+       if (sn->bsp == NULL)
+               return;
+
+       family = static_next_hop_type_to_family(sn);
+       if (family == AF_UNSPEC)
+               return;
+
+       bfd_sess_set_auto_source(sn->bsp, false);
+       if (family == AF_INET)
+               bfd_sess_set_ipv4_addrs(sn->bsp, &source->ip._v4_addr,
+                                       &sn->addr.ipv4);
+       else if (family == AF_INET6)
+               bfd_sess_set_ipv6_addrs(sn->bsp, &source->ip._v6_addr,
+                                       &sn->addr.ipv6);
+
+       bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_auto_source(struct static_nexthop *sn)
+{
+       if (sn->bsp == NULL)
+               return;
+
+       bfd_sess_set_auto_source(sn->bsp, true);
+       bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop)
+{
+       if (sn->bsp == NULL)
+               return;
+
+       bfd_sess_set_hop_count(sn->bsp, mhop ? 254 : 1);
+       bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_profile(struct static_nexthop *sn, const char *name)
+{
+       if (sn->bsp == NULL)
+               return;
+
+       bfd_sess_set_profile(sn->bsp, name);
+       bfd_sess_install(sn->bsp);
+}
+
+void static_bfd_initialize(struct zclient *zc, struct thread_master *tm)
+{
+       /* Initialize BFD integration library. */
+       bfd_protocol_integration_init(zc, tm);
+}
+
+/*
+ * Display functions
+ */
+static void static_bfd_show_nexthop_json(struct vty *vty,
+                                        struct json_object *jo,
+                                        const struct static_nexthop *sn)
+{
+       const struct prefix *dst_p, *src_p;
+       struct json_object *jo_nh;
+
+       jo_nh = json_object_new_object();
+
+       srcdest_rnode_prefixes(sn->rn, &dst_p, &src_p);
+       if (src_p)
+               json_object_string_addf(jo_nh, "from", "%pFX", src_p);
+
+       json_object_string_addf(jo_nh, "prefix", "%pFX", dst_p);
+       json_object_string_add(jo_nh, "vrf", sn->nh_vrfname);
+
+       json_object_boolean_add(jo_nh, "installed", !sn->path_down);
+
+       json_object_array_add(jo, jo_nh);
+}
+
+static void static_bfd_show_path_json(struct vty *vty, struct json_object *jo,
+                                     struct route_table *rt)
+{
+       struct route_node *rn;
+
+       for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+               struct static_route_info *si = static_route_info_from_rnode(rn);
+               struct static_path *sp;
+
+               if (si == NULL)
+                       continue;
+
+               frr_each (static_path_list, &si->path_list, sp) {
+                       struct static_nexthop *sn;
+
+                       frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+                               /* Skip non configured BFD sessions. */
+                               if (sn->bsp == NULL)
+                                       continue;
+
+                               static_bfd_show_nexthop_json(vty, jo, sn);
+                       }
+               }
+       }
+}
+
+static void static_bfd_show_json(struct vty *vty)
+{
+       struct json_object *jo, *jo_path, *jo_afi_safi;
+       struct vrf *vrf;
+
+       jo = json_object_new_object();
+       jo_path = json_object_new_object();
+
+       json_object_object_add(jo, "path-list", jo_path);
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+               const struct static_vrf *svrf = vrf->info;
+               struct route_table *rt;
+
+               jo_afi_safi = json_object_new_array();
+               json_object_object_add(jo_path, "ipv4-unicast", jo_afi_safi);
+               rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+               if (rt)
+                       static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+               jo_afi_safi = json_object_new_array();
+               json_object_object_add(jo_path, "ipv4-multicast", jo_afi_safi);
+               rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+               if (rt)
+                       static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+               jo_afi_safi = json_object_new_array();
+               json_object_object_add(jo_path, "ipv6-unicast", jo_afi_safi);
+               rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+               if (rt)
+                       static_bfd_show_path_json(vty, jo_afi_safi, rt);
+       }
+
+       vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
+       json_object_free(jo);
+}
+
+static void static_bfd_show_nexthop(struct vty *vty,
+                                   const struct static_nexthop *sn)
+{
+       vty_out(vty, "        %pRN", sn->rn);
+
+       if (sn->bsp == NULL) {
+               vty_out(vty, "\n");
+               return;
+       }
+
+       if (sn->type == STATIC_IPV4_GATEWAY ||
+           sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+               vty_out(vty, " peer %pI4", &sn->addr.ipv4);
+       else if (sn->type == STATIC_IPV6_GATEWAY ||
+                sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+               vty_out(vty, " peer %pI6", &sn->addr.ipv6);
+       else
+               vty_out(vty, " peer unknown");
+
+       vty_out(vty, " (status: %s)\n",
+               sn->path_down ? "uninstalled" : "installed");
+}
+
+static void static_bfd_show_path(struct vty *vty, struct route_table *rt)
+{
+       struct route_node *rn;
+
+       for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+               struct static_route_info *si = static_route_info_from_rnode(rn);
+               struct static_path *sp;
+
+               if (si == NULL)
+                       continue;
+
+               frr_each (static_path_list, &si->path_list, sp) {
+                       struct static_nexthop *sn;
+
+                       frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+                               /* Skip non configured BFD sessions. */
+                               if (sn->bsp == NULL)
+                                       continue;
+
+                               static_bfd_show_nexthop(vty, sn);
+                       }
+               }
+       }
+}
+
+void static_bfd_show(struct vty *vty, bool json)
+{
+       struct vrf *vrf;
+
+       if (json) {
+               static_bfd_show_json(vty);
+               return;
+       }
+
+       vty_out(vty, "Showing BFD monitored static routes:\n");
+       vty_out(vty, "\n  Next hops:\n");
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+               const struct static_vrf *svrf = vrf->info;
+               struct route_table *rt;
+
+               vty_out(vty, "    VRF %s IPv4 Unicast:\n", vrf->name);
+               rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+               if (rt)
+                       static_bfd_show_path(vty, rt);
+
+               vty_out(vty, "\n    VRF %s IPv4 Multicast:\n", vrf->name);
+               rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+               if (rt)
+                       static_bfd_show_path(vty, rt);
+
+               vty_out(vty, "\n    VRF %s IPv6 Unicast:\n", vrf->name);
+               rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+               if (rt)
+                       static_bfd_show_path(vty, rt);
+       }
+
+       vty_out(vty, "\n");
+}
index 45f845b40b2022305743291ae6dbcae46b04ed61..847e7d61a4a9319e2e6c4b90e72bee287f3f0d46 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "lib/command.h"
 #include "lib/debug.h"
+#include "lib/bfd.h"
 
 #include "static_debug.h"
 
 /* clang-format off */
 struct debug static_dbg_events = {0, "Staticd events"};
 struct debug static_dbg_route = {0, "Staticd route"};
+struct debug static_dbg_bfd = {0, "Staticd bfd"};
 
 struct debug *static_debug_arr[] =  {
        &static_dbg_events,
-       &static_dbg_route
+       &static_dbg_route,
+       &static_dbg_bfd
 };
 
 const char *static_debugs_conflines[] = {
        "debug static events",
-       "debug static route"
+       "debug static route",
+       "debug static bfd"
 };
 /* clang-format on */
 
@@ -105,7 +109,8 @@ int static_debug_status_write(struct vty *vty)
  *    Debug general internal events
  *
  */
-void static_debug_set(int vtynode, bool onoff, bool events, bool route)
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+                     bool bfd)
 {
        uint32_t mode = DEBUG_NODE2MODE(vtynode);
 
@@ -113,6 +118,10 @@ void static_debug_set(int vtynode, bool onoff, bool events, bool route)
                DEBUG_MODE_SET(&static_dbg_events, mode, onoff);
        if (route)
                DEBUG_MODE_SET(&static_dbg_route, mode, onoff);
+       if (bfd) {
+               DEBUG_MODE_SET(&static_dbg_bfd, mode, onoff);
+               bfd_protocol_integration_set_debug(onoff);
+       }
 }
 
 /*
index ee9f7b053737ab82e165bbf5453f2fdd906848c2..129c09668819f0c9b1437cd7ecb8aa48abae0ac0 100644 (file)
@@ -34,6 +34,7 @@ extern "C" {
 /* staticd debugging records */
 extern struct debug static_dbg_events;
 extern struct debug static_dbg_route;
+extern struct debug static_dbg_bfd;
 
 /*
  * Initialize staticd debugging.
@@ -71,7 +72,8 @@ int static_debug_status_write(struct vty *vty);
  *    Debug general internal events
  *
  */
-void static_debug_set(int vtynode, bool onoff, bool events, bool route);
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+                     bool bfd);
 
 #ifdef __cplusplus
 }
index 5935364d5a61b35c3adcf9a83a97c6930433bcfe..68d9ba97b46cd94384d2c8c37f5b085542828cb9 100644 (file)
@@ -116,6 +116,33 @@ const struct frr_yang_module_info frr_staticd_info = {
                                .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy,
                        }
                },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring",
+                       .cbs = {
+                               .create = route_next_hop_bfd_create,
+                               .destroy = route_next_hop_bfd_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source",
+                       .cbs = {
+                               .modify = route_next_hop_bfd_source_modify,
+                               .destroy = route_next_hop_bfd_source_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop",
+                       .cbs = {
+                               .modify = route_next_hop_bfd_multi_hop_modify,
+                       }
+               },
+               {
+                       .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile",
+                       .cbs = {
+                               .modify = route_next_hop_bfd_profile_modify,
+                               .destroy = route_next_hop_bfd_profile_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list",
                        .cbs = {
index 5c3030fcfa5d4bd848971991dff6293121cdc63c..96c9f8d9b7098ea6404f0ad1b1d1c477b4abc970 100644 (file)
@@ -63,6 +63,13 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
        struct nb_cb_modify_args *args);
 int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
        struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_create(struct nb_cb_create_args *args);
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args);
 int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
        struct nb_cb_create_args *args);
 int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
index 4a3d9e17a4433fcbfb72152244353b4f724330f7..cbb5b8234fc27ed01972f6b8dc7c38d215301512 100644 (file)
@@ -748,6 +748,113 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
        return NB_OK;
 }
 
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring
+ */
+int route_next_hop_bfd_create(struct nb_cb_create_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_monitor_enable(sn, args->dnode);
+       return NB_OK;
+}
+
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_monitor_disable(sn);
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source
+ */
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args)
+{
+       struct static_nexthop *sn;
+       struct ipaddr source;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       yang_dnode_get_ip(&source, args->dnode, NULL);
+       static_next_hop_bfd_source(sn, &source);
+       return NB_OK;
+}
+
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_auto_source(sn);
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop
+ */
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_multi_hop(sn,
+                                     yang_dnode_get_bool(args->dnode, NULL));
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile
+ */
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_profile(sn,
+                                   yang_dnode_get_string(args->dnode, NULL));
+
+       return NB_OK;
+}
+
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+       struct static_nexthop *sn;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       sn = nb_running_get_entry(args->dnode, NULL, true);
+       static_next_hop_bfd_profile(sn, NULL);
+
+       return NB_OK;
+}
+
 /*
  * XPath:
  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list
index ed4cdc51ce717e8bbf93e71a6858abbf3a36708a..ccbb98bd117c32ac2e1504882de7357335b73055 100644 (file)
@@ -276,6 +276,8 @@ struct static_nexthop *static_add_nexthop(struct static_path *pn,
        /* Make new static route structure. */
        nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop));
 
+       /* Copy back pointers. */
+       nh->rn = rn;
        nh->pn = pn;
 
        nh->type = type;
@@ -393,6 +395,8 @@ void static_delete_nexthop(struct static_nexthop *nh)
        struct route_node *rn = pn->rn;
 
        static_nexthop_list_del(&(pn->nexthop_list), nh);
+       /* Remove BFD session/configuration if any. */
+       bfd_sess_free(&nh->bsp);
 
        if (nh->nh_vrf_id == VRF_UNKNOWN)
                goto EXIT;
@@ -432,6 +436,8 @@ static void static_ifindex_update_nh(struct interface *ifp, bool up,
                nh->ifindex = IFINDEX_INTERNAL;
        }
 
+       /* Remove previously configured route if any. */
+       static_uninstall_path(pn);
        static_install_path(pn);
 }
 
index 71c3689be54ad3eb60e6d41b64a7350cda18721e..2332cfd2bf819fb3209ab7cf0967d29ab3e30960 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef __STATIC_ROUTES_H__
 #define __STATIC_ROUTES_H__
 
+#include "lib/bfd.h"
 #include "lib/mpls.h"
 #include "table.h"
 #include "memory.h"
@@ -30,6 +31,8 @@ extern "C" {
 
 DECLARE_MGROUP(STATIC);
 
+#include "staticd/static_vrf.h"
+
 /* Static route label information */
 struct static_nh_label {
        uint8_t num_labels;
@@ -148,6 +151,13 @@ struct static_nexthop {
 
        /* SR-TE color */
        uint32_t color;
+
+       /** BFD integration data. */
+       struct bfd_session_params *bsp;
+       /** Back pointer for route node. */
+       struct route_node *rn;
+       /** Path connection status. */
+       bool path_down;
 };
 
 DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list);
@@ -218,6 +228,24 @@ extern void zebra_stable_node_cleanup(struct route_table *table,
 extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
                              size_t size);
 
+/*
+ * BFD integration.
+ */
+extern void static_next_hop_bfd_source(struct static_nexthop *sn,
+                                      const struct ipaddr *source);
+extern void static_next_hop_bfd_auto_source(struct static_nexthop *sn);
+extern void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+                                              const struct lyd_node *dnode);
+extern void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn);
+extern void static_next_hop_bfd_profile(struct static_nexthop *sn,
+                                       const char *name);
+extern void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop);
+
+/** Call this function after zebra client initialization. */
+extern void static_bfd_initialize(struct zclient *zc, struct thread_master *tm);
+
+extern void static_bfd_show(struct vty *vty, bool isjson);
+
 #ifdef __cplusplus
 }
 #endif
index efae3c53da6b0befc29ede1ff930121fdbbb53aa..ff79622038d26c64cbf3bf2471444947c1d98407 100644 (file)
@@ -65,6 +65,11 @@ struct static_route_args {
        const char *label;
        const char *table;
        const char *color;
+
+       bool bfd;
+       bool bfd_multi_hop;
+       const char *bfd_source;
+       const char *bfd_profile;
 };
 
 static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
@@ -138,6 +143,11 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
        apply_mask(&p);
        prefix2str(&p, buf_prefix, sizeof(buf_prefix));
 
+       if (args->bfd && args->gateway == NULL) {
+               vty_out(vty, "%% Route monitoring requires a gateway\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        if (args->source)
                prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));
        if (args->gateway)
@@ -332,6 +342,41 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
                        nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
                                              NULL);
                }
+
+               if (args->bfd) {
+                       char xpath_bfd[XPATH_MAXLEN];
+
+                       if (args->bfd_source) {
+                               strlcpy(xpath_bfd, xpath_nexthop,
+                                       sizeof(xpath_bfd));
+                               strlcat(xpath_bfd,
+                                       "/frr-staticd:bfd-monitoring/source",
+                                       sizeof(xpath_bfd));
+                               nb_cli_enqueue_change(vty, xpath_bfd,
+                                                     NB_OP_MODIFY,
+                                                     args->bfd_source);
+                       }
+
+                       strlcpy(xpath_bfd, xpath_nexthop, sizeof(xpath_bfd));
+                       strlcat(xpath_bfd,
+                               "/frr-staticd:bfd-monitoring/multi-hop",
+                               sizeof(xpath_bfd));
+                       nb_cli_enqueue_change(vty, xpath_bfd, NB_OP_MODIFY,
+                                             args->bfd_multi_hop ? "true"
+                                                                 : "false");
+
+                       if (args->bfd_profile) {
+                               strlcpy(xpath_bfd, xpath_nexthop,
+                                       sizeof(xpath_bfd));
+                               strlcat(xpath_bfd,
+                                       "/frr-staticd:bfd-monitoring/profile",
+                                       sizeof(xpath_bfd));
+                               nb_cli_enqueue_change(vty, xpath_bfd,
+                                                     NB_OP_MODIFY,
+                                                     args->bfd_profile);
+                       }
+               }
+
                ret = nb_cli_apply_changes(vty, xpath_prefix);
        } else {
                if (args->source)
@@ -375,14 +420,23 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
 /* Static unicast routes for multicast RPF lookup. */
 DEFPY_YANG (ip_mroute_dist,
        ip_mroute_dist_cmd,
-       "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [(1-255)$distance]",
+       "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [{"
+       "(1-255)$distance"
+       "|bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}]"
+       "}]",
        NO_STR
        IP_STR
        "Configure static unicast route into MRIB for multicast RPF lookup\n"
        "IP destination prefix (e.g. 10.0.0.0/8)\n"
        "Nexthop address\n"
        "Nexthop interface name\n"
-       "Distance\n")
+       "Distance\n"
+       BFD_INTEGRATION_STR
+       BFD_INTEGRATION_MULTI_HOP_STR
+       BFD_INTEGRATION_SOURCE_STR
+       BFD_INTEGRATION_SOURCEV4_STR
+       BFD_PROFILE_STR
+       BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -392,6 +446,10 @@ DEFPY_YANG (ip_mroute_dist,
                .gateway = gate_str,
                .interface_name = ifname,
                .distance = distance_str,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -506,6 +564,7 @@ DEFPY_YANG(ip_route_address_interface,
          |nexthop-vrf NAME                            \
          |onlink$onlink                               \
          |color (1-4294967295)                        \
+         |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -525,7 +584,13 @@ DEFPY_YANG(ip_route_address_interface,
       VRF_CMD_HELP_STR
       "Treat the nexthop as directly attached to the interface\n"
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -543,6 +608,10 @@ DEFPY_YANG(ip_route_address_interface,
                .onlink = !!onlink,
                .vrf = vrf,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -562,6 +631,7 @@ DEFPY_YANG(ip_route_address_interface_vrf,
          |nexthop-vrf NAME                            \
          |onlink$onlink                               \
          |color (1-4294967295)                        \
+         |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
          }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -580,7 +650,13 @@ DEFPY_YANG(ip_route_address_interface_vrf,
       VRF_CMD_HELP_STR
       "Treat the nexthop as directly attached to the interface\n"
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -598,6 +674,10 @@ DEFPY_YANG(ip_route_address_interface_vrf,
                .onlink = !!onlink,
                .xpath_vrf = true,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -616,6 +696,7 @@ DEFPY_YANG(ip_route,
          |table (1-4294967295)                        \
          |nexthop-vrf NAME                            \
          |color (1-4294967295)                        \
+         |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -634,7 +715,13 @@ DEFPY_YANG(ip_route,
       "The table number to configure\n"
       VRF_CMD_HELP_STR
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -651,6 +738,10 @@ DEFPY_YANG(ip_route,
                .color = color_str,
                .vrf = vrf,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -668,6 +759,7 @@ DEFPY_YANG(ip_route_vrf,
          |table (1-4294967295)                        \
          |nexthop-vrf NAME                            \
          |color (1-4294967295)                        \
+         |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -685,7 +777,13 @@ DEFPY_YANG(ip_route_vrf,
       "The table number to configure\n"
       VRF_CMD_HELP_STR
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -702,6 +800,10 @@ DEFPY_YANG(ip_route_vrf,
                .color = color_str,
                .xpath_vrf = true,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -814,6 +916,7 @@ DEFPY_YANG(ipv6_route_address_interface,
             |nexthop-vrf NAME                              \
            |onlink$onlink                                 \
            |color (1-4294967295)                          \
+           |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR
       IPV6_STR
@@ -834,7 +937,13 @@ DEFPY_YANG(ipv6_route_address_interface,
       VRF_CMD_HELP_STR
       "Treat the nexthop as directly attached to the interface\n"
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -852,6 +961,10 @@ DEFPY_YANG(ipv6_route_address_interface,
                .onlink = !!onlink,
                .vrf = vrf,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -870,6 +983,7 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
             |nexthop-vrf NAME                              \
            |onlink$onlink                                 \
            |color (1-4294967295)                          \
+           |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR
       IPV6_STR
@@ -889,7 +1003,13 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
       VRF_CMD_HELP_STR
       "Treat the nexthop as directly attached to the interface\n"
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -907,6 +1027,10 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
                .onlink = !!onlink,
                .xpath_vrf = true,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -924,6 +1048,7 @@ DEFPY_YANG(ipv6_route,
            |table (1-4294967295)                          \
             |nexthop-vrf NAME                              \
             |color (1-4294967295)                          \
+           |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR
       IPV6_STR
@@ -943,7 +1068,13 @@ DEFPY_YANG(ipv6_route,
       "The table number to configure\n"
       VRF_CMD_HELP_STR
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -960,6 +1091,10 @@ DEFPY_YANG(ipv6_route,
                .color = color_str,
                .vrf = vrf,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -976,6 +1111,7 @@ DEFPY_YANG(ipv6_route_vrf,
            |table (1-4294967295)                          \
             |nexthop-vrf NAME                              \
            |color (1-4294967295)                          \
+           |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
           }]",
       NO_STR
       IPV6_STR
@@ -994,7 +1130,13 @@ DEFPY_YANG(ipv6_route_vrf,
       "The table number to configure\n"
       VRF_CMD_HELP_STR
       "SR-TE color\n"
-      "The SR-TE color to configure\n")
+      "The SR-TE color to configure\n"
+      BFD_INTEGRATION_STR
+      BFD_INTEGRATION_MULTI_HOP_STR
+      BFD_INTEGRATION_SOURCE_STR
+      BFD_INTEGRATION_SOURCEV4_STR
+      BFD_PROFILE_STR
+      BFD_PROFILE_NAME_STR)
 {
        struct static_route_args args = {
                .delete = !!no,
@@ -1011,6 +1153,10 @@ DEFPY_YANG(ipv6_route_vrf,
                .color = color_str,
                .xpath_vrf = true,
                .nexthop_vrf = nexthop_vrf,
+               .bfd = !!bfd,
+               .bfd_multi_hop = !!bfd_multi_hop,
+               .bfd_source = bfd_source_str,
+               .bfd_profile = bfd_profile,
        };
 
        return static_route_nb_run(vty, &args);
@@ -1165,6 +1311,25 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
                vty_out(vty, " color %s",
                        yang_dnode_get_string(nexthop, "./srte-color"));
 
+       if (yang_dnode_exists(nexthop, "./bfd-monitoring")) {
+               const struct lyd_node *bfd_dnode =
+                       yang_dnode_get(nexthop, "./bfd-monitoring");
+
+               if (yang_dnode_get_bool(bfd_dnode, "./multi-hop")) {
+                       vty_out(vty, " bfd multi-hop");
+
+                       if (yang_dnode_exists(bfd_dnode, "./source"))
+                               vty_out(vty, " source %s",
+                                       yang_dnode_get_string(bfd_dnode,
+                                                             "./source"));
+               } else
+                       vty_out(vty, " bfd");
+
+               if (yang_dnode_exists(bfd_dnode, "./profile"))
+                       vty_out(vty, " profile %s",
+                               yang_dnode_get_string(bfd_dnode, "./profile"));
+       }
+
        vty_out(vty, "\n");
 }
 
@@ -1292,20 +1457,33 @@ int static_path_list_cli_cmp(const struct lyd_node *dnode1,
 }
 
 DEFPY_YANG(debug_staticd, debug_staticd_cmd,
-          "[no] debug static [{events$events|route$route}]",
+          "[no] debug static [{events$events|route$route|bfd$bfd}]",
           NO_STR DEBUG_STR STATICD_STR
           "Debug events\n"
-          "Debug route\n")
+          "Debug route\n"
+          "Debug bfd\n")
 {
        /* If no specific category, change all */
        if (strmatch(argv[argc - 1]->text, "static"))
-               static_debug_set(vty->node, !no, true, true);
+               static_debug_set(vty->node, !no, true, true, true);
        else
-               static_debug_set(vty->node, !no, !!events, !!route);
+               static_debug_set(vty->node, !no, !!events, !!route, !!bfd);
 
        return CMD_SUCCESS;
 }
 
+DEFPY(staticd_show_bfd_routes, staticd_show_bfd_routes_cmd,
+      "show bfd static route [json]$isjson",
+      SHOW_STR
+      BFD_INTEGRATION_STR
+      STATICD_STR
+      ROUTE_STR
+      JSON_STR)
+{
+       static_bfd_show(vty, !!isjson);
+       return CMD_SUCCESS;
+}
+
 DEFUN_NOSH (show_debugging_static,
            show_debugging_static_cmd,
            "show debugging [static]",
@@ -1352,4 +1530,6 @@ void static_vty_init(void)
        install_element(ENABLE_NODE, &show_debugging_static_cmd);
        install_element(ENABLE_NODE, &debug_staticd_cmd);
        install_element(CONFIG_NODE, &debug_staticd_cmd);
+
+       install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd);
 }
index cb36304473f65903d659309a15683ed671d789e2..316247adb3118d023d73b8ce9db049952c4b774b 100644 (file)
@@ -441,6 +441,9 @@ extern void static_zebra_route_add(struct static_path *pn, bool install)
                api_nh = &api.nexthops[nh_num];
                if (nh->nh_vrf_id == VRF_UNKNOWN)
                        continue;
+               /* Skip next hop which peer is down. */
+               if (nh->path_down)
+                       continue;
 
                api_nh->vrf_id = nh->nh_vrf_id;
                if (nh->onlink)
@@ -545,6 +548,7 @@ void static_zebra_init(void)
        zclient->zebra_connected = zebra_connected;
 
        static_nht_hash_init(static_nht_hash);
+       static_bfd_initialize(zclient, master);
 }
 
 /* static_zebra_stop used by tests/lib/test_grpc.cpp */
index bb0fc95bc202b798da7b6ae5eaddd84fe8afaf48..022428281f60dbe5fc884fcbbb4e4279cdf41427 100644 (file)
@@ -10,6 +10,7 @@ man8 += $(MANBUILD)/frr-staticd.8
 endif
 
 staticd_libstatic_a_SOURCES = \
+       staticd/static_bfd.c \
        staticd/static_debug.c \
        staticd/static_nht.c \
        staticd/static_routes.c \
@@ -38,5 +39,6 @@ staticd_staticd_SOURCES = staticd/static_main.c
 staticd_staticd_LDADD = staticd/libstatic.a lib/libfrr.la $(LIBCAP)
 
 nodist_staticd_staticd_SOURCES = \
+       yang/frr-bfdd.yang.c \
        yang/frr-staticd.yang.c \
        # end
diff --git a/tests/topotests/bfd_topo3/r3/bfd-static-down.json b/tests/topotests/bfd_topo3/r3/bfd-static-down.json
new file mode 100644 (file)
index 0000000..60752d3
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "path-list": {
+        "ipv4-multicast": [],
+        "ipv4-unicast": [],
+        "ipv6-unicast": [
+            {
+                "prefix": "2001:db8:5::\/64",
+                "vrf": "default",
+                "installed": false
+            }
+        ]
+    }}
diff --git a/tests/topotests/bfd_topo3/r3/bfd-static.json b/tests/topotests/bfd_topo3/r3/bfd-static.json
new file mode 100644 (file)
index 0000000..c65060c
--- /dev/null
@@ -0,0 +1,13 @@
+{
+    "path-list": {
+        "ipv4-multicast": [],
+        "ipv4-unicast": [],
+        "ipv6-unicast": [
+            {
+                "prefix": "2001:db8:5::\/64",
+                "vrf": "default",
+                "installed": true
+            }
+        ]
+    }
+}
diff --git a/tests/topotests/bfd_topo3/r3/staticd.conf b/tests/topotests/bfd_topo3/r3/staticd.conf
new file mode 100644 (file)
index 0000000..44f91f3
--- /dev/null
@@ -0,0 +1 @@
+ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx
index 2f41f25c58d83dbc94c65c3c715e7dd1011d0d96..4f71d753896eae40587ad0bd666a84355ebc71c3 100644 (file)
@@ -19,7 +19,8 @@
         "remote-transmit-interval": 2000,
         "status": "up",
         "uptime": "*",
-        "transmit-interval": 2000
+        "transmit-interval": 2000,
+        "vrf": "default"
     },
     {
         "detect-multiplier": 3,
         "remote-transmit-interval": 2000,
         "status": "up",
         "uptime": "*",
-        "transmit-interval": 2000
+        "transmit-interval": 2000,
+        "vrf": "default"
+    },
+    {
+        "detect-multiplier": 3,
+        "diagnostic": "ok",
+        "echo-receive-interval": 50,
+        "echo-transmit-interval": 0,
+        "id": "*",
+        "multihop": false,
+        "passive-mode": false,
+        "peer": "192.168.4.3",
+        "receive-interval": 2000,
+        "remote-detect-multiplier": 3,
+        "remote-diagnostic": "ok",
+        "remote-echo-receive-interval": 50,
+        "remote-id": "*",
+        "remote-receive-interval": 2000,
+        "remote-transmit-interval": 2000,
+        "status": "up",
+        "transmit-interval": 2000,
+        "uptime": "*",
+        "vrf": "default"
+    },
+    {
+        "detect-multiplier": 3,
+        "diagnostic": "ok",
+        "echo-receive-interval": 50,
+        "echo-transmit-interval": 0,
+        "id": "*",
+        "multihop": false,
+        "passive-mode": false,
+        "peer": "192.168.4.2",
+        "receive-interval": 2000,
+        "remote-detect-multiplier": 3,
+        "remote-diagnostic": "ok",
+        "remote-echo-receive-interval": 50,
+        "remote-id": "*",
+        "remote-receive-interval": 2000,
+        "remote-transmit-interval": 2000,
+        "status": "up",
+        "transmit-interval": 2000,
+        "uptime": "*",
+        "vrf": "default"
     }
 ]
index 0aab6e3017a694eac407ff8f2a7b955c3c0a8010..bfad78a7adf31986d638d899d08cf3f6f1e5376f 100644 (file)
@@ -9,6 +9,7 @@ router bgp 400
  neighbor 2001:db8:1::1 bfd profile slow-tx-mh
  address-family ipv4 unicast
   redistribute connected
+  redistribute static
  exit-address-family
  address-family ipv6 unicast
   redistribute connected
diff --git a/tests/topotests/bfd_topo3/r4/staticd.conf b/tests/topotests/bfd_topo3/r4/staticd.conf
new file mode 100644 (file)
index 0000000..3b1c5bf
--- /dev/null
@@ -0,0 +1,2 @@
+ip route 10.254.254.5/32 192.168.4.2 bfd profile slow-tx
+ip route 10.254.254.6/32 192.168.4.3 bfd profile slow-tx
index bf0cfcf42c7626c88d874269fef70407866835fc..2574941724a06d6f860f7f8697d17963b7fc31cf 100644 (file)
@@ -8,3 +8,7 @@ interface r4-eth0
  ip address 192.168.3.1/24
  ipv6 address 2001:db8:3::1/64
 !
+interface r4-eth1
+ ip address 192.168.4.1/24
+ ipv6 address 2001:db8:4::1/64
+!
diff --git a/tests/topotests/bfd_topo3/r5/bfd-peers.json b/tests/topotests/bfd_topo3/r5/bfd-peers.json
new file mode 100644 (file)
index 0000000..777b1dd
--- /dev/null
@@ -0,0 +1,23 @@
+[
+    {
+        "detect-multiplier": 3,
+        "diagnostic": "ok",
+        "echo-receive-interval": 50,
+        "echo-transmit-interval": 0,
+        "id": "*",
+        "multihop": false,
+        "passive-mode": false,
+        "peer": "192.168.4.1",
+        "receive-interval": 2000,
+        "remote-detect-multiplier": 3,
+        "remote-diagnostic": "ok",
+        "remote-echo-receive-interval": 50,
+        "remote-id": "*",
+        "remote-receive-interval": 2000,
+        "remote-transmit-interval": 2000,
+        "status": "up",
+        "transmit-interval": 2000,
+        "uptime": "*",
+        "vrf": "default"
+    }
+]
diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf
new file mode 100644 (file)
index 0000000..6d4483a
--- /dev/null
@@ -0,0 +1,11 @@
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+  receive-interval 2000
+  transmit-interval 2000
+  minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r5/staticd.conf b/tests/topotests/bfd_topo3/r5/staticd.conf
new file mode 100644 (file)
index 0000000..9828cff
--- /dev/null
@@ -0,0 +1,2 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r5/zebra.conf b/tests/topotests/bfd_topo3/r5/zebra.conf
new file mode 100644 (file)
index 0000000..f84ce7e
--- /dev/null
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.5/32
+!
+interface r5-eth0
+ ip address 192.168.4.2/24
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd_topo3/r6/bfd-peers.json b/tests/topotests/bfd_topo3/r6/bfd-peers.json
new file mode 100644 (file)
index 0000000..4de451d
--- /dev/null
@@ -0,0 +1,46 @@
+[
+    {
+        "detect-multiplier": 3,
+        "diagnostic": "ok",
+        "echo-receive-interval": 50,
+        "echo-transmit-interval": 0,
+        "id": "*",
+        "multihop": false,
+        "passive-mode": false,
+        "peer": "192.168.4.1",
+        "receive-interval": 2000,
+        "remote-detect-multiplier": 3,
+        "remote-diagnostic": "ok",
+        "remote-echo-receive-interval": 50,
+        "remote-id": "*",
+        "remote-receive-interval": 2000,
+        "remote-transmit-interval": 2000,
+        "status": "up",
+        "transmit-interval": 2000,
+        "uptime": "*",
+        "vrf": "default"
+    },
+    {
+        "detect-multiplier": 3,
+        "diagnostic": "ok",
+        "echo-receive-interval": 50,
+        "echo-transmit-interval": 0,
+        "id": "*",
+        "local": "2001:db8:4::3",
+        "minimum-ttl": 2,
+        "multihop": true,
+        "passive-mode": false,
+        "peer": "2001:db8:3::2",
+        "receive-interval": 2000,
+        "remote-detect-multiplier": 3,
+        "remote-diagnostic": "ok",
+        "remote-echo-receive-interval": 50,
+        "remote-id": "*",
+        "remote-receive-interval": 2000,
+        "remote-transmit-interval": 2000,
+        "status": "up",
+        "transmit-interval": 2000,
+        "uptime": "*",
+        "vrf": "default"
+    }
+]
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static-down.json b/tests/topotests/bfd_topo3/r6/bfd-static-down.json
new file mode 100644 (file)
index 0000000..4dadff2
--- /dev/null
@@ -0,0 +1,19 @@
+{
+    "path-list": {
+        "ipv4-multicast": [],
+        "ipv4-unicast": [
+            {
+                "installed": true,
+                "prefix": "10.254.254.4/32",
+                "vrf": "default"
+            }
+        ],
+        "ipv6-unicast": [
+            {
+                "prefix": "2001:db8:1::\/64",
+                "vrf": "default",
+                "installed": false
+            }
+        ]
+    }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static.json b/tests/topotests/bfd_topo3/r6/bfd-static.json
new file mode 100644 (file)
index 0000000..d042889
--- /dev/null
@@ -0,0 +1,19 @@
+{
+    "path-list": {
+        "ipv4-multicast": [],
+        "ipv4-unicast": [
+            {
+                "installed": true,
+                "prefix": "10.254.254.4/32",
+                "vrf": "default"
+            }
+        ],
+        "ipv6-unicast": [
+            {
+                "prefix": "2001:db8:1::\/64",
+                "vrf": "default",
+                "installed": true
+            }
+        ]
+    }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf
new file mode 100644 (file)
index 0000000..6d4483a
--- /dev/null
@@ -0,0 +1,11 @@
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+  receive-interval 2000
+  transmit-interval 2000
+  minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r6/staticd.conf b/tests/topotests/bfd_topo3/r6/staticd.conf
new file mode 100644 (file)
index 0000000..28da508
--- /dev/null
@@ -0,0 +1,5 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
+!
+ipv6 route 2001:db8:3::/64 2001:db8:4::1
+ipv6 route 2001:db8:1::/64 2001:db8:3::2 bfd multi-hop source 2001:db8:4::3 profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r6/zebra.conf b/tests/topotests/bfd_topo3/r6/zebra.conf
new file mode 100644 (file)
index 0000000..b8f2ea4
--- /dev/null
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.6/32
+!
+interface r6-eth0
+ ip address 192.168.4.3/24
+ ipv6 address 2001:db8:4::3/64
+!
index 502cea11f2a2f72b42d9f46e5999e639d2092118..8d18783d545cae44412ae066181e5c6d2536a77a 100644 (file)
@@ -40,6 +40,18 @@ graph template {
     fillcolor="#f08080",
     style=filled,
   ];
+  r5 [
+    shape=doubleoctagon
+    label="r5",
+    fillcolor="#f08080",
+    style=filled,
+  ];
+  r6 [
+    shape=doubleoctagon
+    label="r6",
+    fillcolor="#f08080",
+    style=filled,
+  ];
 
   # Switches
   sw1 [
@@ -60,6 +72,12 @@ graph template {
     fillcolor="#d0e0d0",
     style=filled,
   ];
+  sw4 [
+    shape=oval,
+    label="sw4\n192.168.4.0/24\n2001:db8:4::/64",
+    fillcolor="#d0e0d0",
+    style=filled,
+  ];
 
   # Connections
   r1 -- sw1 [label="eth0\n.1"];
@@ -70,4 +88,8 @@ graph template {
 
   r4 -- sw3 [label="eth0\n.1"];
   r3 -- sw3 [label="eth2\n.2"];
+
+  r4 -- sw4 [label="eth1\n.1"];
+  r5 -- sw4 [label="eth0\n.2"];
+  r6 -- sw4 [label="eth0\n.3"];
 }
index 6b532560bf98c04cdfba54b8645f270b7aca1aba..f100eae712481f072e082643680d5ec1e4eb02ef 100644 (file)
Binary files a/tests/topotests/bfd_topo3/test_bfd_topo3.jpg and b/tests/topotests/bfd_topo3/test_bfd_topo3.jpg differ
index 978593e41ac130aaa604b17be467bc3ad2962537..ea9f981d9b65256f97821100ad4585af7ae39fb3 100644 (file)
@@ -51,6 +51,7 @@ def setup_module(mod):
         "s1": ("r1", "r2"),
         "s2": ("r2", "r3"),
         "s3": ("r3", "r4"),
+        "s4": ("r4", "r5", "r6"),
     }
     tgen = Topogen(topodef, mod.__name__)
     tgen.start_topology()
@@ -69,6 +70,10 @@ def setup_module(mod):
         if os.path.isfile(daemon_file):
             router.load_config(TopoRouter.RD_BGP, daemon_file)
 
+        daemon_file = "{}/{}/staticd.conf".format(CWD, rname)
+        if os.path.isfile(daemon_file):
+            router.load_config(TopoRouter.RD_STATIC, daemon_file)
+
     # Initialize all routers.
     tgen.start_router()
 
@@ -100,6 +105,10 @@ def test_wait_bgp_convergence():
     expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp")
     # Wait for R1 <-> R4 convergence.
     expect_loopback_route("r1", "ip", "10.254.254.4/32", "bgp")
+    # Wait for R1 <-> R5 convergence.
+    expect_loopback_route("r1", "ip", "10.254.254.5/32", "bgp")
+    # Wait for R1 <-> R6 convergence.
+    expect_loopback_route("r1", "ip", "10.254.254.6/32", "bgp")
 
     # Wait for R2 <-> R1 convergence.
     expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
@@ -107,6 +116,10 @@ def test_wait_bgp_convergence():
     expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
     # Wait for R2 <-> R4 convergence.
     expect_loopback_route("r2", "ip", "10.254.254.4/32", "bgp")
+    # Wait for R2 <-> R5 convergence.
+    expect_loopback_route("r2", "ip", "10.254.254.5/32", "bgp")
+    # Wait for R2 <-> R6 convergence.
+    expect_loopback_route("r2", "ip", "10.254.254.6/32", "bgp")
 
     # Wait for R3 <-> R1 convergence.
     expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp")
@@ -114,6 +127,10 @@ def test_wait_bgp_convergence():
     expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp")
     # Wait for R3 <-> R4 convergence.
     expect_loopback_route("r3", "ip", "10.254.254.4/32", "bgp")
+    # Wait for R3 <-> R5 convergence.
+    expect_loopback_route("r3", "ip", "10.254.254.5/32", "bgp")
+    # Wait for R3 <-> R6 convergence.
+    expect_loopback_route("r3", "ip", "10.254.254.6/32", "bgp")
 
     # Wait for R4 <-> R1 convergence.
     expect_loopback_route("r4", "ip", "10.254.254.1/32", "bgp")
@@ -121,6 +138,15 @@ def test_wait_bgp_convergence():
     expect_loopback_route("r4", "ip", "10.254.254.2/32", "bgp")
     # Wait for R4 <-> R3 convergence.
     expect_loopback_route("r4", "ip", "10.254.254.3/32", "bgp")
+    # Wait for R4 <-> R5 convergence.
+    expect_loopback_route("r4", "ip", "10.254.254.5/32", "static")
+    # Wait for R4 <-> R6 convergence.
+    expect_loopback_route("r4", "ip", "10.254.254.6/32", "static")
+
+    # Wait for R5 <-> R6 convergence.
+    expect_loopback_route("r3", "ipv6", "2001:db8:5::/64", "static")
+    # Wait for R6 <-> R5 convergence.
+    expect_loopback_route("r6", "ipv6", "2001:db8:1::/64", "static")
 
 
 def test_wait_bfd_convergence():
@@ -149,6 +175,70 @@ def test_wait_bfd_convergence():
     expect_bfd_configuration("r2")
     expect_bfd_configuration("r3")
     expect_bfd_configuration("r4")
+    expect_bfd_configuration("r5")
+    expect_bfd_configuration("r6")
+
+
+def test_static_route_monitoring():
+    "Test static route monitoring output."
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("test BFD static route status")
+
+    def expect_static_bfd_output(router, filename):
+        "Load JSON file and compare with 'show bfd peer json'"
+        logger.info("waiting BFD configuration on router {}".format(router))
+        bfd_config = json.loads(
+            open("{}/{}/{}.json".format(CWD, router, filename)).read()
+        )
+        test_func = partial(
+            topotest.router_json_cmp,
+            tgen.gears[router],
+            "show bfd static route json",
+            bfd_config,
+        )
+        _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+        assertmsg = '"{}" BFD static route status failure'.format(router)
+        assert result is None, assertmsg
+
+    expect_static_bfd_output("r3", "bfd-static")
+    expect_static_bfd_output("r6", "bfd-static")
+
+    logger.info("Setting r4 link down ...")
+
+    tgen.gears["r4"].link_enable("r4-eth0", False)
+
+    expect_static_bfd_output("r3", "bfd-static-down")
+    expect_static_bfd_output("r6", "bfd-static-down")
+
+
+def test_expect_static_rib_removal():
+    "Test that route got removed from RIB (staticd and bgpd)."
+
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    def expect_route_missing(router, iptype, route):
+        "Wait until route is present on RIB for protocol."
+        logger.info("waiting route {} to disapear in {}".format(route, router))
+        test_func = partial(
+            topotest.router_json_cmp,
+            tgen.gears[router],
+            "show {} route json".format(iptype),
+            {route: None},
+        )
+        rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+        assertmsg = '"{}" convergence failure'.format(router)
+        assert result is None, assertmsg
+
+    expect_route_missing("r1", "ip", "10.254.254.5/32")
+    expect_route_missing("r2", "ip", "10.254.254.5/32")
+    expect_route_missing("r3", "ip", "10.254.254.5/32")
+    expect_route_missing("r3", "ipv6", "2001:db8:5::/64")
+    expect_route_missing("r6", "ipv6", "2001:db8:1::/64")
 
 
 def teardown_module(_mod):
diff --git a/tests/topotests/bgp_path_attribute_discard/__init__.py b/tests/topotests/bgp_path_attribute_discard/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_path_attribute_discard/exabgp.env b/tests/topotests/bgp_path_attribute_discard/exabgp.env
new file mode 100644 (file)
index 0000000..28e6423
--- /dev/null
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
new file mode 100644 (file)
index 0000000..7fb9210
--- /dev/null
@@ -0,0 +1,24 @@
+neighbor 10.0.0.1 {
+  router-id 10.0.0.2;
+  local-address 10.0.0.2;
+  local-as 65001;
+  peer-as 65002;
+
+  capability {
+    route-refresh;
+  }
+
+  static {
+    route 192.168.100.101/32 {
+      atomic-aggregate;
+      community 65001:101;
+      next-hop 10.0.0.2;
+    }
+
+    route 192.168.100.102/32 {
+      originator-id 10.0.0.2;
+      community 65001:102;
+      next-hop 10.0.0.2;
+    }
+  }
+}
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..c96f354
--- /dev/null
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
new file mode 100644 (file)
index 0000000..51a1b26
--- /dev/null
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
new file mode 100644 (file)
index 0000000..4badf64
--- /dev/null
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if `neighbor path-attribute discard` command works correctly,
+can discard unwanted attributes from UPDATE messages, and ignore them
+by continuing to process UPDATE messages.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+    r1 = tgen.add_router("r1")
+    peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+
+    switch = tgen.add_switch("s1")
+    switch.add_link(r1)
+    switch.add_link(peer1)
+
+
+def setup_module(mod):
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+
+    router = tgen.gears["r1"]
+    router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+    router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+    router.start()
+
+    peer = tgen.gears["peer1"]
+    peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_bgp_path_attribute_discard():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    r1 = tgen.gears["r1"]
+
+    def _bgp_converge():
+        output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
+        expected = {
+            "routes": {
+                "192.168.100.101/32": [
+                    {
+                        "valid": True,
+                        "atomicAggregate": True,
+                        "community": {
+                            "string": "65001:101",
+                        },
+                    }
+                ],
+                "192.168.100.102/32": [
+                    {
+                        "valid": True,
+                        "originatorId": "10.0.0.2",
+                        "community": {
+                            "string": "65001:102",
+                        },
+                    }
+                ],
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert result is None, "Failed bgp convergence"
+
+    step("Discard atomic-aggregate, community, and originator-id attributes from peer1")
+    r1.vtysh_cmd(
+        """
+    configure terminal
+        router bgp
+            neighbor 10.0.0.2 path-attribute discard 6 8 9
+    """
+    )
+
+    def _bgp_check_if_attributes_discarded():
+        output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
+        expected = {
+            "routes": {
+                "192.168.100.101/32": [
+                    {
+                        "valid": True,
+                        "atomicAggregate": None,
+                        "community": None,
+                    }
+                ],
+                "192.168.100.102/32": [
+                    {
+                        "valid": True,
+                        "originatorId": None,
+                        "community": None,
+                    }
+                ],
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_check_if_attributes_discarded)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert (
+        result is None
+    ), "Failed to discard path attributes (atomic-aggregate, community, and originator-id)"
+
+
+def test_memory_leak():
+    "Run the memory leak test and report results."
+    tgen = get_topogen()
+    if not tgen.is_memleak_enabled():
+        pytest.skip("Memory leak test/report is disabled")
+
+    tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 719d76392d33b8d94b4c90bd0a64c336b9cd68a7..409be747408809541992422902b4bea3ea1792ac 100644 (file)
@@ -1,14 +1,19 @@
 ! exit1
 router bgp 65001
-  no bgp ebgp-requires-policy
-  neighbor 192.168.255.1 remote-as 65002
-  neighbor 192.168.255.1 timers 3 10
-  address-family ipv4 unicast
-    neighbor 192.168.255.1 route-map prepend out
-    redistribute connected
-  exit-address-family
-  !
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+  neighbor 192.168.255.1 route-map prepend out
+  redistribute connected
+ exit-address-family
+ !
+!
+ip prefix-list p1 seq 5 permit 172.16.255.253/32
 !
 route-map prepend permit 10
-  set as-path prepend 65003
+ match ip address prefix-list p1
+ set as-path prepend 65003
+!
+route-map prepend permit 20
 !
index 9904bb4e16f40c28e4e101cc2e2cb4c74a093cbc..74489a057110d48d27ff7119d22bf195fec008d5 100644 (file)
@@ -1,5 +1,6 @@
 ! exit1
 interface lo
+ ip address 172.16.255.253/32
  ip address 172.16.255.254/32
 !
 interface r1-eth0
index a4a654d7b5c24f458a5dc2fa144df451f9417f93..dcb52a2e7dbcfaff7bba5373eaf1502263145864 100644 (file)
@@ -1,11 +1,10 @@
 ! spine
 router bgp 65002
-  no bgp ebgp-requires-policy
-  neighbor 192.168.255.2 remote-as 65001
-  neighbor 192.168.255.2 timers 3 10
-  neighbor 192.168.255.2 solo
-  neighbor 192.168.254.2 remote-as 65003
-  neighbor 192.168.254.2 timers 3 10
-  neighbor 192.168.254.2 solo
-  neighbor 192.168.254.2 sender-as-path-loop-detection
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 sender-as-path-loop-detection
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.254.2 timers 3 10
+ neighbor 192.168.254.2 sender-as-path-loop-detection
 !
index 2e24de0b2d89ca48fa02267a8fb25a911e0e86c9..519273d30d7ff79132c92efbe46ef21cba55b541 100644 (file)
@@ -1,6 +1,6 @@
 ! exit2
 router bgp 65003
 no bgp ebgp-requires-policy
 neighbor 192.168.254.1 remote-as 65002
 neighbor 192.168.254.1 timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.254.1 remote-as 65002
+ neighbor 192.168.254.1 timers 3 10
 !
index b5c33f359bfc538846a38e6a2805fc17a2fc2afb..ebeab05648e9670372b469b10f64825bd642701f 100644 (file)
@@ -85,20 +85,20 @@ def test_bgp_sender_as_path_loop_detection():
     if tgen.routers_have_failure():
         pytest.skip(tgen.errors)
 
-    router = tgen.gears["r2"]
+    r2 = tgen.gears["r2"]
 
-    def _bgp_converge(router):
-        output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+    def _bgp_converge():
+        output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
         expected = {
             "192.168.255.2": {
                 "bgpState": "Established",
-                "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+                "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
             }
         }
         return topotest.json_cmp(output, expected)
 
-    def _bgp_has_route_from_r1(router):
-        output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+    def _bgp_has_route_from_r1():
+        output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.253/32 json"))
         expected = {
             "paths": [
                 {
@@ -111,31 +111,35 @@ def test_bgp_sender_as_path_loop_detection():
         }
         return topotest.json_cmp(output, expected)
 
-    def _bgp_suppress_route_to_r3(router):
+    def _bgp_suppress_route_to_r1():
         output = json.loads(
-            router.vtysh_cmd(
-                "show ip bgp neighbor 192.168.254.2 advertised-routes json"
-            )
+            r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json")
         )
         expected = {"totalPrefixCounter": 0}
         return topotest.json_cmp(output, expected)
 
-    test_func = functools.partial(_bgp_converge, router)
-    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
-
-    assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+    def _bgp_suppress_route_to_r3():
+        output = json.loads(
+            r2.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json")
+        )
+        expected = {"totalPrefixCounter": 2}
+        return topotest.json_cmp(output, expected)
 
-    test_func = functools.partial(_bgp_has_route_from_r1, router)
-    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    test_func = functools.partial(_bgp_converge)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert result is None, "Failed bgp to convergence"
 
-    assert result is None, 'Failed to see a route from r1 in "{}"'.format(router)
+    test_func = functools.partial(_bgp_has_route_from_r1)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert result is None, "Failed to see a route from r1"
 
-    test_func = functools.partial(_bgp_suppress_route_to_r3, router)
-    success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    test_func = functools.partial(_bgp_suppress_route_to_r3)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert result is None, "Route 172.16.255.253/32 should not be sent to r3 from r2"
 
-    assert (
-        result is None
-    ), 'Route 172.16.255.254/32 should not be sent to r3 "{}"'.format(router)
+    test_func = functools.partial(_bgp_suppress_route_to_r1)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+    assert result is None, "Routes should not be sent to r1 from r2"
 
 
 if __name__ == "__main__":
index 856a2d0fa781528c1451b5398a5bc2973f897482..b9f324d56199bb2fdb0423d26f45a355c94b8297 100644 (file)
@@ -178,7 +178,7 @@ def route_install_helper(iter):
 
     # Table of defaults, used for timeout values and 'expected' objects
     scale_defaults = dict(
-        zip(scale_keys, [None, None, 7, 30, expected_installed, expected_removed])
+        zip(scale_keys, [None, None, 10, 50, expected_installed, expected_removed])
     )
 
     # List of params for each step in the test; note extra time given
index bf402e1bef7b69cc3449da7e804c900cc927f354..dfbc9b80083dbd49c3d47b47481769e5ea8a6725 100755 (executable)
@@ -1914,6 +1914,7 @@ if __name__ == "__main__":
         "bgpd",
         "fabricd",
         "isisd",
+        "babeld",
         "ospf6d",
         "ospfd",
         "pbrd",
@@ -1925,6 +1926,7 @@ if __name__ == "__main__":
         "staticd",
         "vrrpd",
         "ldpd",
+        "nhrpd",
         "pathd",
         "bfdd",
         "eigrpd",
index 08b607347983e671f8c34eb69f3b628a9b247d15..d3e756e0bfce0a78593ec37161cbd89bac7109af 100644 (file)
@@ -242,6 +242,36 @@ module frr-bfdd {
     }
   }
 
+  grouping bfd-monitoring {
+    description
+      "BFD monitoring template for protocol integration.";
+
+    leaf source {
+      type inet:ip-address;
+      description
+        "Source address to use for liveness check.
+
+         When source is not set and multi-hop is `false` the source
+         address will be `0.0.0.0` (any).
+
+         When source is not set and multi-hop is `true` the source
+         address will be automatic selected through Next Hop Tracking (NHT).";
+    }
+
+    leaf multi-hop {
+      description
+        "Use multi hop session instead of single hop.";
+      type boolean;
+      default false;
+    }
+
+    leaf profile {
+      description
+        "BFD pre configured profile.";
+      type frr-bfdd:profile-ref;
+    }
+  }
+
   grouping session-states {
     /*
      * Local settings.
index 98ff3a83c65b7bfa9714dc40ee94a678faa0dc70..cb5e25b877fa750ea0cbede4258396ad59ffbb14 100644 (file)
@@ -15,6 +15,10 @@ module frr-staticd {
     prefix inet;
   }
 
+  import frr-bfdd {
+    prefix frr-bfdd;
+  }
+
   organization
     "FRRouting";
   contact
@@ -114,7 +118,19 @@ module frr-staticd {
             "AFI-SAFI type.";
         }
 
-        uses staticd-prefix-attributes;
+        uses staticd-prefix-attributes {
+          augment "path-list/frr-nexthops/nexthop" {
+            container bfd-monitoring {
+              description "BFD monitoring options.";
+              presence
+                "Present if BFD configuration is available.";
+
+              when "../nh-type = 'ip4' or ../nh-type = 'ip4-ifindex' or
+                    ../nh-type = 'ip6' or ../nh-type = 'ip6-ifindex'";
+              uses frr-bfdd:bfd-monitoring;
+            }
+          }
+        }
 
         list src-list {
           key "src-prefix";
index f8e843cc735db6ba19cac57669c08234a14042b1..2eed7f301c8bbb5c886b1654809439cd0d2b475b 100644 (file)
@@ -63,14 +63,12 @@ struct zebra_ptm_cb {
 #define ZEBRA_IF_PTM_ENABLE_ON     1
 #define ZEBRA_IF_PTM_ENABLE_UNSPEC 2
 
-#define IS_BFD_ENABLED_PROTOCOL(protocol) ( \
-       (protocol) == ZEBRA_ROUTE_BGP || \
-       (protocol) == ZEBRA_ROUTE_OSPF || \
-       (protocol) == ZEBRA_ROUTE_OSPF6 || \
-       (protocol) == ZEBRA_ROUTE_ISIS || \
-       (protocol) == ZEBRA_ROUTE_PIM || \
-       (protocol) == ZEBRA_ROUTE_OPENFABRIC \
-)
+#define IS_BFD_ENABLED_PROTOCOL(protocol)                                      \
+       ((protocol) == ZEBRA_ROUTE_BGP || (protocol) == ZEBRA_ROUTE_OSPF ||    \
+        (protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS ||  \
+        (protocol) == ZEBRA_ROUTE_PIM ||                                      \
+        (protocol) == ZEBRA_ROUTE_OPENFABRIC ||                               \
+        (protocol) == ZEBRA_ROUTE_STATIC)
 
 void zebra_ptm_init(void);
 void zebra_ptm_finish(void);