mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 06:50:17 +00:00
Merge pull request #17435 from opensourcerouting/fix/bgp_allow_0.0.0.0_if_multiple_nlris_exist
bgpd: Validate both nexthop information (NEXTHOP and NLRI)
This commit is contained in:
commit
7c46eb68d4
@ -4459,7 +4459,7 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
|
|||||||
uint8_t type, uint8_t stype, struct attr *attr,
|
uint8_t type, uint8_t stype, struct attr *attr,
|
||||||
struct bgp_dest *dest)
|
struct bgp_dest *dest)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool nh_invalid = false;
|
||||||
bool is_bgp_static_route =
|
bool is_bgp_static_route =
|
||||||
(type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true
|
(type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true
|
||||||
: false;
|
: false;
|
||||||
@ -4481,13 +4481,15 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
|
|||||||
(safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN))
|
(safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* If NEXT_HOP is present, validate it. */
|
/* If NEXT_HOP is present, validate it:
|
||||||
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))) {
|
* The route can have both nexthop + mp_nexthop encoded as multiple NLRIs,
|
||||||
if (attr->nexthop.s_addr == INADDR_ANY ||
|
* and we MUST check if at least one of them is valid.
|
||||||
!ipv4_unicast_valid(&attr->nexthop) ||
|
* E.g.: IPv6 prefix can be with nexthop: 0.0.0.0, and mp_nexthop: fc00::1.
|
||||||
bgp_nexthop_self(bgp, afi, type, stype, attr, dest))
|
*/
|
||||||
return true;
|
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)))
|
||||||
}
|
nh_invalid = (attr->nexthop.s_addr == INADDR_ANY ||
|
||||||
|
!ipv4_unicast_valid(&attr->nexthop) ||
|
||||||
|
bgp_nexthop_self(bgp, afi, type, stype, attr, dest));
|
||||||
|
|
||||||
/* If MP_NEXTHOP is present, validate it. */
|
/* If MP_NEXTHOP is present, validate it. */
|
||||||
/* Note: For IPv6 nexthops, we only validate the global (1st) nexthop;
|
/* Note: For IPv6 nexthops, we only validate the global (1st) nexthop;
|
||||||
@ -4502,39 +4504,31 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
|
|||||||
switch (attr->mp_nexthop_len) {
|
switch (attr->mp_nexthop_len) {
|
||||||
case BGP_ATTR_NHLEN_IPV4:
|
case BGP_ATTR_NHLEN_IPV4:
|
||||||
case BGP_ATTR_NHLEN_VPNV4:
|
case BGP_ATTR_NHLEN_VPNV4:
|
||||||
ret = (attr->mp_nexthop_global_in.s_addr ==
|
nh_invalid = (attr->mp_nexthop_global_in.s_addr == INADDR_ANY ||
|
||||||
INADDR_ANY ||
|
!ipv4_unicast_valid(&attr->mp_nexthop_global_in) ||
|
||||||
!ipv4_unicast_valid(
|
bgp_nexthop_self(bgp, afi, type, stype, attr, dest));
|
||||||
&attr->mp_nexthop_global_in) ||
|
|
||||||
bgp_nexthop_self(bgp, afi, type, stype, attr,
|
|
||||||
dest));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BGP_ATTR_NHLEN_IPV6_GLOBAL:
|
case BGP_ATTR_NHLEN_IPV6_GLOBAL:
|
||||||
case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
|
case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
|
||||||
ret = (IN6_IS_ADDR_UNSPECIFIED(
|
nh_invalid = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
|
||||||
&attr->mp_nexthop_global)
|
IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) ||
|
||||||
|| IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global)
|
IN6_IS_ADDR_MULTICAST(&attr->mp_nexthop_global) ||
|
||||||
|| IN6_IS_ADDR_MULTICAST(
|
bgp_nexthop_self(bgp, afi, type, stype, attr, dest));
|
||||||
&attr->mp_nexthop_global)
|
|
||||||
|| bgp_nexthop_self(bgp, afi, type, stype, attr,
|
|
||||||
dest));
|
|
||||||
break;
|
break;
|
||||||
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
|
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
|
||||||
ret = (IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global)
|
nh_invalid = (IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) ||
|
||||||
|| IN6_IS_ADDR_MULTICAST(
|
IN6_IS_ADDR_MULTICAST(&attr->mp_nexthop_global) ||
|
||||||
&attr->mp_nexthop_global)
|
bgp_nexthop_self(bgp, afi, type, stype, attr, dest));
|
||||||
|| bgp_nexthop_self(bgp, afi, type, stype, attr,
|
|
||||||
dest));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ret = true;
|
nh_invalid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return nh_invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bgp_attr_add_no_export_community(struct attr *attr)
|
static void bgp_attr_add_no_export_community(struct attr *attr)
|
||||||
|
0
tests/topotests/bgp_invalid_nexthop/__init__.py
Normal file
0
tests/topotests/bgp_invalid_nexthop/__init__.py
Normal file
53
tests/topotests/bgp_invalid_nexthop/exabgp.env
Normal file
53
tests/topotests/bgp_invalid_nexthop/exabgp.env
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[exabgp.api]
|
||||||
|
encoder = text
|
||||||
|
highres = false
|
||||||
|
respawn = false
|
||||||
|
socket = ''
|
||||||
|
|
||||||
|
[exabgp.bgp]
|
||||||
|
openwait = 60
|
||||||
|
|
||||||
|
[exabgp.cache]
|
||||||
|
attributes = true
|
||||||
|
nexthops = true
|
||||||
|
|
||||||
|
[exabgp.daemon]
|
||||||
|
daemonize = true
|
||||||
|
pid = '/var/run/exabgp/exabgp.pid'
|
||||||
|
user = 'exabgp'
|
||||||
|
##daemonize = false
|
||||||
|
|
||||||
|
[exabgp.log]
|
||||||
|
all = false
|
||||||
|
configuration = true
|
||||||
|
daemon = true
|
||||||
|
destination = '/var/log/exabgp.log'
|
||||||
|
enable = true
|
||||||
|
level = INFO
|
||||||
|
message = false
|
||||||
|
network = true
|
||||||
|
packets = false
|
||||||
|
parser = false
|
||||||
|
processes = true
|
||||||
|
reactor = true
|
||||||
|
rib = false
|
||||||
|
routes = false
|
||||||
|
short = false
|
||||||
|
timers = false
|
||||||
|
|
||||||
|
[exabgp.pdb]
|
||||||
|
enable = false
|
||||||
|
|
||||||
|
[exabgp.profile]
|
||||||
|
enable = false
|
||||||
|
file = ''
|
||||||
|
|
||||||
|
[exabgp.reactor]
|
||||||
|
speed = 1.0
|
||||||
|
|
||||||
|
[exabgp.tcp]
|
||||||
|
acl = false
|
||||||
|
bind = ''
|
||||||
|
delay = 0
|
||||||
|
once = false
|
||||||
|
port = 179
|
19
tests/topotests/bgp_invalid_nexthop/peer1/exabgp.cfg
Normal file
19
tests/topotests/bgp_invalid_nexthop/peer1/exabgp.cfg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
neighbor fc00::1 {
|
||||||
|
router-id 10.0.0.2;
|
||||||
|
local-address fc00::2;
|
||||||
|
local-as 65002;
|
||||||
|
peer-as 65001;
|
||||||
|
group-updates false;
|
||||||
|
|
||||||
|
family {
|
||||||
|
ipv4 unicast;
|
||||||
|
ipv6 unicast;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
route 2001:db8:100::/64 {
|
||||||
|
next-hop 0.0.0.0;
|
||||||
|
next-hop fc00::2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
tests/topotests/bgp_invalid_nexthop/r1/frr.conf
Normal file
14
tests/topotests/bgp_invalid_nexthop/r1/frr.conf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
!
|
||||||
|
interface r1-eth0
|
||||||
|
ip address fc00::1/64
|
||||||
|
!
|
||||||
|
router bgp 65001
|
||||||
|
bgp router-id 10.0.0.1
|
||||||
|
no bgp default ipv4-unicast
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor fc00::2 remote-as external
|
||||||
|
neighbor fc00::2 timers 3 10
|
||||||
|
address-family ipv6
|
||||||
|
neighbor fc00::2 activate
|
||||||
|
exit-address-family
|
||||||
|
!
|
@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: ISC
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 by
|
||||||
|
# Donatas Abraitis <donatas@opensourcerouting.org>
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
|
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, get_topogen
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.bgpd]
|
||||||
|
|
||||||
|
|
||||||
|
def build_topo(tgen):
|
||||||
|
r1 = tgen.add_router("r1")
|
||||||
|
peer1 = tgen.add_exabgp_peer("peer1", ip="fc00::2/64", defaultRoute="via fc00::1")
|
||||||
|
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(r1)
|
||||||
|
switch.add_link(peer1)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
tgen = Topogen(build_topo, mod.__name__)
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
for _, (rname, router) in enumerate(tgen.routers().items(), 1):
|
||||||
|
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
|
||||||
|
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
peer = tgen.gears["peer1"]
|
||||||
|
peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(mod):
|
||||||
|
tgen = get_topogen()
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def test_bgp_invalid_nexthop():
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
r1 = tgen.gears["r1"]
|
||||||
|
|
||||||
|
def _bgp_converge():
|
||||||
|
output = json.loads(r1.vtysh_cmd("show bgp ipv6 unicast json"))
|
||||||
|
expected = {
|
||||||
|
"routes": {
|
||||||
|
"2001:db8:100::/64": [
|
||||||
|
{"valid": True, "nexthops": [{"ip": "fc00::2", "afi": "ipv6"}]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topotest.json_cmp(output, expected)
|
||||||
|
|
||||||
|
test_func = functools.partial(_bgp_converge)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
||||||
|
assert result is None, "2001:db8:100::/64 does not have a valid nexthop"
|
||||||
|
|
||||||
|
|
||||||
|
def test_memory_leak():
|
||||||
|
"Run the memory leak test and report results."
|
||||||
|
tgen = get_topogen()
|
||||||
|
if not tgen.is_memleak_enabled():
|
||||||
|
pytest.skip("Memory leak test/report is disabled")
|
||||||
|
|
||||||
|
tgen.report_memory_leaks()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = ["-s"] + sys.argv[1:]
|
||||||
|
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user