]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_nht.c
Merge pull request #3087 from opensourcerouting/bfd-memleak
[mirror_frr.git] / bgpd / bgp_nht.c
index 54c0f85cb3b663072420fd93c55f35bbab34d4c6..dd1ffe9f3bd4540132c3d42d1ee24b742e9adcf4 100644 (file)
@@ -38,6 +38,7 @@
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_nexthop.h"
 #include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
 #include "bgpd/bgp_nht.h"
 #include "bgpd/bgp_fsm.h"
 #include "bgpd/bgp_zebra.h"
@@ -59,6 +60,12 @@ static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc)
                || (bnc && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)));
 }
 
+static int bgp_isvalid_labeled_nexthop(struct bgp_nexthop_cache *bnc)
+{
+       return (bgp_zebra_num_connects() == 0
+               || (bnc && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID)));
+}
+
 int bgp_find_nexthop(struct bgp_info *path, int connected)
 {
        struct bgp_nexthop_cache *bnc = path->nexthop;
@@ -90,7 +97,7 @@ static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc)
                }
                unregister_zebra_rnh(bnc,
                                     CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE));
-               bnc->node->info = NULL;
+               bgp_nexthop_set_node_info(bnc->node, NULL);
                bgp_unlock_node(bnc->node);
                bnc->node = NULL;
                bnc_free(bnc);
@@ -121,18 +128,22 @@ void bgp_unlink_nexthop_by_peer(struct peer *peer)
 
        rn = bgp_node_get(peer->bgp->nexthop_cache_table[afi], &p);
 
-       if (!rn->info)
+       bnc = bgp_nexthop_get_node_info(rn);
+       if (!bnc)
                return;
 
-       bnc = rn->info;
-
        /* cleanup the peer reference */
        bnc->nht_info = NULL;
 
        bgp_unlink_nexthop_check(bnc);
 }
 
-int bgp_find_or_add_nexthop(struct bgp *bgp, afi_t afi, struct bgp_info *ri,
+/*
+ * A route and its nexthop might belong to different VRFs. Therefore,
+ * we need both the bgp_route and bgp_nexthop pointers.
+ */
+int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
+                           afi_t afi, struct bgp_info *ri,
                            struct peer *peer, int connected)
 {
        struct bgp_node *rn;
@@ -175,15 +186,16 @@ int bgp_find_or_add_nexthop(struct bgp *bgp, afi_t afi, struct bgp_info *ri,
                return 0;
 
        if (is_bgp_static_route)
-               rn = bgp_node_get(bgp->import_check_table[afi], &p);
+               rn = bgp_node_get(bgp_nexthop->import_check_table[afi], &p);
        else
-               rn = bgp_node_get(bgp->nexthop_cache_table[afi], &p);
+               rn = bgp_node_get(bgp_nexthop->nexthop_cache_table[afi], &p);
 
-       if (!rn->info) {
+       bnc = bgp_nexthop_get_node_info(rn);
+       if (!bnc) {
                bnc = bnc_new();
-               rn->info = bnc;
+               bgp_nexthop_set_node_info(rn, bnc);
                bnc->node = rn;
-               bnc->bgp = bgp;
+               bnc->bgp = bgp_nexthop;
                bgp_lock_node(rn);
                if (BGP_DEBUG(nht, NHT)) {
                        char buf[PREFIX2STR_BUFFER];
@@ -193,18 +205,17 @@ int bgp_find_or_add_nexthop(struct bgp *bgp, afi_t afi, struct bgp_info *ri,
                }
        }
 
-       bnc = rn->info;
        bgp_unlock_node(rn);
        if (is_bgp_static_route) {
                SET_FLAG(bnc->flags, BGP_STATIC_ROUTE);
 
                /* If we're toggling the type, re-register */
-               if ((bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK))
+               if ((bgp_flag_check(bgp_route, BGP_FLAG_IMPORT_CHECK))
                    && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) {
                        SET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH);
                        UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
                        UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
-               } else if ((!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK))
+               } else if ((!bgp_flag_check(bgp_route, BGP_FLAG_IMPORT_CHECK))
                           && CHECK_FLAG(bnc->flags,
                                         BGP_STATIC_ROUTE_EXACT_MATCH)) {
                        UNSET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH);
@@ -235,7 +246,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp, afi_t afi, struct bgp_info *ri,
                UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
                UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
        }
-       if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) {
+       if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) {
                bnc->flags |= BGP_NEXTHOP_REGISTERED;
                bnc->flags |= BGP_NEXTHOP_VALID;
        } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED))
@@ -261,7 +272,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp, afi_t afi, struct bgp_info *ri,
         * ability to detect nexthops.  So when we have a view
         * just tell everyone the nexthop is valid
         */
-       if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+       if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW)
                return 1;
        else
                return (bgp_isvalid_nexthop(bnc));
@@ -285,16 +296,21 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer)
 
        rn = bgp_node_lookup(
                peer->bgp->nexthop_cache_table[family2afi(p.family)], &p);
