diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7365d53212e0..48fff2188fe0 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -15115,8 +15115,9 @@ static int bgp_distance_unset(struct vty *vty, const char *distance_str, } /* Apply BGP information to distance method. */ -uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, - afi_t afi, safi_t safi, struct bgp *bgp) +bool bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, + afi_t afi, safi_t safi, struct bgp *bgp, + uint8_t *distance) { struct bgp_dest *dest; struct prefix q = {0}; @@ -15127,12 +15128,15 @@ uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, struct bgp_path_info *bpi_ultimate; if (!bgp) - return 0; + return false; - peer = pinfo->peer; + if (pinfo->attr->distance != ZEBRA_EBGP_DISTANCE_DEFAULT || + pinfo->attr->distance != ZEBRA_IBGP_DISTANCE_DEFAULT) { + *distance = pinfo->attr->distance; + return true; + } - if (pinfo->attr->distance) - return pinfo->attr->distance; + peer = pinfo->peer; /* get peer origin to calculate appropriate distance */ if (pinfo->sub_type == BGP_ROUTE_IMPORTED) { @@ -15145,7 +15149,7 @@ uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, */ if (pinfo->sub_type != BGP_ROUTE_AGGREGATE && !sockunion2hostprefix(&peer->su, &q)) - return 0; + return false; dest = bgp_node_match(bgp_distance_table[afi][safi], &q); if (dest) { @@ -15154,11 +15158,15 @@ uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, if (bdistance->access_list) { alist = access_list_lookup(afi, bdistance->access_list); - if (alist - && access_list_apply(alist, p) == FILTER_PERMIT) - return bdistance->distance; - } else - return bdistance->distance; + if (alist && + access_list_apply(alist, p) == FILTER_PERMIT) { + *distance = bdistance->distance; + return true; + } + } else { + *distance = bdistance->distance; + return true; + } } /* Backdoor check. */ @@ -15168,26 +15176,31 @@ uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, bgp_dest_unlock_node(dest); if (bgp_static->backdoor) { - if (bgp->distance_local[afi][safi]) - return bgp->distance_local[afi][safi]; - else - return ZEBRA_IBGP_DISTANCE_DEFAULT; + *distance = (bgp->distance_local[afi][safi]) + ? bgp->distance_local[afi][safi] + : ZEBRA_IBGP_DISTANCE_DEFAULT; + return true; } } if (peer->sort == BGP_PEER_EBGP) { - if (bgp->distance_ebgp[afi][safi]) - return bgp->distance_ebgp[afi][safi]; - return ZEBRA_EBGP_DISTANCE_DEFAULT; + *distance = (bgp->distance_ebgp[afi][safi]) + ? bgp->distance_ebgp[afi][safi] + : ZEBRA_EBGP_DISTANCE_DEFAULT; + return true; } else if (peer->sort == BGP_PEER_IBGP) { - if (bgp->distance_ibgp[afi][safi]) - return bgp->distance_ibgp[afi][safi]; - return ZEBRA_IBGP_DISTANCE_DEFAULT; + *distance = (bgp->distance_ibgp[afi][safi]) + ? bgp->distance_ibgp[afi][safi] + : ZEBRA_IBGP_DISTANCE_DEFAULT; + return true; } else { - if (bgp->distance_local[afi][safi]) - return bgp->distance_local[afi][safi]; - return ZEBRA_IBGP_DISTANCE_DEFAULT; + *distance = (bgp->distance_local[afi][safi]) + ? bgp->distance_local[afi][safi] + : ZEBRA_IBGP_DISTANCE_DEFAULT; + return true; } + + return false; } /* If we enter `distance bgp (1-255) (1-255) (1-255)`, diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index c45dcaa5169d..2c369330a2e6 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -776,9 +776,9 @@ extern void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *path, afi_t afi, safi_t safi); -extern uint8_t bgp_distance_apply(const struct prefix *p, - struct bgp_path_info *path, afi_t afi, - safi_t safi, struct bgp *bgp); +extern bool bgp_distance_apply(const struct prefix *p, + struct bgp_path_info *path, afi_t afi, + safi_t safi, struct bgp *bgp, uint8_t *distance); extern afi_t bgp_node_afi(struct vty *); extern safi_t bgp_node_safi(struct vty *); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index da598993d145..ee326c39da02 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1286,7 +1286,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, int nh_family; unsigned int valid_nh_count = 0; bool allow_recursion = false; - uint8_t distance; + uint8_t distance = ZEBRA_EBGP_DISTANCE_DEFAULT; struct peer *peer; struct bgp_path_info *mpinfo; struct bgp *bgp_orig; @@ -1613,9 +1613,11 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api.tag = tag; } - distance = bgp_distance_apply(p, info, afi, safi, bgp); - if (distance) { + if (bgp_distance_apply(p, info, afi, safi, bgp, &distance)) { SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); + if (!distance) + SET_FLAG(api.flags, + ZEBRA_FLAG_DEFAULT_DISTANCE_OVERRIDE); api.distance = distance; } @@ -1630,9 +1632,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, zlog_debug( "Tx route %s VRF %u %pFX metric %u tag %" ROUTE_TAG_PRI - " count %d nhg %d", + " count %d nhg %d distance %u", is_add ? "add" : "delete", bgp->vrf_id, &api.prefix, - api.metric, api.tag, api.nexthop_num, nhg_id); + api.metric, api.tag, api.nexthop_num, nhg_id, + api.distance); for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; diff --git a/lib/zclient.h b/lib/zclient.h index 53c7038c8874..01f28950df7e 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -533,13 +533,18 @@ struct zapi_route { * offload situation. */ #define ZEBRA_FLAG_OFFLOAD_FAILED 0x200 - /* * This flag lets us know that we think the route entry * received has caused us to be out of sync with the * kernel (NLM_F_APPEND at the very least ) */ #define ZEBRA_FLAG_OUTOFSYNC 0x400 +/* + * This flag tells to override a default distance for + * the routes that has distance set to 0. For example, + * via route-maps `set distance 0`. + */ +#define ZEBRA_FLAG_DEFAULT_DISTANCE_OVERRIDE 0x800 /* The older XXX_MESSAGE flags live here */ uint32_t message; diff --git a/tests/topotests/bgp_distance_zero/__init__.py b/tests/topotests/bgp_distance_zero/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_distance_zero/r1/bgpd.conf b/tests/topotests/bgp_distance_zero/r1/bgpd.conf new file mode 100644 index 000000000000..c829c67af71b --- /dev/null +++ b/tests/topotests/bgp_distance_zero/r1/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + exit-address-family +! diff --git a/tests/topotests/bgp_distance_zero/r1/zebra.conf b/tests/topotests/bgp_distance_zero/r1/zebra.conf new file mode 100644 index 000000000000..b29940f46a31 --- /dev/null +++ b/tests/topotests/bgp_distance_zero/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_distance_zero/r2/bgpd.conf b/tests/topotests/bgp_distance_zero/r2/bgpd.conf new file mode 100644 index 000000000000..b5cf4b79bfb8 --- /dev/null +++ b/tests/topotests/bgp_distance_zero/r2/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65002 + neighbor 192.168.1.1 remote-as external + address-family ipv4 unicast + neighbor 192.168.1.1 route-map r1 in + exit-address-family +! +route-map r1 permit 10 + set distance 0 +exit diff --git a/tests/topotests/bgp_distance_zero/r2/zebra.conf b/tests/topotests/bgp_distance_zero/r2/zebra.conf new file mode 100644 index 000000000000..cffe8273636d --- /dev/null +++ b/tests/topotests/bgp_distance_zero/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_distance_zero/test_bgp_distance_zero.py b/tests/topotests/bgp_distance_zero/test_bgp_distance_zero.py new file mode 100644 index 000000000000..7a98ca12a442 --- /dev/null +++ b/tests/topotests/bgp_distance_zero/test_bgp_distance_zero.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis +# + +""" +Test if distance can be set to zero via route-maps. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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 + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_distance_zero(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_converge_table(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 10.10.10.1/32 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_table) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see distance 0 in BGP table" + + def _bgp_converge_rib(): + output = json.loads(r2.vtysh_cmd("show ip route 10.10.10.1/32 json")) + expected = { + "10.10.10.1/32": [ + {"protocol": "bgp", "distance": 0, "selected": True, "installed": True} + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_rib) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't see distance 0 in RIB" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6c499b77d70a..4450f98b8bd0 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2781,8 +2781,11 @@ static void process_subq_early_route_add(struct zebra_early_route *ere) if (re->distance == 0) { if (same && !zebra_router_notify_on_ack()) re->distance = same->distance; - else - re->distance = route_distance(re->type); + else { + if (!CHECK_FLAG(re->flags, + ZEBRA_FLAG_DEFAULT_DISTANCE_OVERRIDE)) + re->distance = route_distance(re->type); + } } if (re->metric == ROUTE_INSTALLATION_METRIC &&