Merge pull request #15259 from dmytroshytyi-6WIND/nexthop_resolution

zebra: add LSP entry to nexthop via recursive (part 2)
This commit is contained in:
Russ White 2024-09-10 10:04:08 -04:00 committed by GitHub
commit add56c61dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 1078 additions and 33 deletions

View file

@ -4634,7 +4634,22 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (aspath_get_last_as(attr->aspath) == bgp->as)
do_loop_check = 0;
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
/* When using bgp ipv4 labeled session, the local prefix is
* received by a peer, and finds out that the proposed prefix
* and its next-hop are the same. To avoid a route loop locally,
* no nexthop entry is referenced for that prefix, and the route
* will not be selected.
*
* As it has been done for ipv4-unicast, apply the following fix
* for labeled address families: when the received peer is
* a route reflector, the prefix has to be selected, even if the
* route can not be installed locally.
*/
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) ||
(safi == SAFI_UNICAST && !peer->afc[afi][safi] &&
peer->afc[afi][SAFI_LABELED_UNICAST] &&
CHECK_FLAG(peer->af_flags[afi][SAFI_LABELED_UNICAST],
PEER_FLAG_REFLECTOR_CLIENT)))
bgp_nht_param_prefix = NULL;
else
bgp_nht_param_prefix = p;

View file

@ -815,6 +815,16 @@ Allocated label chunks table can be dumped using the command
range is configured, static label requests that match that
range are not accepted.
FEC nexthop entry resolution over MPLS networks
-----------------------------------------------
The LSP associated with a BGP labeled route is normally restricted to
directly-connected nexthops. If connected nexthops are not available,
the LSP entry will not be installed. This command permits the use of
recursive resolution for LSPs, similar to that available for IP routes.
.. clicmd:: mpls fec nexthop-resolution
.. _zebra-srv6:
Segment-Routing IPv6

View file