-       if (!rn || !rn->info) {
+       if (!rn) {
                if (BGP_DEBUG(nht, NHT))
                        zlog_debug("Cannot find connected NHT node for peer %s",
                                   peer->host);
-               if (rn)
-                       bgp_unlock_node(rn);
                return;
        }
 
-       bnc = rn->info;
+       bnc = bgp_nexthop_get_node_info(rn);
+       if (!bnc) {
+               if (BGP_DEBUG(nht, NHT))
+                       zlog_debug("Cannot find connected NHT node for peer %s on route_node as expected",
+                                  peer->host);
+               bgp_unlock_node(rn);
+               return;
+       }
        bgp_unlock_node(rn);
 
        if (bnc->nht_info != peer) {
@@ -312,7 +328,7 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer)
                        zlog_debug("Freeing connected NHT node %p for peer %s",
                                   bnc, peer->host);
                unregister_zebra_rnh(bnc, 0);
-               bnc->node->info = NULL;
+               bgp_nexthop_set_node_info(bnc->node, NULL);
                bgp_unlock_node(bnc->node);
                bnc_free(bnc);
        }
@@ -332,7 +348,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
 
        bgp = bgp_lookup_by_vrf_id(vrf_id);
        if (!bgp) {
-               zlog_err(
+               flog_err(
+                       EC_BGP_NH_UPD,
                        "parse nexthop update: instance not found for vrf_id %u",
                        vrf_id);
                return;
@@ -354,19 +371,29 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                        bgp->import_check_table[family2afi(nhr.prefix.family)],
                        &nhr.prefix);
 
-       if (!rn || !rn->info) {
+       if (!rn) {
                if (BGP_DEBUG(nht, NHT)) {
                        char buf[PREFIX2STR_BUFFER];
                        prefix2str(&nhr.prefix, buf, sizeof(buf));
                        zlog_debug("parse nexthop update(%s): rn not found",
                                   buf);
                }
-               if (rn)
-                       bgp_unlock_node(rn);
                return;
        }
 
-       bnc = rn->info;
+       bnc = bgp_nexthop_get_node_info(rn);
+       if (!bnc) {
+               if (BGP_DEBUG(nht, NHT)) {
+                       char buf[PREFIX2STR_BUFFER];
+
+                       prefix2str(&nhr.prefix, buf, sizeof(buf));
+                       zlog_debug("parse nexthop update(%s): bnc node info not found",
+                                  buf);
+               }
+               bgp_unlock_node(rn);
+               return;
+       }
+
        bgp_unlock_node(rn);
        bnc->last_update = bgp_clock();
        bnc->change_flags = 0;
@@ -388,6 +415,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                bnc->change_flags |= BGP_NEXTHOP_CHANGED;
 
        if (nhr.nexthop_num) {
+               struct peer *peer = bnc->nht_info;
+
                /* notify bgp fsm if nbr ip goes from invalid->valid */
                if (!bnc->nexthop_num)
                        UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
@@ -396,14 +425,43 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                bnc->metric = nhr.metric;
                bnc->nexthop_num = nhr.nexthop_num;
 
+               bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; /* check below */
+
                for (i = 0; i < nhr.nexthop_num; i++) {
+                       int num_labels = 0;
+
                        nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
 
+                       /*
+                        * Turn on RA for the v6 nexthops
+                        * we receive from bgp.  This is to allow us
+                        * to work with v4 routing over v6 nexthops
+                        */
+                       if (peer &&
+                           CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)
+                           && nhr.prefix.family == AF_INET6) {
+                               struct interface *ifp;
+
+                               ifp = if_lookup_by_index(nexthop->ifindex,
+                                                        nexthop->vrf_id);
+                               zclient_send_interface_radv_req(
+                                       zclient, nexthop->vrf_id, ifp, true,
+                                       BGP_UNNUM_DEFAULT_RA_INTERVAL);
+                       }
+                       /* There is at least one label-switched path */
+                       if (nexthop->nh_label &&
+                               nexthop->nh_label->num_labels) {
+
+                               bnc->flags |= BGP_NEXTHOP_LABELED_VALID;
+                               num_labels = nexthop->nh_label->num_labels;
+                       }
+
                        if (BGP_DEBUG(nht, NHT)) {
                                char buf[NEXTHOP_STRLEN];
                                zlog_debug(
-                                       "    nhop via %s",
-                                       nexthop2str(nexthop, buf, sizeof(buf)));
+                                       "    nhop via %s (%d labels)",
+                                       nexthop2str(nexthop, buf, sizeof(buf)),
+                                       num_labels);
                        }
 
                        if (nhlist_tail) {
@@ -422,7 +480,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                                continue;
 
                        for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next)
-                               if (nexthop_same_no_recurse(oldnh, nexthop))
+                               if (nexthop_same_no_recurse(oldnh, nexthop) &&
+                                   nexthop_labels_match(oldnh, nexthop))
                                        break;
 
                        if (!oldnh)
@@ -460,7 +519,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
 
                for (rn = bgp_table_top(bgp->nexthop_cache_table[afi]); rn;
                     rn = bgp_route_next(rn)) {
-                       bnc = rn->info;
+                       bnc = bgp_nexthop_get_node_info(rn);
                        if (!bnc)
                                continue;
 
@@ -552,11 +611,21 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command)
                || CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)))
                exact_match = true;
 
