mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-17 22:55:43 +00:00
Merge pull request #8644 from idryzhov/fix-vrf-bind
lib: fix binding to a vrf
This commit is contained in:
commit
d532d1092e
@ -895,7 +895,10 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
|
|||||||
frr_with_privs(&bgpd_privs) {
|
frr_with_privs(&bgpd_privs) {
|
||||||
sock = vrf_socket(ainfo->ai_family,
|
sock = vrf_socket(ainfo->ai_family,
|
||||||
ainfo->ai_socktype,
|
ainfo->ai_socktype,
|
||||||
ainfo->ai_protocol, bgp->vrf_id,
|
ainfo->ai_protocol,
|
||||||
|
(bgp->inst_type
|
||||||
|
!= BGP_INSTANCE_TYPE_VIEW
|
||||||
|
? bgp->vrf_id : VRF_DEFAULT),
|
||||||
(bgp->inst_type
|
(bgp->inst_type
|
||||||
== BGP_INSTANCE_TYPE_VRF
|
== BGP_INSTANCE_TYPE_VRF
|
||||||
? bgp->name : NULL));
|
? bgp->name : NULL));
|
||||||
|
@ -56,7 +56,4 @@ net.ipv6.neigh.default.base_reachable_time_ms=14400000
|
|||||||
|
|
||||||
# Use neigh information on selection of nexthop for multipath hops
|
# Use neigh information on selection of nexthop for multipath hops
|
||||||
net.ipv4.fib_multipath_use_neigh=1
|
net.ipv4.fib_multipath_use_neigh=1
|
||||||
|
|
||||||
# Allows Apps to Work with VRF
|
|
||||||
net.ipv4.tcp_l3mdev_accept=1
|
|
||||||
```
|
```
|
||||||
|
@ -541,20 +541,15 @@ Additional kernel modules are also needed to support MPLS forwarding.
|
|||||||
|
|
||||||
:makevar:`VRF forwarding`
|
:makevar:`VRF forwarding`
|
||||||
General information on Linux VRF support can be found in
|
General information on Linux VRF support can be found in
|
||||||
https://www.kernel.org/doc/Documentation/networking/vrf.txt. Kernel
|
https://www.kernel.org/doc/Documentation/networking/vrf.txt.
|
||||||
support for VRFs was introduced in 4.3 and improved upon through
|
|
||||||
4.13, which is the version most used in FRR testing (as of June
|
|
||||||
2018). Additional background on using Linux VRFs and kernel specific
|
|
||||||
features can be found in
|
|
||||||
http://schd.ws/hosted_files/ossna2017/fe/vrf-tutorial-oss.pdf.
|
|
||||||
|
|
||||||
A separate BGP TCP socket is opened per VRF.
|
Kernel support for VRFs was introduced in 4.3, but there are known issues
|
||||||
|
in versions up to 4.15 (for IPv4) and 5.0 (for IPv6). The FRR CI system
|
||||||
|
doesn't perform VRF tests on older kernel versions, and VRFs may not work
|
||||||
|
on them. If you experience issues with VRF support, you should upgrade your
|
||||||
|
kernel version.
|
||||||
|
|
||||||
**Important note** as of June 2018, Kernel versions 4.14-4.18 have a
|
.. seealso:: :ref:`zebra-vrf`
|
||||||
known bug where VRF-specific TCP sockets are not properly handled. When
|
|
||||||
running these kernel versions, if unable to establish any VRF BGP
|
|
||||||
adjacencies, downgrade to 4.13. The issue was fixed in 4.14.57, 4.17.9
|
|
||||||
and more recent kernel versions.
|
|
||||||
|
|
||||||
Building
|
Building
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
@ -261,8 +261,6 @@ Link Parameters Commands
|
|||||||
Allows nexthop tracking to resolve via the default route. This is useful
|
Allows nexthop tracking to resolve via the default route. This is useful
|
||||||
when e.g. you want to allow BGP to peer across the default route.
|
when e.g. you want to allow BGP to peer across the default route.
|
||||||
|
|
||||||
.. _zebra-vrf:
|
|
||||||
|
|
||||||
Administrative Distance
|
Administrative Distance
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
@ -338,6 +336,8 @@ work FRR must use the same metric when issuing the replace command.
|
|||||||
Currently FRR only supports Route Replace semantics using the Linux
|
Currently FRR only supports Route Replace semantics using the Linux
|
||||||
Kernel.
|
Kernel.
|
||||||
|
|
||||||
|
.. _zebra-vrf:
|
||||||
|
|
||||||
Virtual Routing and Forwarding
|
Virtual Routing and Forwarding
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
47
lib/vrf.c
47
lib/vrf.c
@ -994,24 +994,49 @@ const char *vrf_get_default_name(void)
|
|||||||
return vrf_default_name;
|
return vrf_default_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vrf_bind(vrf_id_t vrf_id, int fd, const char *name)
|
int vrf_bind(vrf_id_t vrf_id, int fd, const char *ifname)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
|
struct vrf *vrf;
|
||||||
|
|
||||||
if (fd < 0 || name == NULL)
|
if (fd < 0)
|
||||||
return fd;
|
return -1;
|
||||||
/* the device should exist
|
|
||||||
* otherwise we should return
|
if (vrf_id == VRF_UNKNOWN)
|
||||||
* case ifname = vrf in netns mode => return
|
return -1;
|
||||||
*/
|
|
||||||
ifp = if_lookup_by_name(name, vrf_id);
|
/* can't bind to a VRF that doesn't exist */
|
||||||
|
vrf = vrf_lookup_by_id(vrf_id);
|
||||||
|
if (!vrf_is_enabled(vrf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ifname && strcmp(ifname, vrf->name)) {
|
||||||
|
/* binding to a regular interface */
|
||||||
|
|
||||||
|
/* can't bind to an interface that doesn't exist */
|
||||||
|
ifp = if_lookup_by_name(ifname, vrf_id);
|
||||||
if (!ifp)
|
if (!ifp)
|
||||||
return fd;
|
return -1;
|
||||||
|
} else {
|
||||||
|
/* binding to a VRF device */
|
||||||
|
|
||||||
|
/* nothing to do for netns */
|
||||||
|
if (vrf_is_backend_netns())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* nothing to do for default vrf */
|
||||||
|
if (vrf_id == VRF_DEFAULT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ifname = vrf->name;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SO_BINDTODEVICE
|
#ifdef SO_BINDTODEVICE
|
||||||
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1);
|
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
|
||||||
|
strlen(ifname) + 1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
zlog_debug("bind to interface %s failed, errno=%d", name,
|
zlog_err("bind to interface %s failed, errno=%d", ifname,
|
||||||
errno);
|
errno);
|
||||||
#endif /* SO_BINDTODEVICE */
|
#endif /* SO_BINDTODEVICE */
|
||||||
return ret;
|
return ret;
|
||||||
|
11
lib/vrf.h
11
lib/vrf.h
@ -251,15 +251,14 @@ extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
|
|||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Binds a socket to a VRF device.
|
* Binds a socket to an interface (ifname) in a VRF (vrf_id).
|
||||||
*
|
*
|
||||||
* If name is null, the socket is not bound, irrespective of any other
|
* If ifname is NULL or is equal to the VRF name then bind to a VRF device.
|
||||||
* arguments.
|
* Otherwise, bind to the specified interface in the specified VRF.
|
||||||
*
|
*
|
||||||
* name should be the name of the VRF device. vrf_id should be the
|
* Returns 0 on success and -1 on failure.
|
||||||
* corresponding vrf_id (the ifindex of the device).
|
|
||||||
*/
|
*/
|
||||||
extern int vrf_bind(vrf_id_t vrf_id, int fd, const char *name);
|
extern int vrf_bind(vrf_id_t vrf_id, int fd, const char *ifname);
|
||||||
|
|
||||||
/* VRF ioctl operations */
|
/* VRF ioctl operations */
|
||||||
extern int vrf_getaddrinfo(const char *node, const char *service,
|
extern int vrf_getaddrinfo(const char *node, const char *service,
|
||||||
|
@ -42,7 +42,6 @@ sys.path.append(os.path.join(CWD, "../"))
|
|||||||
from lib import topotest
|
from lib import topotest
|
||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.common_config import adjust_router_l3mdev
|
|
||||||
|
|
||||||
# Required to instantiate the topology builder class.
|
# Required to instantiate the topology builder class.
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
@ -130,7 +129,6 @@ def setup_module(mod):
|
|||||||
logger.info("result: " + output)
|
logger.info("result: " + output)
|
||||||
|
|
||||||
router = tgen.gears["r2"]
|
router = tgen.gears["r2"]
|
||||||
adjust_router_l3mdev(tgen, "r2")
|
|
||||||
for cmd in cmds_vrflite:
|
for cmd in cmds_vrflite:
|
||||||
logger.info("cmd to r2: " + cmd.format("r2"))
|
logger.info("cmd to r2: " + cmd.format("r2"))
|
||||||
output = router.run(cmd.format("r2"))
|
output = router.run(cmd.format("r2"))
|
||||||
|
@ -84,7 +84,6 @@ from lib import topotest
|
|||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.ltemplate import ltemplateRtrCmd
|
from lib.ltemplate import ltemplateRtrCmd
|
||||||
from lib.common_config import adjust_router_l3mdev
|
|
||||||
|
|
||||||
# Required to instantiate the topology builder class.
|
# Required to instantiate the topology builder class.
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
@ -176,9 +175,6 @@ def ltemplatePreRouterStartHook():
|
|||||||
"ip link set dev {0}-cust1 up",
|
"ip link set dev {0}-cust1 up",
|
||||||
]
|
]
|
||||||
for rtr in rtrs:
|
for rtr in rtrs:
|
||||||
# adjust handling of VRF traffic
|
|
||||||
adjust_router_l3mdev(tgen, rtr)
|
|
||||||
|
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
cc.doCmd(tgen, rtr, cmd.format(rtr))
|
cc.doCmd(tgen, rtr, cmd.format(rtr))
|
||||||
cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr))
|
cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr))
|
||||||
@ -219,9 +215,6 @@ def ltemplatePreRouterStartHook():
|
|||||||
"ip link set dev {0}-cust2 up",
|
"ip link set dev {0}-cust2 up",
|
||||||
]
|
]
|
||||||
for rtr in rtrs:
|
for rtr in rtrs:
|
||||||
# adjust handling of VRF traffic
|
|
||||||
adjust_router_l3mdev(tgen, rtr)
|
|
||||||
|
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
cc.doCmd(tgen, rtr, cmd.format(rtr))
|
cc.doCmd(tgen, rtr, cmd.format(rtr))
|
||||||
cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr))
|
cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr))
|
||||||
|
@ -1,24 +1,4 @@
|
|||||||
from lib.lutil import luCommand
|
from lib.lutil import luCommand
|
||||||
from lib.common_config import kernel_requires_l3mdev_adjustment
|
|
||||||
|
|
||||||
l3mdev_accept = kernel_requires_l3mdev_adjustment()
|
|
||||||
l3mdev_rtrs = ["r1", "r3", "r4", "ce4"]
|
|
||||||
for rtr in l3mdev_rtrs:
|
|
||||||
luCommand(rtr, "sysctl net.ipv4.tcp_l3mdev_accept", " = \d*", "none", "")
|
|
||||||
found = luLast()
|
|
||||||
luCommand(
|
|
||||||
rtr, "ss -naep", ":179", "pass", "IPv4:bgp, l3mdev{}".format(found.group(0))
|
|
||||||
)
|
|
||||||
luCommand(rtr, "ss -naep", ":.*:179", "pass", "IPv6:bgp")
|
|
||||||
luCommand(
|
|
||||||
rtr,
|
|
||||||
"sysctl net.ipv4.tcp_l3mdev_accept",
|
|
||||||
" = {}".format(l3mdev_accept),
|
|
||||||
"pass",
|
|
||||||
"l3mdev matches expected (real/expected{}/{})".format(
|
|
||||||
found.group(0), l3mdev_accept
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
rtrs = ["r1", "r3", "r4"]
|
rtrs = ["r1", "r3", "r4"]
|
||||||
for rtr in rtrs:
|
for rtr in rtrs:
|
||||||
|
@ -42,7 +42,7 @@ sys.path.append(os.path.join(CWD, "../"))
|
|||||||
from lib import topotest
|
from lib import topotest
|
||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.common_config import adjust_router_l3mdev
|
from lib.common_config import required_linux_kernel_version
|
||||||
|
|
||||||
# Required to instantiate the topology builder class.
|
# Required to instantiate the topology builder class.
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
@ -66,6 +66,12 @@ class BGPIPV6RTADVVRFTopo(Topo):
|
|||||||
|
|
||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
"Sets up the pytest environment"
|
"Sets up the pytest environment"
|
||||||
|
|
||||||
|
# Required linux kernel version for this suite to run.
|
||||||
|
result = required_linux_kernel_version("5.0")
|
||||||
|
if result is not True:
|
||||||
|
pytest.skip("Kernel requirements are not met")
|
||||||
|
|
||||||
tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__)
|
tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__)
|
||||||
tgen.start_topology()
|
tgen.start_topology()
|
||||||
|
|
||||||
@ -84,9 +90,6 @@ def setup_module(mod):
|
|||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
output = tgen.net[rname].cmd(cmd.format(rname))
|
output = tgen.net[rname].cmd(cmd.format(rname))
|
||||||
|
|
||||||
# adjust handling of vrf traffic
|
|
||||||
adjust_router_l3mdev(tgen, rname)
|
|
||||||
|
|
||||||
for rname, router in router_list.items():
|
for rname, router in router_list.items():
|
||||||
router.load_config(
|
router.load_config(
|
||||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||||
|
@ -41,10 +41,7 @@ from lib import topotest
|
|||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.topotest import iproute2_is_vrf_capable
|
from lib.topotest import iproute2_is_vrf_capable
|
||||||
from lib.common_config import (
|
from lib.common_config import required_linux_kernel_version
|
||||||
required_linux_kernel_version,
|
|
||||||
adjust_router_l3mdev,
|
|
||||||
)
|
|
||||||
|
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
|
|
||||||
@ -124,9 +121,6 @@ def setup_module(mod):
|
|||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
output = tgen.net[rname].cmd(cmd.format(rname))
|
output = tgen.net[rname].cmd(cmd.format(rname))
|
||||||
|
|
||||||
# adjust handling of vrf traffic
|
|
||||||
adjust_router_l3mdev(tgen, rname)
|
|
||||||
|
|
||||||
for rname, router in tgen.routers().items():
|
for rname, router in tgen.routers().items():
|
||||||
router.load_config(
|
router.load_config(
|
||||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||||
|
@ -4487,51 +4487,3 @@ def verify_ip_nht(tgen, input_dict):
|
|||||||
|
|
||||||
logger.debug("Exiting lib API: verify_ip_nht()")
|
logger.debug("Exiting lib API: verify_ip_nht()")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def kernel_requires_l3mdev_adjustment():
|
|
||||||
"""
|
|
||||||
Checks if the L3 master device needs to be adjusted to handle VRF traffic
|
|
||||||
based on kernel version.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
1 or 0
|
|
||||||
"""
|
|
||||||
|
|
||||||
if version_cmp(platform.release(), "4.15") >= 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def adjust_router_l3mdev(tgen, router):
|
|
||||||
"""
|
|
||||||
Adjusts a routers L3 master device to handle VRF traffic depending on kernel
|
|
||||||
version.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
* `tgen` : tgen object
|
|
||||||
* `router` : router id to be configured.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
True
|
|
||||||
"""
|
|
||||||
|
|
||||||
l3mdev_accept = kernel_requires_l3mdev_adjustment()
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"router {0}: setting net.ipv4.tcp_l3mdev_accept={1}".format(
|
|
||||||
router, l3mdev_accept
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
output = tgen.net[router].cmd("sysctl -n net.ipv4.tcp_l3mdev_accept")
|
|
||||||
logger.info("router {0}: existing tcp_l3mdev_accept was {1}".format(router, output))
|
|
||||||
|
|
||||||
tgen.net[router].cmd(
|
|
||||||
"sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
@ -92,10 +92,7 @@ from lib import topotest
|
|||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.topotest import iproute2_is_vrf_capable
|
from lib.topotest import iproute2_is_vrf_capable
|
||||||
from lib.common_config import (
|
from lib.common_config import required_linux_kernel_version
|
||||||
required_linux_kernel_version,
|
|
||||||
adjust_router_l3mdev,
|
|
||||||
)
|
|
||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
##
|
##
|
||||||
@ -159,6 +156,11 @@ class NetworkTopo(Topo):
|
|||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
"Sets up the pytest environment"
|
"Sets up the pytest environment"
|
||||||
|
|
||||||
|
# Required linux kernel version for this suite to run.
|
||||||
|
result = required_linux_kernel_version("5.0")
|
||||||
|
if result is not True:
|
||||||
|
pytest.skip("Kernel requirements are not met")
|
||||||
|
|
||||||
tgen = Topogen(NetworkTopo, mod.__name__)
|
tgen = Topogen(NetworkTopo, mod.__name__)
|
||||||
tgen.start_topology()
|
tgen.start_topology()
|
||||||
|
|
||||||
@ -197,9 +199,6 @@ def setup_module(mod):
|
|||||||
for cmd in cmds2:
|
for cmd in cmds2:
|
||||||
output = tgen.net[rname].cmd(cmd.format(rname))
|
output = tgen.net[rname].cmd(cmd.format(rname))
|
||||||
|
|
||||||
# adjust handling of vrf traffic
|
|
||||||
adjust_router_l3mdev(tgen, rname)
|
|
||||||
|
|
||||||
for rname, router in tgen.routers().items():
|
for rname, router in tgen.routers().items():
|
||||||
router.load_config(
|
router.load_config(
|
||||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||||
|
Loading…
Reference in New Issue
Block a user