@ -713,6 +713,15 @@ struct nexthop *nexthop_next(const struct nexthop *nexthop)
return NULL;
}
struct nexthop *nexthop_next_resolution(const struct nexthop *nexthop,
bool nexthop_resolution)
{
if (nexthop_resolution)
return nexthop_next(nexthop);
/* no resolution attempt */
return nexthop->next;
}
/* Return the next nexthop in the tree that is resolved and active */
struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
{

View file

@ -225,6 +225,8 @@ extern bool nexthop_labels_match(const struct nexthop *nh1,
extern const char *nexthop2str(const struct nexthop *nexthop,
char *str, int size);
extern struct nexthop *nexthop_next(const struct nexthop *nexthop);
extern struct nexthop *nexthop_next_resolution(const struct nexthop *nexthop,
bool nexthop_resolution);
extern struct nexthop *
nexthop_next_active_resolved(const struct nexthop *nexthop);
extern unsigned int nexthop_level(const struct nexthop *nexthop);

View file

@ -0,0 +1,24 @@
!
router bgp 65500
bgp router-id 192.0.2.1
neighbor 192.0.2.3 remote-as 65500
neighbor 192.0.2.3 update-source lo
neighbor 192.0.2.7 remote-as 65500
neighbor 192.0.2.7 ttl-security hops 10
neighbor 192.0.2.7 disable-connected-check
neighbor 192.0.2.7 update-source lo
!
address-family ipv4 unicast
network 192.0.2.1/32
no neighbor 192.0.2.3 activate
neighbor 192.0.2.7 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.3 activate
neighbor 192.0.2.3 route-reflector-client
neighbor 192.0.2.3 next-hop-self force
exit-address-family
!
exit
!

View file

@ -0,0 +1,25 @@
log stdout
!
interface lo
ip ospf passive
exit
!
interface r1-eth0
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.1
network 192.0.2.1/32 area 0.0.0.0
network 192.168.1.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.1
segment-routing on
segment-routing global-block 1000 10000 local-block 32000 32999
segment-routing node-msd 8
segment-routing prefix 192.0.2.1/32 index 11
exit
!

View file

@ -0,0 +1,13 @@
interface lo
ip address 192.0.2.1/32
mpls enable
exit
!
interface r1-eth0
ip address 192.168.1.1/24
mpls enable
link-params
enable
exit-link-params
exit
!

View file

@ -0,0 +1,23 @@
router bgp 65500
bgp router-id 192.0.2.2
neighbor 192.0.2.1 remote-as 65500
neighbor 192.0.2.1 update-source lo
neighbor 192.0.2.3 remote-as 65500
neighbor 192.0.2.3 update-source lo
!
address-family ipv4 unicast
network 192.0.2.2/32
no neighbor 192.0.2.1 activate
no neighbor 192.0.2.3 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.1 activate
neighbor 192.0.2.1 route-reflector-client
neighbor 192.0.2.1 next-hop-self force
neighbor 192.0.2.3 activate
neighbor 192.0.2.3 route-reflector-client
neighbor 192.0.2.3 next-hop-self force
exit-address-family
exit
!

View file

@ -0,0 +1,25 @@
!
interface lo
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
interface r2-eth1
ip router isis 2
isis hello-interval 1
isis hello-multiplier 3
exit
!
router isis 1
is-type level-1
net 49.0000.0007.e901.2223.00
lsp-timers gen-interval 1 refresh-interval 900 max-lifetime 1200
mpls-te on
mpls-te router-address 192.0.2.2
segment-routing on
segment-routing global-block 11000 20000 local-block 36000 36999
segment-routing node-msd 8
segment-routing prefix 192.0.2.2/32 index 22 no-php-flag
exit
!

View file

@ -0,0 +1,32 @@
log stdout
!
interface lo
ip ospf network point-to-point
ip ospf passive
exit
!
interface r2-eth0
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
interface r2-eth1
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.2
network 192.0.2.2/32 area 0.0.0.0
network 192.168.1.0/24 area 0.0.0.0
network 192.168.2.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.2
segment-routing on
segment-routing global-block 1000 10000 local-block 36000 36999
segment-routing node-msd 8
segment-routing prefix 192.0.2.2/32 index 22
exit
!

View file

@ -0,0 +1,16 @@
!
interface lo
ip address 192.0.2.2/32
mpls enable
exit
!
interface r2-eth0
ip address 192.168.1.2/24
mpls enable
exit
!
interface r2-eth1
ip address 192.168.2.2/24
mpls enable
exit
!

View file

@ -0,0 +1,23 @@
router bgp 65500
bgp router-id 192.0.2.3
neighbor 192.0.2.1 remote-as 65500
neighbor 192.0.2.1 update-source lo
neighbor 192.0.2.5 remote-as 65500
neighbor 192.0.2.5 update-source lo
!
address-family ipv4 unicast
network 192.0.2.3/32
no neighbor 192.0.2.1 activate
no neighbor 192.0.2.5 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.1 activate
neighbor 192.0.2.1 route-reflector-client
neighbor 192.0.2.1 next-hop-self force
neighbor 192.0.2.5 activate
neighbor 192.0.2.5 route-reflector-client
neighbor 192.0.2.5 next-hop-self force
exit-address-family
exit
!

View file

@ -0,0 +1,25 @@
!
interface lo
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
interface r3-eth1
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
router isis 1
is-type level-1
net 49.0000.0007.e901.3333.00
lsp-timers gen-interval 1 refresh-interval 900 max-lifetime 1200
mpls-te on
mpls-te router-address 192.0.2.3
segment-routing on
segment-routing global-block 11000 12000 local-block 36000 36999
segment-routing node-msd 8
segment-routing prefix 192.0.2.3/32 index 33
exit
!

View file

@ -0,0 +1,26 @@
log stdout
!
interface lo
ip ospf network point-to-point
ip ospf passive
exit
!
interface r3-eth0
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.3
network 192.0.2.3/32 area 0.0.0.0
network 192.168.2.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.3
segment-routing on
segment-routing global-block 1000 10000 local-block 30000 30999
segment-routing node-msd 8
segment-routing prefix 192.0.2.3/32 index 33
exit
!

View file

@ -0,0 +1,19 @@
!
interface lo
ip address 192.0.2.3/32
mpls enable
exit
!
interface r3-eth0
ip address 192.168.2.3/24
mpls enable
link-params
enable
exit-link-params
exit
!
interface r3-eth1
ip address 192.168.3.3/24
mpls enable
exit
!

View file

@ -0,0 +1,24 @@
!
router bgp 65500
bgp router-id 192.0.2.4
neighbor 192.0.2.1 remote-as 65500
neighbor 192.0.2.1 ttl-security hops 10
neighbor 192.0.2.1 disable-connected-check
neighbor 192.0.2.1 update-source lo
neighbor 192.0.2.3 remote-as 65500
neighbor 192.0.2.3 update-source lo
!
address-family ipv4 unicast
network 192.0.2.4/32
neighbor 192.0.2.1 activate
no neighbor 192.0.2.3 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.3 activate
neighbor 192.0.2.3 route-reflector-client
neighbor 192.0.2.3 next-hop-self force
exit-address-family
!
exit
!

View file

@ -0,0 +1,31 @@
!
interface lo
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
interface r4-eth0
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
interface r4-eth1
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
router isis 1
is-type level-1
net 49.0000.0007.e901.4444.00
lsp-timers gen-interval 1 refresh-interval 900 max-lifetime 1200
mpls-te on
mpls-te router-address 192.0.2.4
segment-routing on
segment-routing global-block 11000 12000 local-block 37000 37999
segment-routing node-msd 8
segment-routing prefix 192.0.2.4/32 index 44
exit
!

View file

@ -0,0 +1,19 @@
!
interface lo
ip ospf area 0
ip ospf passive
exit
!
interface r4-eth0
ip ospf area 0
exit
!
router ospf
mpls-te on
mpls-te router-address 192.0.2.4
segment-routing on
segment-routing global-block 21000 29000 local-block 31000 31999
segment-routing node-msd 8
segment-routing prefix 192.0.2.4/32 index 44 no-php-flag
exit
!

View file

@ -0,0 +1,16 @@
!
interface lo
ip address 192.0.2.4/32
mpls enable
exit
!
interface r4-eth0
ip address 192.168.3.4/24
mpls enable
exit
!
interface r4-eth1
ip address 192.168.4.4/24
mpls enable
exit
!

View file

@ -0,0 +1,23 @@
router bgp 65500
bgp router-id 192.0.2.5
neighbor 192.0.2.3 remote-as 65500
neighbor 192.0.2.3 update-source lo
neighbor 192.0.2.7 remote-as 65500
neighbor 192.0.2.7 update-source lo
!
address-family ipv4 unicast
network 192.0.2.5/32
no neighbor 192.0.2.3 activate
no neighbor 192.0.2.7 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.3 activate
neighbor 192.0.2.3 route-reflector-client
neighbor 192.0.2.3 next-hop-self force
neighbor 192.0.2.7 activate
neighbor 192.0.2.7 route-reflector-client
neighbor 192.0.2.7 next-hop-self force
exit-address-family
exit
!

View file

@ -0,0 +1,26 @@
!
interface lo
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
isis passive
exit
!
interface r5-eth0
ip router isis 1
isis hello-interval 1
isis hello-multiplier 3
exit
!
router isis 1
is-type level-1
net 49.0000.0007.e901.5555.00
lsp-timers gen-interval 1 refresh-interval 900 max-lifetime 1200
mpls-te on
mpls-te router-address 192.0.2.5
segment-routing on
segment-routing global-block 11000 12000 local-block 33000 33999
segment-routing node-msd 8
segment-routing prefix 192.0.2.5/32 index 55
exit
!

View file

@ -0,0 +1,26 @@
log stdout
!
interface lo
ip ospf network point-to-point
ip ospf passive
exit
!
interface r5-eth1
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.5
network 192.0.2.5/32 area 0.0.0.0
network 192.168.5.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.5
segment-routing on
segment-routing global-block 21000 22000 local-block 35000 35999
segment-routing node-msd 8
segment-routing prefix 192.0.2.5/32 index 55
exit
!

View file

@ -0,0 +1,19 @@
!
interface lo
ip address 192.0.2.5/32
mpls enable
exit
!
interface r5-eth0
ip address 192.168.4.5/24
mpls enable
exit
!
interface r5-eth1
ip address 192.168.5.5/24
mpls enable
link-params
enable
exit-link-params
exit
!

View file

@ -0,0 +1,32 @@
log stdout
!
interface lo
ip ospf network point-to-point
ip ospf passive
exit
!
interface r6-eth0
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
interface r6-eth1
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.6
segment-routing on
segment-routing global-block 21000 22000 local-block 38000 38999
network 192.0.2.6/32 area 0.0.0.0
network 192.168.5.0/24 area 0.0.0.0
network 192.168.6.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.6
segment-routing node-msd 8
segment-routing prefix 192.0.2.6/32 index 66
exit
!

View file

@ -0,0 +1,22 @@
!
interface lo
ip address 192.0.2.6/32
mpls enable
exit
!
interface r6-eth0
ip address 192.168.5.6/24
mpls enable
link-params
enable
exit-link-params
exit
!
interface r6-eth1
ip address 192.168.6.6/24
mpls enable
link-params
enable
exit-link-params
exit
!

View file

@ -0,0 +1,24 @@
!
router bgp 65500
bgp router-id 192.0.2.7
neighbor 192.0.2.1 remote-as 65500
neighbor 192.0.2.1 ttl-security hops 10
neighbor 192.0.2.1 disable-connected-check
neighbor 192.0.2.1 update-source lo
neighbor 192.0.2.5 remote-as 65500
neighbor 192.0.2.5 update-source lo
!
address-family ipv4 unicast
network 192.0.2.7/32
neighbor 192.0.2.1 activate
no neighbor 192.0.2.5 activate
exit-address-family
!
address-family ipv4 labeled-unicast
neighbor 192.0.2.5 activate
neighbor 192.0.2.5 route-reflector-client
neighbor 192.0.2.5 next-hop-self force
exit-address-family
!
exit
!

View file

@ -0,0 +1,26 @@
log stdout
!
interface lo
ip ospf network point-to-point
ip ospf passive
exit
!
interface r7-eth0
ip ospf network point-to-point
ip ospf hello-interval 1
exit
!
router ospf
ospf router-id 192.0.2.7
network 192.0.2.7/32 area 0.0.0.0
network 192.168.6.0/24 area 0.0.0.0
passive-interface lo
capability opaque
mpls-te on
mpls-te router-address 192.0.2.7
segment-routing on
segment-routing global-block 21000 22000 local-block 31000 31999
segment-routing node-msd 8
segment-routing prefix 192.0.2.7/32 index 77
exit
!

View file

@ -0,0 +1,14 @@
!
interface lo
ip address 192.0.2.7/32
mpls enable
exit
!
interface r7-eth0
ip address 192.168.6.7/24
mpls enable
link-params
enable
exit-link-params
exit
!

View file

@ -0,0 +1,259 @@
#!/usr/bin/env python
#
# Copyright 2022 6WIND S.A.
#
# 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.
#
"""
Check if fec nexthop resolution works correctly.
"""
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 ---- r2 ---- r3 ---- r4 ----- r5 ---- r6 ---- r7
<--- ospf ----> <---- isis -----> <--- ospf ---->
"""
for routern in range(1, 8):
tgen.add_router("r{}".format(routern))
switch1 = tgen.add_switch("s1")
switch1.add_link(tgen.gears["r1"])
switch1.add_link(tgen.gears["r2"])
switch2 = tgen.add_switch("s2")
switch2.add_link(tgen.gears["r2"])
switch2.add_link(tgen.gears["r3"])
switch3 = tgen.add_switch("s3")
switch3.add_link(tgen.gears["r3"])
switch3.add_link(tgen.gears["r4"])
switch4 = tgen.add_switch("s4")
switch4.add_link(tgen.gears["r4"])
switch4.add_link(tgen.gears["r5"])
switch5 = tgen.add_switch("s5")
switch5.add_link(tgen.gears["r5"])
switch5.add_link(tgen.gears["r6"])
switch6 = tgen.add_switch("s6")
switch6.add_link(tgen.gears["r6"])
switch6.add_link(tgen.gears["r7"])
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
def _enable_mpls_misc(router):
router.run("modprobe mpls_router")
router.run("echo 100000 > /proc/sys/net/mpls/platform_labels")
router.run("echo 1 > /proc/sys/net/mpls/conf/lo/input")
router = tgen.gears["r1"]
_enable_mpls_misc(router)
router = tgen.gears["r2"]
_enable_mpls_misc(router)
router = tgen.gears["r3"]
_enable_mpls_misc(router)
router = tgen.gears["r4"]
_enable_mpls_misc(router)
router = tgen.gears["r5"]
_enable_mpls_misc(router)
router = tgen.gears["r6"]
_enable_mpls_misc(router)
router = tgen.gears["r7"]
_enable_mpls_misc(router)
for i, (rname, router) in enumerate(router_list.items(), 1):
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
if rname in ("r1", "r3", "r5", "r7"):
router.load_config(
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
)
if rname in ("r3", "r4", "r5"):
router.load_config(
TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
)
if rname in ("r1", "r2", "r3", "r5", "r6", "r7"):
router.load_config(
TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
# There are some startup issued when initialising OSPF
# To avoid those issues, load the ospf configuration after zebra started
def test_zebra_fec_nexthop_resolution_finalise_ospf_config():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
topotest.sleep(2)
tgen.net["r1"].cmd("vtysh -f {}/r1/ospfd.conf.after".format(CWD))
tgen.net["r2"].cmd("vtysh -f {}/r2/ospfd.conf.after".format(CWD))
tgen.net["r3"].cmd("vtysh -f {}/r3/ospfd.conf.after".format(CWD))
tgen.net["r5"].cmd("vtysh -f {}/r5/ospfd.conf.after".format(CWD))
tgen.net["r6"].cmd("vtysh -f {}/r6/ospfd.conf.after".format(CWD))
tgen.net["r7"].cmd("vtysh -f {}/r7/ospfd.conf.after".format(CWD))
def test_zebra_fec_nexthop_resolution_bgp():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
def _check_bgp_session():
r1 = tgen.gears["r1"]
tgen.gears["r3"].vtysh_cmd("config \n no mpls fec nexthop-resolution \n end")
tgen.gears["r3"].vtysh_cmd("config \n mpls fec nexthop-resolution \n end")
tgen.gears["r5"].vtysh_cmd("config \n no mpls fec nexthop-resolution \n end")
tgen.gears["r5"].vtysh_cmd("config \n mpls fec nexthop-resolution \n end")
output = json.loads(r1.vtysh_cmd("show bgp summary json"))
if output["ipv4Unicast"]["peers"]["192.0.2.7"]["state"] == "Established":
return None
return False
test_func1 = functools.partial(_check_bgp_session)
_, result1 = topotest.run_and_expect(test_func1, None, count=60, wait=0.5)
assert result1 is None, "Failed to verify the fec_nexthop_resolution: bgp session"
def test_zebra_fec_nexthop_resolution_ping():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
def _check_ping_launch():
r1 = tgen.gears["r1"]
ping_launch = "ping 192.0.2.7 -I 192.0.2.1 -c 1"
selected_lines = r1.run(ping_launch).splitlines()[-2:-1]
rtx_stats = "".join(selected_lines[0].split(",")[0:3])
current = topotest.normalize_text(rtx_stats)
expected_stats = "1 packets transmitted 1 received 0% packet loss"
expected = topotest.normalize_text(expected_stats)
if current == expected:
return None
return False
test_func2 = functools.partial(_check_ping_launch)
_, result2 = topotest.run_and_expect(test_func2, None, count=60, wait=1)
assert result2 is None, "Failed to verify the fec_nexthop_resolution: ping"
def test_zebra_fec_nexthop_resolution_table():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
def _zebra_check_mpls_table():
r3 = tgen.gears["r3"]
inLabel = 0
outLabels = 0
"""
Retrieve inLabel from MPLS FEC table
"""
mpls_fec = r3.vtysh_cmd("show mpls fec 192.0.2.7/32")
lines = mpls_fec.split("\n")
for line in lines:
if "Label" in line:
inLabel = line.split(": ", 1)[1]
"""
Retrieve outLabel from BGP
"""
output = json.loads(r3.vtysh_cmd("show ip route 192.0.2.7/32 json"))
outLabels = output["192.0.2.7/32"][0]["nexthops"][1]["labels"]
if (inLabel == 0) or (outLabels == 0):
return True
"""
Compare expected data with real data
"""
output = json.loads(r3.vtysh_cmd("show mpls table " + str(inLabel) + " json"))
expected = {
"inLabel": int(inLabel),
"installed": True,
"nexthops": [
{
"type": "BGP",
"outLabel": outLabels[0],
"outLabelStack": outLabels,
"distance": 20,
"installed": True,
"nexthop": "192.168.3.4",
}
],
}
return topotest.json_cmp(output, expected)
test_func3 = functools.partial(_zebra_check_mpls_table)
_, result3 = topotest.run_and_expect(test_func3, None, count=60, wait=0.5)
assert result3 is None, "Failed to verify the fec_nexthop_resolution: mpls table"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View file

@ -2856,6 +2856,16 @@ module frr-zebra {
}
}
container mpls {
description
"MPLS Configuration.";
leaf fec-nexthop-resolution {
type boolean;
description
"Authorise nexthop resolution over all labeled routes.";
}
}
uses ribs;
uses vrf-vni-mapping;

View file

@ -108,8 +108,8 @@ struct route_entry {
uint32_t nexthop_mtu;
/* Flags of this route.
* This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed
* to clients via Zserv
* This flag's definition is in lib/zclient.h ZEBRA_FLAG_* and is
* exposed to clients via Zserv
*/
uint32_t flags;

View file

@ -2221,6 +2221,37 @@ static void lib_vrf_zebra_ipv6_resolve_via_default_cli_write(
}
}
DEFPY_YANG (mpls_fec_nexthop_resolution, mpls_fec_nexthop_resolution_cmd,
"[no$no] mpls fec nexthop-resolution",
NO_STR
MPLS_STR
"MPLS FEC table\n"
"Authorise nexthop resolution over all labeled routes.\n")
{
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/mpls/fec-nexthop-resolution",
NB_OP_MODIFY, no ? "false" : "true");
if (vty->node == CONFIG_NODE)
return nb_cli_apply_changes(vty, "/frr-vrf:lib/vrf[name='%s']",
VRF_DEFAULT_NAME);
return nb_cli_apply_changes(vty, NULL);
}
static void lib_vrf_mpls_fec_nexthop_resolution_cli_write(
struct vty *vty, const struct lyd_node *dnode, bool show_defaults)
{
bool fec_nexthop_resolution = yang_dnode_get_bool(dnode, NULL);
if (fec_nexthop_resolution || show_defaults) {
zebra_vrf_indent_cli_write(vty, dnode);
vty_out(vty, "%smpls fec nexthop-resolution\n",
fec_nexthop_resolution ? "" : "no ");
}
}
DEFPY_YANG (vrf_netns,
vrf_netns_cmd,
"[no] netns ![NAME$netns_name]",
@ -2851,6 +2882,10 @@ const struct frr_yang_module_info frr_zebra_cli_info = {
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/netns/table-range",
.cbs.cli_show = lib_vrf_zebra_netns_table_range_cli_write,
},
{
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/mpls/fec-nexthop-resolution",
.cbs.cli_show = lib_vrf_mpls_fec_nexthop_resolution_cli_write,
},
{
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id",
.cbs.cli_show = lib_vrf_zebra_l3vni_id_cli_write,
@ -2957,6 +2992,9 @@ void zebra_cli_init(void)
install_element(VRF_NODE, &ip_nht_default_route_cmd);
install_element(VRF_NODE, &ipv6_nht_default_route_cmd);
install_element(CONFIG_NODE, &mpls_fec_nexthop_resolution_cmd);
install_element(VRF_NODE, &mpls_fec_nexthop_resolution_cmd);
install_element(CONFIG_NODE, &vni_mapping_cmd);
install_element(VRF_NODE, &vni_mapping_cmd);

View file

@ -37,6 +37,7 @@
DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object");
DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC object");
DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object");
DEFINE_MTYPE_STATIC(ZEBRA, NH_LABEL, "Nexthop label");
bool mpls_enabled;
bool mpls_pw_reach_strict; /* Strict reachability checking */
@ -50,7 +51,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
struct route_node *rn, struct route_entry *re);
static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label);
static int fec_change_update_lsp(struct zebra_vrf *zvrf, struct zebra_fec *fec,
mpls_label_t old_label);
mpls_label_t old_label, bool uninstall);
static int fec_send(struct zebra_fec *fec, struct zserv *client);
static void fec_update_clients(struct zebra_fec *fec);
static void fec_print(struct zebra_fec *fec, struct vty *vty);
@ -161,12 +162,14 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
enum lsp_types_t lsp_type;
char buf[BUFSIZ];
int added, changed;
bool zvrf_nexthop_resolution;
/* Lookup table. */
lsp_table = zvrf->lsp_table;
if (!lsp_table)
return -1;
zvrf_nexthop_resolution = zvrf->zebra_mpls_fec_nexthop_resolution;
lsp_type = lsp_type_from_re_type(re->type);
added = changed = 0;
@ -180,13 +183,20 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
* the label advertised by the recursive nexthop (plus we don't have the
* logic yet to push multiple labels).
*/
for (nexthop = re->nhe->nhg.nexthop;
nexthop; nexthop = nexthop->next) {
/* Skip inactive and recursive entries. */
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
nexthop = re->nhe->nhg.nexthop;
while (nexthop) {
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
nexthop =
nexthop_next_resolution(nexthop,
zvrf_nexthop_resolution);
continue;
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
}
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) {
nexthop =
nexthop_next_resolution(nexthop,
zvrf_nexthop_resolution);
continue;
}
nhlfe = nhlfe_find(&lsp->nhlfe_list, lsp_type,
nexthop->type, &nexthop->gate,
@ -194,9 +204,13 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
if (nhlfe) {
/* Clear deleted flag (in case it was set) */
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED);
if (nexthop_labels_match(nhlfe->nexthop, nexthop))
if (nexthop_labels_match(nhlfe->nexthop, nexthop)) {
/* No change */
nexthop =
nexthop_next_resolution(nexthop,
zvrf_nexthop_resolution);
continue;
}
if (IS_ZEBRA_DEBUG_MPLS) {
@ -221,11 +235,18 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
return -1;
if (IS_ZEBRA_DEBUG_MPLS) {
char label_str[MPLS_LABEL_STRLEN];
nhlfe2str(nhlfe, buf, BUFSIZ);
zlog_debug(
"Add LSP in-label %u type %d nexthop %s out-label %u",
lsp->ile.in_label, lsp_type, buf,
nexthop->nh_label->label[0]);
zlog_debug("Add LSP in-label %u type %d nexthop %s out-label %s",
lsp->ile.in_label, lsp_type, buf,
mpls_label2str(nexthop->nh_label
->num_labels,
nexthop->nh_label->label,
label_str,
sizeof(label_str),
nexthop->nh_label_type,
0));
}
lsp->addr_family = NHLFE_FAMILY(nhlfe);
@ -234,6 +255,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED);
added++;
}
nexthop = nexthop_next_resolution(nexthop,
zvrf_nexthop_resolution);
}
/* Queue LSP for processing if necessary. If no NHLFE got added (special
@ -245,6 +268,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,
return -1;
} else {
lsp_check_free(lsp_table, &lsp);
/* failed to install a new LSP */
return -1;
}
return 0;
@ -353,7 +378,7 @@ static void fec_evaluate(struct zebra_vrf *zvrf)
fec_update_clients(fec);
/* Update label forwarding entries appropriately */
fec_change_update_lsp(zvrf, fec, old_label);
fec_change_update_lsp(zvrf, fec, old_label, false);
}
}
}
@ -384,7 +409,7 @@ static uint32_t fec_derive_label_from_index(struct zebra_vrf *zvrf,
* entries, as appropriate.
*/
static int fec_change_update_lsp(struct zebra_vrf *zvrf, struct zebra_fec *fec,
mpls_label_t old_label)
mpls_label_t old_label, bool uninstall)
{
struct route_table *table;
struct route_node *rn;
@ -416,11 +441,17 @@ static int fec_change_update_lsp(struct zebra_vrf *zvrf, struct zebra_fec *fec,
break;
}
if (!re || !zebra_rib_labeled_unicast(re))
if (!re || !zebra_rib_labeled_unicast(re)) {
if (uninstall)
lsp_uninstall(zvrf, fec->label);
return 0;
}
if (lsp_install(zvrf, fec->label, rn, re))
if (lsp_install(zvrf, fec->label, rn, re)) {
if (uninstall)
lsp_uninstall(zvrf, fec->label);
return -1;
}
return 0;
}
@ -447,6 +478,30 @@ static int fec_send(struct zebra_fec *fec, struct zserv *client)
return zserv_send_message(client, s);
}
/*
* Upon reconfiguring nexthop-resolution updates, update the
* lsp entries accordingly.
*/
void zebra_mpls_fec_nexthop_resolution_update(struct zebra_vrf *zvrf)
{
int af;
struct route_node *rn;
struct zebra_fec *fec;
for (af = AFI_IP; af < AFI_MAX; af++) {
if (zvrf->fec_table[af] == NULL)
continue;
for (rn = route_top(zvrf->fec_table[af]); rn;
rn = route_next(rn)) {
if (!rn->info)
continue;
fec = rn->info;
fec_change_update_lsp(zvrf, fec, MPLS_INVALID_LABEL,
true);
}
}
}
/*
* Update all registered clients about this FEC. Caller should've updated
* FEC and ensure no duplicate updates.
@ -1398,7 +1453,31 @@ static int nhlfe_del(struct zebra_nhlfe *nhlfe)
static void nhlfe_out_label_update(struct zebra_nhlfe *nhlfe,
struct mpls_label_stack *nh_label)
{
nhlfe->nexthop->nh_label->label[0] = nh_label->label[0];
struct mpls_label_stack *nh_label_tmp;
int i;
/* Enforce limit on label stack size */
if (nh_label->num_labels > MPLS_MAX_LABELS)
nh_label->num_labels = MPLS_MAX_LABELS;
/* Resize the array to accommodate the new label stack */
if (nh_label->num_labels > nhlfe->nexthop->nh_label->num_labels) {
nh_label_tmp = XREALLOC(MTYPE_NH_LABEL, nhlfe->nexthop->nh_label,
sizeof(struct mpls_label_stack) +
nh_label->num_labels *
sizeof(mpls_label_t));
if (nh_label_tmp) {
nhlfe->nexthop->nh_label = nh_label_tmp;
nhlfe->nexthop->nh_label->num_labels =
nh_label->num_labels;
} else
nh_label->num_labels =
nhlfe->nexthop->nh_label->num_labels;
}
/* Copy the label stack into the array */
for (i = 0; i < nh_label->num_labels; i++)
nhlfe->nexthop->nh_label->label[i] = nh_label->label[i];
}
static int mpls_lsp_uninstall_all(struct hash *lsp_table, struct zebra_lsp *lsp,
@ -2117,7 +2196,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)
/*
* Install dynamic LSP entry.
*/
int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
void zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
struct route_entry *re)
{
struct route_table *table;
@ -2125,23 +2204,20 @@ int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))];
if (!table)
return -1;
return;
/* See if there is a configured label binding for this FEC. */
fec = fec_find(table, &rn->p);
if (!fec || fec->label == MPLS_INVALID_LABEL)
return 0;
return;
/* We cannot install a label forwarding entry if local label is the
* implicit-null label.
*/
if (fec->label == MPLS_LABEL_IMPLICIT_NULL)
return 0;
return;
if (lsp_install(zvrf, fec->label, rn, re))
return -1;
return 0;
lsp_install(zvrf, fec->label, rn, re);
}
/*
@ -2345,7 +2421,7 @@ int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p,
}
if (new_client || label_change)
return fec_change_update_lsp(zvrf, fec, old_label);
return fec_change_update_lsp(zvrf, fec, old_label, false);
return 0;
}
@ -2386,7 +2462,7 @@ int zebra_mpls_fec_unregister(struct zebra_vrf *zvrf, struct prefix *p,
list_isempty(fec->client_list)) {
mpls_label_t old_label = fec->label;
fec->label = MPLS_INVALID_LABEL; /* reset */
fec_change_update_lsp(zvrf, fec, old_label);
fec_change_update_lsp(zvrf, fec, old_label, false);
fec_del(fec);
}
@ -2556,7 +2632,7 @@ int zebra_mpls_static_fec_add(struct zebra_vrf *zvrf, struct prefix *p,
fec_update_clients(fec);
/* Update label forwarding entries appropriately */
ret = fec_change_update_lsp(zvrf, fec, old_label);
ret = fec_change_update_lsp(zvrf, fec, old_label, false);
}
return ret;
@ -2609,7 +2685,7 @@ int zebra_mpls_static_fec_del(struct zebra_vrf *zvrf, struct prefix *p)
fec_update_clients(fec);
/* Update label forwarding entries appropriately */
return fec_change_update_lsp(zvrf, fec, old_label);
return fec_change_update_lsp(zvrf, fec, old_label, false);
}
/*

View file

@ -146,7 +146,7 @@ int zebra_mpls_write_label_block_config(struct vty *vty, struct zebra_vrf *vrf);
/*
* Install dynamic LSP entry.
*/
int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
void zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
struct route_entry *re);
/*
@ -256,6 +256,12 @@ void mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf,
void zebra_mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf,
const struct zapi_labels *zl);
/*
* Upon reconfiguring nexthop-resolution updates, update the
* lsp entries accordingly.
*/
void zebra_mpls_fec_nexthop_resolution_update(struct zebra_vrf *zvrf);
/*
* Uninstall all NHLFEs bound to a single FEC.
*

View file

@ -883,6 +883,13 @@ const struct frr_yang_module_info frr_zebra_info = {
.modify = lib_vrf_zebra_netns_table_range_end_modify,
}
},
{
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/mpls/fec-nexthop-resolution",
.cbs = {
.modify = lib_vrf_zebra_mpls_fec_nexthop_resolution_modify,
.destroy = lib_vrf_zebra_mpls_fec_nexthop_resolution_destroy,
}
},
{
.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib",
.cbs = {

View file

@ -309,6 +309,10 @@ int lib_vrf_zebra_netns_table_range_create(struct nb_cb_create_args *args);
int lib_vrf_zebra_netns_table_range_destroy(struct nb_cb_destroy_args *args);
int lib_vrf_zebra_netns_table_range_start_modify(struct nb_cb_modify_args *args);
int lib_vrf_zebra_netns_table_range_end_modify(struct nb_cb_modify_args *args);
int lib_vrf_zebra_mpls_fec_nexthop_resolution_modify(
struct nb_cb_modify_args *args);
int lib_vrf_zebra_mpls_fec_nexthop_resolution_destroy(
struct nb_cb_destroy_args *args);
const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args);
int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args);
const void *

View file

@ -3780,6 +3780,59 @@ int lib_vrf_zebra_netns_table_range_end_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
/*
* XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/mpls/fec-nexthop-resolution
*/
int lib_vrf_zebra_mpls_fec_nexthop_resolution_modify(
struct nb_cb_modify_args *args)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
bool fec_nexthop_resolution;
if (args->event != NB_EV_APPLY)
return NB_OK;
vrf = nb_running_get_entry(args->dnode, NULL, true);
zvrf = vrf->info;
fec_nexthop_resolution = yang_dnode_get_bool(args->dnode, NULL);
if (zvrf->zebra_mpls_fec_nexthop_resolution == fec_nexthop_resolution)
return NB_OK;
zvrf->zebra_mpls_fec_nexthop_resolution = fec_nexthop_resolution;
zebra_mpls_fec_nexthop_resolution_update(zvrf);
return NB_OK;
}
int lib_vrf_zebra_mpls_fec_nexthop_resolution_destroy(
struct nb_cb_destroy_args *args)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
bool fec_nexthop_resolution;
if (args->event != NB_EV_APPLY)
return NB_OK;
vrf = nb_running_get_entry(args->dnode, NULL, true);
zvrf = vrf->info;
fec_nexthop_resolution = DFLT_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT;
if (zvrf->zebra_mpls_fec_nexthop_resolution == fec_nexthop_resolution)
return NB_OK;
zvrf->zebra_mpls_fec_nexthop_resolution = fec_nexthop_resolution;
zebra_mpls_fec_nexthop_resolution_update(zvrf);
return NB_OK;
}
/*
* XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id
*/

View file

@ -651,8 +651,10 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id)
int zebra_rib_labeled_unicast(struct route_entry *re)
{
struct nexthop *nexthop = NULL;
struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
if (re->type != ZEBRA_ROUTE_BGP)
if ((re->type != ZEBRA_ROUTE_BGP) &&
!zvrf->zebra_mpls_fec_nexthop_resolution)
return 0;
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop))

View file

@ -173,6 +173,7 @@ struct zebra_vrf {
bool zebra_rnh_ip_default_route;
bool zebra_rnh_ipv6_default_route;
bool zebra_mpls_fec_nexthop_resolution;
};
#define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name
#define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name