+       if (BGP_DEBUG(zebra, ZEBRA)) {
+               char buf[PREFIX2STR_BUFFER];
+
+               prefix2str(p, buf, PREFIX2STR_BUFFER);
+               zlog_debug("%s: sending cmd %s for %s (vrf %s)",
+                       __func__, zserv_command_string(command), buf,
+                       bnc->bgp->name);
+       }
+
        ret = zclient_send_rnh(zclient, command, p, exact_match,
                               bnc->bgp->vrf_id);
        /* TBD: handle the failure */
        if (ret < 0)
-               zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
+               flog_warn(EC_BGP_ZEBRA_SEND,
+                         "sendmsg_nexthop: zclient_send_message() failed");
 
        if ((command == ZEBRA_NEXTHOP_REGISTER)
            || (command == ZEBRA_IMPORT_ROUTE_REGISTER))
@@ -618,11 +687,11 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
 {
        struct bgp_node *rn;
        struct bgp_info *path;
-       struct bgp *bgp = bnc->bgp;
        int afi;
        struct peer *peer = (struct peer *)bnc->nht_info;
        struct bgp_table *table;
        safi_t safi;
+       struct bgp *bgp_path;
 
        if (BGP_DEBUG(nht, NHT)) {
                char buf[PREFIX2STR_BUFFER];
@@ -635,7 +704,8 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
        LIST_FOREACH (path, &(bnc->paths), nh_thread) {
                if (!(path->type == ZEBRA_ROUTE_BGP
                      && ((path->sub_type == BGP_ROUTE_NORMAL)
-                         || (path->sub_type == BGP_ROUTE_STATIC))))
+                         || (path->sub_type == BGP_ROUTE_STATIC)
+                         || (path->sub_type == BGP_ROUTE_IMPORTED))))
                        continue;
 
                rn = path->net;
@@ -644,19 +714,55 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
                table = bgp_node_table(rn);
                safi = table->safi;
 
-               /* Path becomes valid/invalid depending on whether the nexthop
+               /*
+                * handle routes from other VRFs (they can have a
+                * nexthop in THIS VRF). bgp_path is the bgp instance
+                * that owns the route referencing this nexthop.
+                */
+               bgp_path = table->bgp;
+
+               /*
+                * Path becomes valid/invalid depending on whether the nexthop
                 * reachable/unreachable.
+                *
+                * In case of unicast routes that were imported from vpn
+                * and that have labels, they are valid only if there are
+                * nexthops with labels
                 */
+
+               int bnc_is_valid_nexthop = 0;
+
+               if (safi == SAFI_UNICAST &&
+                       path->sub_type == BGP_ROUTE_IMPORTED &&
+                       path->extra &&
+                       path->extra->num_labels) {
+
+                       bnc_is_valid_nexthop =
+                               bgp_isvalid_labeled_nexthop(bnc) ? 1 : 0;
+               } else {
+                       bnc_is_valid_nexthop =
+                               bgp_isvalid_nexthop(bnc) ? 1 : 0;
+               }
+
+               if (BGP_DEBUG(nht, NHT)) {
+                       char buf[PREFIX_STRLEN];
+
+                       prefix2str(&rn->p, buf, PREFIX_STRLEN);
+                       zlog_debug("%s: prefix %s (vrf %s) %svalid",
+                               __func__, buf, bgp_path->name,
+                               (bnc_is_valid_nexthop ? "" : "not "));
+               }
+
                if ((CHECK_FLAG(path->flags, BGP_INFO_VALID) ? 1 : 0)
-                   != (bgp_isvalid_nexthop(bnc) ? 1 : 0)) {
+                   != bnc_is_valid_nexthop) {
                        if (CHECK_FLAG(path->flags, BGP_INFO_VALID)) {
-                               bgp_aggregate_decrement(bgp, &rn->p, path, afi,
-                                                       safi);
+                               bgp_aggregate_decrement(bgp_path, &rn->p,
+                                                       path, afi, safi);
                                bgp_info_unset_flag(rn, path, BGP_INFO_VALID);
                        } else {
                                bgp_info_set_flag(rn, path, BGP_INFO_VALID);
-                               bgp_aggregate_increment(bgp, &rn->p, path, afi,
-                                                       safi);
+                               bgp_aggregate_increment(bgp_path, &rn->p,
+                                                       path, afi, safi);
                        }
                }
 
@@ -671,7 +777,7 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
                    || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED))
                        SET_FLAG(path->flags, BGP_INFO_IGP_CHANGED);
 
-               bgp_process(bgp, rn, afi, safi);
+               bgp_process(bgp_path, rn, afi, safi);
        }
 
        if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) {