diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 7a000a49c4..7985839a39 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -141,6 +141,19 @@ LDP Configuration If GTSM is enabled, multi-hop neighbors should have either GTSM disabled individually or configured with an appropriate ttl-security hops distance. +.. clicmd:: disable-establish-hello + + Located under the MPLS address-family interface node. By default, + the ldpd service sends additional LDP multicast hello messages on TCP + session establishment. This should speed up the connection time, so + the "passive" service would receive the "hello" earlier and would "accept" + TCP without waiting for the UDP message on interval. This behaviour can + produce massive traffic with multicast packets if there are a lot of ldpd + services in one local network, essentially each "addition hello" may trigger + attempt for connection and sending "additional hello" from other ldpd + services. This option allows to disable that behaviour. LDP hello multicast + messages would be sent only by interval. + .. clicmd:: neighbor A.B.C.D password PASSWORD The following command located under MPLS router node configures the router diff --git a/docker/ubuntu-ci/Dockerfile b/docker/ubuntu-ci/Dockerfile index 98d025e554..10939f6a8d 100644 --- a/docker/ubuntu-ci/Dockerfile +++ b/docker/ubuntu-ci/Dockerfile @@ -33,6 +33,7 @@ RUN apt update && apt upgrade -y && \ screen \ texinfo \ tmux \ + iptables \ && \ # Protobuf build requirements apt-get install -y \ diff --git a/ldpd/interface.c b/ldpd/interface.c index f0e70cbacc..6fccd4af56 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -63,11 +63,13 @@ if_new(const char *name) iface->ipv4.af = AF_INET; iface->ipv4.iface = iface; iface->ipv4.enabled = 0; + iface->ipv4.disable_establish_hello = 0; /* ipv6 */ iface->ipv6.af = AF_INET6; iface->ipv6.iface = iface; iface->ipv6.enabled = 0; + iface->ipv6.disable_establish_hello = 0; return (iface); } diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h index 5c83d1c56c..196d05c931 100644 --- a/ldpd/ldp_vty.h +++ b/ldpd/ldp_vty.h @@ -24,6 +24,7 @@ int ldp_vty_allow_broken_lsp(struct vty *, const char *); int ldp_vty_address_family (struct vty *, const char *, const char *); int ldp_vty_disc_holdtime(struct vty *, const char *, enum hello_type, long); int ldp_vty_disc_interval(struct vty *, const char *, enum hello_type, long); +int ldp_vty_disable_establish_hello(struct vty *, const char *); int ldp_vty_targeted_hello_accept(struct vty *, const char *, const char *); int ldp_vty_nbr_session_holdtime(struct vty *, const char *, struct in_addr, long); int ldp_vty_af_session_holdtime(struct vty *, const char *, long); diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index e046ae996b..d6c36c35b1 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -122,6 +122,15 @@ DEFPY (ldp_discovery_link_interval, return (ldp_vty_disc_interval(vty, no, HELLO_LINK, interval)); } +DEFPY (ldp_disable_establish_hello, + ldp_disable_establish_hello_cmd, + "[no] disable-establish-hello", + NO_STR + "Disable sending additional LDP hello message on establishing LDP tcp connection\n") +{ + return ldp_vty_disable_establish_hello(vty, no); +} + DEFPY (ldp_discovery_targeted_interval, ldp_discovery_targeted_interval_cmd, "[no] discovery targeted-hello interval (1-65535)$interval", @@ -866,9 +875,11 @@ ldp_vty_init (void) install_element(LDP_IPV4_IFACE_NODE, &ldp_discovery_link_holdtime_cmd); install_element(LDP_IPV4_IFACE_NODE, &ldp_discovery_link_interval_cmd); + install_element(LDP_IPV4_IFACE_NODE, &ldp_disable_establish_hello_cmd); install_element(LDP_IPV6_IFACE_NODE, &ldp_discovery_link_holdtime_cmd); install_element(LDP_IPV6_IFACE_NODE, &ldp_discovery_link_interval_cmd); + install_element(LDP_IPV6_IFACE_NODE, &ldp_disable_establish_hello_cmd); install_element(LDP_L2VPN_NODE, &ldp_bridge_cmd); install_element(LDP_L2VPN_NODE, &ldp_mtu_cmd); diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index ffff67683d..56ad071c82 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -119,6 +119,8 @@ ldp_af_iface_config_write(struct vty *vty, int af) ia->hello_interval != 0) vty_out (vty, " discovery hello interval %u\n", ia->hello_interval); + if (ia->disable_establish_hello) + vty_out (vty, " disable-establish-hello\n"); vty_out (vty, " exit\n"); } @@ -632,6 +634,36 @@ ldp_vty_disc_interval(struct vty *vty, const char *negate, return (CMD_SUCCESS); } +int +ldp_vty_disable_establish_hello(struct vty *vty, + const char *negate) +{ + struct iface *iface; + struct iface_af *ia; + int af; + + switch (vty->node) { + case LDP_IPV4_IFACE_NODE: + case LDP_IPV6_IFACE_NODE: + af = ldp_vty_get_af(vty); + iface = VTY_GET_CONTEXT(iface); + VTY_CHECK_CONTEXT(iface); + + ia = iface_af_get(iface, af); + if (negate) + ia->disable_establish_hello = 0; + else + ia->disable_establish_hello = 1; + + ldp_config_apply(vty, vty_conf); + break; + default: + fatalx("ldp_vty_disable_establish_hello: unexpected node"); + } + + return (CMD_SUCCESS); +} + int ldp_vty_targeted_hello_accept(struct vty *vty, const char *negate, const char *acl_from_str) diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 94f9ada6da..993972dd35 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -1603,6 +1603,7 @@ merge_iface_af(struct iface_af *ia, struct iface_af *xi) } ia->hello_holdtime = xi->hello_holdtime; ia->hello_interval = xi->hello_interval; + ia->disable_establish_hello = xi->disable_establish_hello; } static void diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 700c388724..5a3f37a6ff 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -332,6 +332,7 @@ struct iface_af { struct event *hello_timer; uint16_t hello_holdtime; uint16_t hello_interval; + int disable_establish_hello; }; struct iface_ldp_sync { diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 2596c79481..00a809186b 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -630,8 +630,9 @@ nbr_establish_connection(struct nbr *nbr) * an adjacency as well. */ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree) - send_hello(adj->source.type, adj->source.link.ia, - adj->source.target); + if (!(adj->source.type == HELLO_LINK && adj->source.link.ia->disable_establish_hello)) + send_hello(adj->source.type, adj->source.link.ia, + adj->source.target); if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) { if (errno == EINPROGRESS) { diff --git a/tests/topotests/ldp_establish_hello_topo1/r1/ldpd.conf b/tests/topotests/ldp_establish_hello_topo1/r1/ldpd.conf new file mode 100644 index 0000000000..4375da0c0b --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r1/ldpd.conf @@ -0,0 +1,21 @@ +hostname r1 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 1.1.1.1 + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/r1/zebra.conf b/tests/topotests/ldp_establish_hello_topo1/r1/zebra.conf new file mode 100644 index 0000000000..55b4b0e9c6 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r1 +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to sw0 + ip address 10.0.1.1/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/r2/ldpd.conf b/tests/topotests/ldp_establish_hello_topo1/r2/ldpd.conf new file mode 100644 index 0000000000..cde0e099ea --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r2/ldpd.conf @@ -0,0 +1,21 @@ +hostname r2 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 2.2.2.2 + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/r2/zebra.conf b/tests/topotests/ldp_establish_hello_topo1/r2/zebra.conf new file mode 100644 index 0000000000..bc70122234 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r2/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + ip address 10.0.1.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/r3/ldpd.conf b/tests/topotests/ldp_establish_hello_topo1/r3/ldpd.conf new file mode 100644 index 0000000000..6a0e19d4f9 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r3/ldpd.conf @@ -0,0 +1,21 @@ +hostname r3 +log file ldpd.log +! +! debug mpls ldp zebra +! debug mpls ldp event +! debug mpls ldp errors +! debug mpls ldp messages recv +! debug mpls ldp messages sent +! debug mpls ldp discovery hello recv +! debug mpls ldp discovery hello sent +! +mpls ldp + router-id 3.3.3.3 + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/r3/zebra.conf b/tests/topotests/ldp_establish_hello_topo1/r3/zebra.conf new file mode 100644 index 0000000000..81b82feb92 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/r3/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r3 +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw0 + ip address 10.0.1.3/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.dot b/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.dot new file mode 100644 index 0000000000..a200ca4ef7 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.dot @@ -0,0 +1,51 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-ESTABLISH-HELLO 1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="1.1.1.1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="2.2.2.2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="3.3.3.3", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw0 [ + shape=oval, + label="10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + + r1 -- sw0 [label="r1-eth0"]; + r2 -- sw0 [label="r2-eth0"]; + r3 -- sw0 [label="r3-eth0"]; +} diff --git a/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.py b/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.py new file mode 100644 index 0000000000..6ab9b3d060 --- /dev/null +++ b/tests/topotests/ldp_establish_hello_topo1/test_establish_hello_topo1.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_establish_hello_topo1.py +# +# Copyright (c) 2025 by VyOS Networks +# Andrii Melnychenko (a.melnychenko@vyos.io) +# + +r""" +test_establish_hello_topo1.py: Simple FRR LDP Test + + +-------------+ + | r1 | + | 1.1.1.1 | + +-------------+ + | + | .1 r1-eth0 + | ++---------+ ~~~~~~~~~~~~~ +| r2 | .2 r2-eth0 ~~ sw0 ~~ +| 2.2.2.2 | ------------- ~~ 10.0.1.0/24 ~~ ++---------+ ~~~~~~~~~~~~~ + | + | .3 r3-eth0 + | + +-------------+ + | r3 | + | 3.3.3.3 | + +-------------+ + +""" + +import os +import re +import sys +import pytest +from time import sleep + +from lib.topogen import Topogen, get_topogen + +fatal_error = "" + +pytestmark = [pytest.mark.ldpd] + +def build_topo(tgen): + # Setup Routers + for router in ["r1", "r2", "r3"]: + tgen.add_router(router) + + # Switch + switch = tgen.add_switch("sw0") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + +def setup_module(module): + + thisDir = os.path.dirname(os.path.realpath(__file__)) + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + net = tgen.net + + # Starting Routers + for router in ["r1", "r2", "r3"]: + net[router].loadConf("zebra", "%s/%s/zebra.conf" % (thisDir, router)) + net[router].loadConf("ldpd", "%s/%s/ldpd.conf" % (thisDir, router)) + tgen.gears[router].start() + + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() + + +def teardown_module(module): + + tgen = get_topogen() + tgen.stop_topology() + + +def test_default_behaviour(): + + global fatal_error + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + tgen = get_topogen() + + # Setup counters + tgen.gears["r3"].run(""" + iptables -t filter -A INPUT -s 10.0.1.1 -p udp --dport 646 -j ACCEPT + iptables -t filter -A INPUT -s 10.0.1.2 -p udp --dport 646 -j ACCEPT + iptables -t filter -A OUTPUT -s 10.0.1.3 -p udp --dport 646 -j ACCEPT + """) + + # Setup the LDP service + for router in ["r3", "r2", "r1"]: + tgen.gears[router].vtysh_multicmd([ + "configure terminal", + "mpls ldp", + "address-family ipv4", + f"interface {router}-eth0", + "end"]) + + sleep(7) + + # Get values from counters + output = tgen.gears["r3"].run("iptables -t filter -L -v -n") + + # Disable the LDP service + for router in ["r3", "r2", "r1"]: + tgen.gears[router].vtysh_multicmd([ + "configure terminal", + "mpls ldp", + "address-family ipv4", + f"no interface {router}-eth0", + "end"]) + + # Remove counter + tgen.gears["r3"].run("iptables -t filter -F") + + pattern = r"\n\s+(\d+)" + matches = re.findall(pattern, output) + + # Each router should send at least 2 packets of LDP hello, + # one at the start and one after the "interval"(default 5 sec) + # So, router 10.0.1.1(1.1.1.1) should send only 2 packets + # Router 10.0.1.2(2.2.2.2) sent 2 packets plus 2 packets for each attempt to connect to the 1.1.1.1 - in total 4 + # Router 10.0.1.3(3.3.3.3) sent 2 packets plus 2 packets to the 1.1.1.1 and 4 packets to the 2.2.2.2 - in total 8 + assert matches == ['2', '4', '8'], "Wrong count of the LDP hello messages" + + +def test_disable_establish_hello(): + + global fatal_error + + # Skip if previous fatal error condition is raised + if fatal_error != "": + pytest.skip(fatal_error) + + tgen = get_topogen() + + # Setup counters + tgen.gears["r3"].run(""" + iptables -t filter -A INPUT -s 10.0.1.1 -p udp --dport 646 -j ACCEPT + iptables -t filter -A INPUT -s 10.0.1.2 -p udp --dport 646 -j ACCEPT + iptables -t filter -A OUTPUT -s 10.0.1.3 -p udp --dport 646 -j ACCEPT + """) + + # Setup the LDP service with disable-establish-hello option + for router in ["r3", "r2", "r1"]: + tgen.gears[router].vtysh_multicmd([ + "configure terminal", + "mpls ldp", + "address-family ipv4", + f"interface {router}-eth0", + "disable-establish-hello", + "end"]) + + sleep(7) + + # Get values from counters + output = tgen.gears["r3"].run("iptables -t filter -L -v -n") + + # Disable the LDP service + for router in ["r3", "r2", "r1"]: + tgen.gears[router].vtysh_multicmd([ + "configure terminal", + "mpls ldp", + "address-family ipv4", + f"no interface {router}-eth0", + "end"]) + + # Remove counter + tgen.gears["r3"].run("iptables -t filter -F") + + pattern = r"\n\s+(\d+)" + matches = re.findall(pattern, output) + + # With disabled sending LDP hello message on attempt to establish TCP connection + # Each router should only send 2 packets, at start and after 5 seconds(default interval) + assert matches == ['2', '2', '2'], "Wrong count of the LDP hello messages" + +if __name__ == "__main__": + + # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) + sys.exit(retval)