| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <zebra.h> |
| |
| #include "command.h" |
| #include "thread.h" |
| #include "prefix.h" |
| #include "zclient.h" |
| #include "stream.h" |
| #include "network.h" |
| #include "log.h" |
| #include "memory.h" |
| #include "nexthop.h" |
| #include "vrf.h" |
| #include "filter.h" |
| |
| #include "bgpd/bgpd.h" |
| #include "bgpd/bgp_table.h" |
| #include "bgpd/bgp_route.h" |
| #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" |
| #include "bgpd/bgp_flowspec_util.h" |
| #include "bgpd/bgp_evpn.h" |
| #include "bgpd/bgp_rd.h" |
| |
| extern struct zclient *zclient; |
| |
| static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, |
| int is_bgp_static_route); |
| static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, |
| int is_bgp_static_route); |
| static void evaluate_paths(struct bgp_nexthop_cache *bnc); |
| static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); |
| |
| static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc) |
| { |
| return (bgp_zebra_num_connects() == 0 |
| || (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))); |
| } |
| |
| static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) |
| { |
| if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) { |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf[PREFIX2STR_BUFFER]; |
| zlog_debug("bgp_unlink_nexthop: freeing bnc %s(%u)(%s)", |
| bnc_str(bnc, buf, PREFIX2STR_BUFFER), |
| bnc->srte_color, bnc->bgp->name_pretty); |
| } |
| |
| if (!bnc_existing_for_prefix(bnc)) |
| unregister_zebra_rnh( |
| bnc, CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); |
| bnc_free(bnc); |
| } |
| } |
| |
| void bgp_unlink_nexthop(struct bgp_path_info *path) |
| { |
| struct bgp_nexthop_cache *bnc = path->nexthop; |
| |
| if (!bnc) |
| return; |
| |
| path_nh_map(path, NULL, false); |
| |
| bgp_unlink_nexthop_check(bnc); |
| } |
| |
| void bgp_unlink_nexthop_by_peer(struct peer *peer) |
| { |
| struct prefix p; |
| struct bgp_nexthop_cache *bnc; |
| afi_t afi = family2afi(peer->su.sa.sa_family); |
| |
| if (!sockunion2hostprefix(&peer->su, &p)) |
| return; |
| |
| bnc = bnc_find(&peer->bgp->nexthop_cache_table[afi], &p, 0); |
| if (!bnc) |
| return; |
| |
| |
| bnc->nht_info = NULL; |
| |
| bgp_unlink_nexthop_check(bnc); |
| } |
| |
| |
| |
| |
| |
| int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, |
| afi_t afi, struct bgp_path_info *pi, |
| struct peer *peer, int connected) |
| { |
| struct bgp_nexthop_cache_head *tree = NULL; |
| struct bgp_nexthop_cache *bnc; |
| struct prefix p; |
| uint32_t srte_color = 0; |
| int is_bgp_static_route = 0; |
| |
| if (pi) { |
| is_bgp_static_route = ((pi->type == ZEBRA_ROUTE_BGP) |
| && (pi->sub_type == BGP_ROUTE_STATIC)) |
| ? 1 |
| : 0; |
| |
| |
| |
| |
| if (!is_bgp_static_route) |
| afi = BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr) ? AFI_IP6 |
| : AFI_IP; |
| |
| |
| if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { |
| afi = AFI_IP; |
| } |
| |
| |
| |
| if (make_prefix(afi, pi, &p) < 0) |
| return 1; |
| |
| srte_color = pi->attr->srte_color; |
| } else if (peer) { |
| if (!sockunion2hostprefix(&peer->su, &p)) { |
| if (BGP_DEBUG(nht, NHT)) { |
| zlog_debug( |
| "%s: Attempting to register with unknown AFI %d (not %d or %d)", |
| __func__, afi, AFI_IP, AFI_IP6); |
| } |
| return 0; |
| } |
| } else |
| return 0; |
| |
| if (is_bgp_static_route) |
| tree = &bgp_nexthop->import_check_table[afi]; |
| else |
| tree = &bgp_nexthop->nexthop_cache_table[afi]; |
| |
| bnc = bnc_find(tree, &p, srte_color); |
| if (!bnc) { |
| bnc = bnc_new(tree, &p, srte_color); |
| bnc->bgp = bgp_nexthop; |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf[PREFIX2STR_BUFFER]; |
| |
| zlog_debug("Allocated bnc %s(%u)(%s) peer %p", |
| bnc_str(bnc, buf, PREFIX2STR_BUFFER), |
| bnc->srte_color, bnc->bgp->name_pretty, |
| peer); |
| } |
| } |
| |
| if (is_bgp_static_route) { |
| SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); |
| |
| |
| if ((CHECK_FLAG(bgp_route->flags, 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 ((!CHECK_FLAG(bgp_route->flags, |
| BGP_FLAG_IMPORT_CHECK)) |
| && CHECK_FLAG(bnc->flags, |
| BGP_STATIC_ROUTE_EXACT_MATCH)) { |
| UNSET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| else if (connected && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) { |
| SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); |
| } else if (peer && !connected |
| && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) { |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); |
| } |
| if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) { |
| SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); |
| } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) |
| && !is_default_host_route(&bnc->prefix)) |
| register_zebra_rnh(bnc, is_bgp_static_route); |
| |
| if (pi && pi->nexthop != bnc) { |
| |
| |
| |
| |
| bgp_unlink_nexthop(pi); |
| |
| |
| path_nh_map(pi, bnc, true); |
| |
| if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric) |
| (bgp_path_info_extra_get(pi))->igpmetric = bnc->metric; |
| else if (pi->extra) |
| pi->extra->igpmetric = 0; |
| } else if (peer) |
| bnc->nht_info = (void *)peer; |
| |
| |
| |
| |
| |
| |
| if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) |
| return 1; |
| else |
| return (bgp_isvalid_nexthop(bnc)); |
| } |
| |
| void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) |
| { |
| struct bgp_nexthop_cache *bnc; |
| struct prefix p; |
| |
| if (!peer) |
| return; |
| |
| if (!sockunion2hostprefix(&peer->su, &p)) |
| return; |
| |
| bnc = bnc_find(&peer->bgp->nexthop_cache_table[family2afi(p.family)], |
| &p, 0); |
| if (!bnc) { |
| if (BGP_DEBUG(nht, NHT)) |
| zlog_debug( |
| "Cannot find connected NHT node for peer %s(%s)", |
| peer->host, peer->bgp->name_pretty); |
| return; |
| } |
| |
| if (bnc->nht_info != peer) { |
| if (BGP_DEBUG(nht, NHT)) |
| zlog_debug( |
| "Connected NHT %p node for peer %s(%s) points to %p", |
| bnc, peer->host, bnc->bgp->name_pretty, |
| bnc->nht_info); |
| return; |
| } |
| |
| bnc->nht_info = NULL; |
| |
| if (LIST_EMPTY(&(bnc->paths))) { |
| if (BGP_DEBUG(nht, NHT)) |
| zlog_debug( |
| "Freeing connected NHT node %p for peer %s(%s)", |
| bnc, peer->host, bnc->bgp->name_pretty); |
| unregister_zebra_rnh(bnc, 0); |
| bnc_free(bnc); |
| } |
| } |
| |
| static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, |
| struct zapi_route *nhr) |
| { |
| struct nexthop *nexthop; |
| struct nexthop *oldnh; |
| struct nexthop *nhlist_head = NULL; |
| struct nexthop *nhlist_tail = NULL; |
| int i; |
| |
| bnc->last_update = bgp_clock(); |
| bnc->change_flags = 0; |
| |
| |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf[PREFIX2STR_BUFFER]; |
| prefix2str(&nhr->prefix, buf, sizeof(buf)); |
| zlog_debug( |
| "%s(%u): Rcvd NH update %s(%u) - metric %d/%d #nhops %d/%d flags 0x%x", |
| bnc->bgp->name_pretty, bnc->bgp->vrf_id, buf, |
| bnc->srte_color, nhr->metric, bnc->metric, |
| nhr->nexthop_num, bnc->nexthop_num, bnc->flags); |
| } |
| |
| if (nhr->metric != bnc->metric) |
| bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED; |
| |
| if (nhr->nexthop_num != bnc->nexthop_num) |
| bnc->change_flags |= BGP_NEXTHOP_CHANGED; |
| |
| if (nhr->nexthop_num) { |
| struct peer *peer = bnc->nht_info; |
| |
| |
| if (!bnc->nexthop_num) |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
| |
| bnc->flags |= BGP_NEXTHOP_VALID; |
| bnc->metric = nhr->metric; |
| bnc->nexthop_num = nhr->nexthop_num; |
| |
| bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; |
| |
| for (i = 0; i < nhr->nexthop_num; i++) { |
| int num_labels = 0; |
| |
| nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]); |
| |
| |
| |
| |
| |
| |
| if (peer && !peer->ifp |
| && CHECK_FLAG(peer->flags, |
| PEER_FLAG_CAPABILITY_ENHE) |
| && nhr->prefix.family == AF_INET6 |
| && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { |
| struct interface *ifp; |
| |
| ifp = if_lookup_by_index(nexthop->ifindex, |
| nexthop->vrf_id); |
| if (ifp) |
| zclient_send_interface_radv_req( |
| zclient, nexthop->vrf_id, ifp, |
| true, |
| BGP_UNNUM_DEFAULT_RA_INTERVAL); |
| } |
| |
| 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 (%d labels)", |
| nexthop2str(nexthop, buf, sizeof(buf)), |
| num_labels); |
| } |
| |
| if (nhlist_tail) { |
| nhlist_tail->next = nexthop; |
| nhlist_tail = nexthop; |
| } else { |
| nhlist_tail = nexthop; |
| nhlist_head = nexthop; |
| } |
| |
| |
| |
| |
| |
| if (bnc->change_flags & BGP_NEXTHOP_CHANGED) |
| continue; |
| |
| for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next) |
| if (nexthop_same(oldnh, nexthop)) |
| break; |
| |
| if (!oldnh) |
| bnc->change_flags |= BGP_NEXTHOP_CHANGED; |
| } |
| bnc_nexthop_free(bnc); |
| bnc->nexthop = nhlist_head; |
| } else { |
| bnc->flags &= ~BGP_NEXTHOP_VALID; |
| bnc->nexthop_num = nhr->nexthop_num; |
| |
| |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
| |
| bnc_nexthop_free(bnc); |
| bnc->nexthop = NULL; |
| } |
| |
| evaluate_paths(bnc); |
| } |
| |
| void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) |
| { |
| struct bgp_nexthop_cache_head *tree = NULL; |
| struct bgp_nexthop_cache *bnc; |
| struct bgp *bgp; |
| struct zapi_route nhr; |
| afi_t afi; |
| |
| bgp = bgp_lookup_by_vrf_id(vrf_id); |
| if (!bgp) { |
| flog_err( |
| EC_BGP_NH_UPD, |
| "parse nexthop update: instance not found for vrf_id %u", |
| vrf_id); |
| return; |
| } |
| |
| if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { |
| zlog_err("%s[%s]: Failure to decode nexthop update", |
| __PRETTY_FUNCTION__, bgp->name_pretty); |
| return; |
| } |
| |
| afi = family2afi(nhr.prefix.family); |
| if (command == ZEBRA_NEXTHOP_UPDATE) |
| tree = &bgp->nexthop_cache_table[afi]; |
| else if (command == ZEBRA_IMPORT_CHECK_UPDATE) |
| tree = &bgp->import_check_table[afi]; |
| |
| bnc = bnc_find(tree, &nhr.prefix, nhr.srte_color); |
| if (!bnc) { |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf[PREFIX2STR_BUFFER]; |
| |
| prefix2str(&nhr.prefix, buf, sizeof(buf)); |
| zlog_debug( |
| "parse nexthop update(%s(%u)(%s)): bnc info not found", |
| buf, nhr.srte_color, bgp->name_pretty); |
| } |
| return; |
| } |
| |
| bgp_process_nexthop_update(bnc, &nhr); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (nhr.srte_color == 0) { |
| struct bgp_nexthop_cache *bnc_iter; |
| |
| frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], |
| bnc_iter) { |
| if (!prefix_same(&bnc->prefix, &bnc_iter->prefix) |
| || bnc_iter->srte_color == 0 |
| || CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID)) |
| continue; |
| |
| bgp_process_nexthop_update(bnc_iter, &nhr); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| void bgp_cleanup_nexthops(struct bgp *bgp) |
| { |
| for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { |
| struct bgp_nexthop_cache *bnc; |
| |
| frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], |
| bnc) { |
| |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) |
| { |
| |
| int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP) |
| && (pi->sub_type == BGP_ROUTE_STATIC)) |
| ? 1 |
| : 0; |
| struct bgp_dest *net = pi->net; |
| const struct prefix *p_orig = bgp_dest_get_prefix(net); |
| struct in_addr ipv4; |
| |
| if (p_orig->family == AF_FLOWSPEC) { |
| if (!pi->peer) |
| return -1; |
| return bgp_flowspec_get_first_nh(pi->peer->bgp, |
| pi, p, afi); |
| } |
| memset(p, 0, sizeof(struct prefix)); |
| switch (afi) { |
| case AFI_IP: |
| p->family = AF_INET; |
| if (is_bgp_static) { |
| p->u.prefix4 = p_orig->u.prefix4; |
| p->prefixlen = p_orig->prefixlen; |
| } else { |
| if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { |
| ipv4_mapped_ipv6_to_ipv4( |
| &pi->attr->mp_nexthop_global, &ipv4); |
| p->u.prefix4 = ipv4; |
| p->prefixlen = IPV4_MAX_BITLEN; |
| } else { |
| p->u.prefix4 = pi->attr->nexthop; |
| p->prefixlen = IPV4_MAX_BITLEN; |
| } |
| } |
| break; |
| case AFI_IP6: |
| p->family = AF_INET6; |
| |
| if (is_bgp_static) { |
| p->u.prefix6 = p_orig->u.prefix6; |
| p->prefixlen = p_orig->prefixlen; |
| } else { |
| |
| |
| |
| if (pi->attr->mp_nexthop_len |
| == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL |
| && (IN6_IS_ADDR_UNSPECIFIED( |
| &pi->attr->mp_nexthop_global) |
| || IN6_IS_ADDR_LINKLOCAL( |
| &pi->attr->mp_nexthop_global))) |
| p->u.prefix6 = pi->attr->mp_nexthop_local; |
| else |
| p->u.prefix6 = pi->attr->mp_nexthop_global; |
| p->prefixlen = IPV6_MAX_BITLEN; |
| } |
| break; |
| default: |
| if (BGP_DEBUG(nht, NHT)) { |
| zlog_debug( |
| "%s: Attempting to make prefix with unknown AFI %d (not %d or %d)", |
| __func__, afi, AFI_IP, AFI_IP6); |
| } |
| break; |
| } |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) |
| { |
| bool exact_match = false; |
| int ret; |
| |
| if (!zclient) |
| return; |
| |
| |
| if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bnc->bgp)) { |
| if (BGP_DEBUG(zebra, ZEBRA)) |
| zlog_debug( |
| "%s: No zebra instance to talk to, not installing NHT entry", |
| __func__); |
| return; |
| } |
| |
| if (!bgp_zebra_num_connects()) { |
| if (BGP_DEBUG(zebra, ZEBRA)) |
| zlog_debug( |
| "%s: We have not connected yet, cannot send nexthops", |
| __func__); |
| } |
| if ((command == ZEBRA_NEXTHOP_REGISTER |
| || command == ZEBRA_IMPORT_ROUTE_REGISTER) |
| && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) |
| || CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH))) |
| exact_match = true; |
| |
| if (BGP_DEBUG(zebra, ZEBRA)) |
| zlog_debug("%s: sending cmd %s for %pFX (vrf %s)", __func__, |
| zserv_command_string(command), &bnc->prefix, |
| bnc->bgp->name_pretty); |
| |
| ret = zclient_send_rnh(zclient, command, &bnc->prefix, exact_match, |
| bnc->bgp->vrf_id); |
| |
| if (ret < 0) |
| flog_warn(EC_BGP_ZEBRA_SEND, |
| "sendmsg_nexthop: zclient_send_message() failed"); |
| |
| if ((command == ZEBRA_NEXTHOP_REGISTER) |
| || (command == ZEBRA_IMPORT_ROUTE_REGISTER)) |
| SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| else if ((command == ZEBRA_NEXTHOP_UNREGISTER) |
| || (command == ZEBRA_IMPORT_ROUTE_UNREGISTER)) |
| UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, |
| int is_bgp_import_route) |
| { |
| |
| if (bnc->flags & BGP_NEXTHOP_REGISTERED) |
| return; |
| if (is_bgp_import_route) |
| sendmsg_zebra_rnh(bnc, ZEBRA_IMPORT_ROUTE_REGISTER); |
| else |
| sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_REGISTER); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, |
| int is_bgp_import_route) |
| { |
| |
| if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) |
| return; |
| |
| if (is_bgp_import_route) |
| sendmsg_zebra_rnh(bnc, ZEBRA_IMPORT_ROUTE_UNREGISTER); |
| else |
| sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_UNREGISTER); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void evaluate_paths(struct bgp_nexthop_cache *bnc) |
| { |
| struct bgp_dest *dest; |
| struct bgp_path_info *path; |
| int afi; |
| struct peer *peer = (struct peer *)bnc->nht_info; |
| struct bgp_table *table; |
| safi_t safi; |
| struct bgp *bgp_path; |
| const struct prefix *p; |
| |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf[PREFIX2STR_BUFFER]; |
| bnc_str(bnc, buf, PREFIX2STR_BUFFER); |
| zlog_debug( |
| "NH update for %s(%u)(%s) - flags 0x%x chgflags 0x%x - evaluate paths", |
| buf, bnc->srte_color, bnc->bgp->name_pretty, bnc->flags, |
| bnc->change_flags); |
| } |
| |
| 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_IMPORTED)))) |
| continue; |
| |
| dest = path->net; |
| assert(dest && bgp_dest_table(dest)); |
| p = bgp_dest_get_prefix(dest); |
| afi = family2afi(p->family); |
| table = bgp_dest_table(dest); |
| safi = table->safi; |
| |
| |
| |
| |
| |
| |
| bgp_path = table->bgp; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool bnc_is_valid_nexthop = false; |
| bool path_valid = false; |
| |
| 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) ? true : false; |
| } else { |
| if (bgp_update_martian_nexthop( |
| bnc->bgp, afi, safi, path->type, |
| path->sub_type, path->attr, dest)) { |
| if (BGP_DEBUG(nht, NHT)) |
| zlog_debug( |
| "%s: prefix %pRN (vrf %s), ignoring path due to martian or self-next-hop", |
| __func__, dest, bgp_path->name); |
| } else |
| bnc_is_valid_nexthop = |
| bgp_isvalid_nexthop(bnc) ? true : false; |
| } |
| |
| if (BGP_DEBUG(nht, NHT)) { |
| char buf1[RD_ADDRSTRLEN]; |
| |
| if (dest->pdest) { |
| prefix_rd2str((struct prefix_rd *)bgp_dest_get_prefix(dest->pdest), |
| buf1, sizeof(buf1)); |
| zlog_debug( |
| "... eval path %d/%d %pRN RD %s %s flags 0x%x", |
| afi, safi, dest, buf1, |
| bgp_path->name_pretty, path->flags); |
| } else |
| zlog_debug( |
| "... eval path %d/%d %pRN %s flags 0x%x", |
| afi, safi, dest, bgp_path->name_pretty, |
| path->flags); |
| } |
| |
| |
| if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED) |
| || CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) |
| continue; |
| |
| |
| |
| if (bgp_isvalid_nexthop(bnc) && bnc->metric) |
| (bgp_path_info_extra_get(path))->igpmetric = |
| bnc->metric; |
| else if (path->extra) |
| path->extra->igpmetric = 0; |
| |
| if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) |
| || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) |
| || path->attr->srte_color != 0) |
| SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); |
| |
| path_valid = !!CHECK_FLAG(path->flags, BGP_PATH_VALID); |
| if (path_valid != bnc_is_valid_nexthop) { |
| if (path_valid) { |
| |
| |
| |
| bgp_aggregate_decrement(bgp_path, p, path, afi, |
| safi); |
| bgp_path_info_unset_flag(dest, path, |
| BGP_PATH_VALID); |
| if (safi == SAFI_EVPN && |
| bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) |
| bgp_evpn_unimport_route(bgp_path, |
| afi, safi, bgp_dest_get_prefix(dest), path); |
| } else { |
| |
| |
| |
| bgp_path_info_set_flag(dest, path, |
| BGP_PATH_VALID); |
| bgp_aggregate_increment(bgp_path, p, path, afi, |
| safi); |
| if (safi == SAFI_EVPN && |
| bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) |
| bgp_evpn_import_route(bgp_path, |
| afi, safi, bgp_dest_get_prefix(dest), path); |
| } |
| } |
| |
| bgp_process(bgp_path, dest, afi, safi); |
| } |
| |
| if (peer) { |
| int valid_nexthops = bgp_isvalid_nexthop(bnc); |
| |
| if (valid_nexthops) |
| peer->last_reset = PEER_DOWN_WAITING_OPEN; |
| else |
| peer->last_reset = PEER_DOWN_WAITING_NHT; |
| |
| if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) { |
| if (BGP_DEBUG(nht, NHT)) |
| zlog_debug( |
| "%s: Updating peer (%s(%s)) status with NHT", |
| __func__, peer->host, |
| peer->bgp->name_pretty); |
| bgp_fsm_event_update(peer, valid_nexthops); |
| SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
| } |
| } |
| |
| RESET_FLAG(bnc->change_flags); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc, |
| bool make) |
| { |
| if (path->nexthop) { |
| LIST_REMOVE(path, nh_thread); |
| path->nexthop->path_count--; |
| path->nexthop = NULL; |
| } |
| if (make) { |
| LIST_INSERT_HEAD(&(bnc->paths), path, nh_thread); |
| path->nexthop = bnc; |
| path->nexthop->path_count++; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| void bgp_nht_register_nexthops(struct bgp *bgp) |
| { |
| for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { |
| struct bgp_nexthop_cache *bnc; |
| |
| frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], |
| bnc) { |
| register_zebra_rnh(bnc, 0); |
| } |
| } |
| } |
| |
| void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) |
| { |
| struct bgp *bgp; |
| struct bgp_nexthop_cache *bnc; |
| struct nexthop *nhop; |
| struct interface *ifp; |
| struct prefix p; |
| |
| if (peer->ifp) |
| return; |
| |
| bgp = peer->bgp; |
| if (!sockunion2hostprefix(&peer->su, &p)) { |
| zlog_warn("%s: Unable to convert sockunion to prefix for %s", |
| __func__, peer->host); |
| return; |
| } |
| |
| if (p.family != AF_INET6) |
| return; |
| |
| bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); |
| if (!bnc) |
| return; |
| |
| if (peer != bnc->nht_info) |
| return; |
| |
| for (nhop = bnc->nexthop; nhop; nhop = nhop->next) { |
| ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id); |
| |
| if (!ifp) |
| continue; |
| |
| zclient_send_interface_radv_req(zclient, |
| nhop->vrf_id, |
| ifp, true, |
| BGP_UNNUM_DEFAULT_RA_INTERVAL); |
| } |
| } |
| |
| void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) |
| { |
| struct bgp *bgp; |
| struct bgp_nexthop_cache *bnc; |
| struct nexthop *nhop; |
| struct interface *ifp; |
| struct prefix p; |
| |
| if (peer->ifp) |
| return; |
| |
| bgp = peer->bgp; |
| |
| if (!sockunion2hostprefix(&peer->su, &p)) { |
| zlog_warn("%s: Unable to convert sockunion to prefix for %s", |
| __func__, peer->host); |
| return; |
| } |
| |
| if (p.family != AF_INET6) |
| return; |
| |
| bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); |
| if (!bnc) |
| return; |
| |
| if (peer != bnc->nht_info) |
| return; |
| |
| for (nhop = bnc->nexthop; nhop; nhop = nhop->next) { |
| ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id); |
| |
| if (!ifp) |
| continue; |
| |
| zclient_send_interface_radv_req(zclient, nhop->vrf_id, ifp, 0, |
| 0); |
| } |
| } |