From 21d49706a0603b119281d1380e57577b91012874 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 15 Nov 2021 09:10:48 +0100 Subject: [PATCH] zebra, topotests: autorise nexthop interface labelled routes When coming from staticd, some nexthop interface based routes should be able to add a label. This helps recursive routes to reuse the label. > interface eth0 > ip address 192.0.2.1/32 > ! > ip route 192.0.2.2/32 eth0 label 55 > ip route 172.31.0.0/24 192.0.2.2 > ip route 172.31.1.0/24 192.0.2.2 The nexthop interface route is a connected route with no next-hop, and the destination packets with DA=192.0.2.2/32 be sent with a broadcast DMAC. This will result in dropping those packets on the receiving router. However, the recursive routes will take the real MAC address of the 192.0.2.2 IP and the label 55 to forge outgoing packets going to the 172.31.0.0 or 172.31.1.0 sub-networks. Fixes: ("7fcb24bbaa91") zebra: reject routes without nexthops Link: https://github.com/FRRouting/frr/pull/10058 Signed-off-by: Philippe Guibert --- tests/topotests/connected_mpls/r1/zebra.conf | 18 ++ tests/topotests/connected_mpls/r2/zebra.conf | 9 + .../connected_mpls/test_connected_mpls.py | 172 ++++++++++++++++++ zebra/zapi_msg.c | 8 +- 4 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 tests/topotests/connected_mpls/r1/zebra.conf create mode 100644 tests/topotests/connected_mpls/r2/zebra.conf create mode 100644 tests/topotests/connected_mpls/test_connected_mpls.py diff --git a/tests/topotests/connected_mpls/r1/zebra.conf b/tests/topotests/connected_mpls/r1/zebra.conf new file mode 100644 index 000000000000..f59e28b5f1d7 --- /dev/null +++ b/tests/topotests/connected_mpls/r1/zebra.conf @@ -0,0 +1,18 @@ +hostname r1 +ip forwarding +ipv6 forwarding + +int r1-eth0 + ip addr 192.168.1.1/24 +! +int r1-eth1 + ip addr 192.168.2.1/32 + mpls enable +! +int lo + ip addr 192.0.2.1/32 +! +ip route 192.168.2.2/32 r1-eth1 label 100 10 +ip route 192.168.3.0/24 192.168.2.2 +! + diff --git a/tests/topotests/connected_mpls/r2/zebra.conf b/tests/topotests/connected_mpls/r2/zebra.conf new file mode 100644 index 000000000000..c51feef9eef2 --- /dev/null +++ b/tests/topotests/connected_mpls/r2/zebra.conf @@ -0,0 +1,9 @@ +hostname r2 +int r2-eth0 + mpls enable + ip addr 192.168.2.2/24 +! +int r2-eth1 + ip addr 192.168.3.2/24 +! + diff --git a/tests/topotests/connected_mpls/test_connected_mpls.py b/tests/topotests/connected_mpls/test_connected_mpls.py new file mode 100644 index 000000000000..7dea17f1f54c --- /dev/null +++ b/tests/topotests/connected_mpls/test_connected_mpls.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_connected_mpls.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" +test_connected_mpls.py: Testing MPLS configuration with mpls connected route +""" + +import os +import re +import sys +import pytest +import json +from functools import partial +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.checkping import check_ping +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +##################################################### +## +## Network Topology Definition +## +##################################################### + +cmds_list_iface = [ + "ip link add link {0}-eth{1} name {0}-eth{1}.{3} type vlan id {3}", + "ip link set dev {0}-eth{1}.{3} up", +] + + +def build_topo(tgen): + "Build function" + + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r2"]) + + +##################################################### +## +## Tests starting +## +##################################################### +def _populate_iface(): + tgen = get_topogen() + tgen.net["r1"].cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") + tgen.net["r2"].cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") + tgen.net["r2"].cmd("ip -f mpls route add 100 dev lo") + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + _populate_iface() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_connected_mpls_route(): + "Test that the static MPLS route can be installed with MPLS label" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Checking that static MPLS route 192.168.2.2/32 is installed with labels on ZEBRA" + ) + assertmsg = "r1, prefix 192.168.2.2/32 not installed as it should be" + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip route 192.168.2.2/32 json")) + for path in output["192.168.2.2/32"]: + assert path["installed"] == True, assertmsg + ": path not installed" + for nh in path["nexthops"]: + assert nh["directlyConnected"] == True, ( + assertmsg + ": nexthop not directly connected" + ) + assert nh["interfaceName"] == "r1-eth1", ( + assertmsg + ": wrong nexthop interface" + ) + assert nh["labels"] == [100], assertmsg + ": wrong nexthop label" + + logger.info( + "Checking that static MPLS route 192.168.2.2/32 is installed with labels on system" + ) + output = tgen.net["r1"].cmd("ip route show 192.168.2.2/32") + assert "encap mpls 100" in output, ( + assertmsg + ": iproute2 has not expected label value" + ) + + # ping does not work because sent MPLS packet is broadcase + # like all connected routes, packets are broadcast + # consequently, on receiving router, broadcast packets received can not be forwarded + # this is the case for MPLS packets. + # logger.info("Checking that ping via 192.168.2.2 is working") + # check_ping("r1", "192.168.2.2", True, 10, 0.5) + + +def test_recursive_mpls_route(): + "Test that a recursive route can re-use the label from the static MPLS route." + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Checking that route 192.168.3.0/24 is installed with labels from recursive route on ZEBRA" + ) + assertmsg = "r1, prefix 192.168.3.0/32 not installed as it should be" + output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip route 192.168.3.0/24 json")) + for path in output["192.168.3.0/24"]: + assert path["installed"] == True, assertmsg + ": path not installed" + for nh in path["nexthops"]: + if "recursive" in nh.keys(): + assert nh["ip"] == "192.168.2.2", assertmsg + ": nexthop not found" + else: + assert nh["interfaceName"] == "r1-eth1", ( + assertmsg + ": wrong nexthop interface" + ) + assert nh["labels"] == [100], assertmsg + ": wrong nexthop label" + + logger.info("Checking that ping via 192.168.3.2 is working") + check_ping("r1", "192.168.3.2", True, 10, 0.5) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 761eafeb1375..d0d33320907a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1787,11 +1787,9 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, } /* Labels for MPLS BGP-LU or Segment Routing or EVPN */ - if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) - && api_nh->type != NEXTHOP_TYPE_IFINDEX - && api_nh->type != NEXTHOP_TYPE_BLACKHOLE - && api_nh->label_num > 0) { - + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && + api_nh->type != NEXTHOP_TYPE_BLACKHOLE && + api_nh->label_num > 0) { /* If label type was passed, use it */ if (api_nh->label_type) label_type = api_nh->label_type;