diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 860c5fd382..137f0a6b59 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -895,7 +895,10 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) frr_with_privs(&bgpd_privs) { sock = vrf_socket(ainfo->ai_family, 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_INSTANCE_TYPE_VRF ? bgp->name : NULL)); diff --git a/doc/user/Useful_Sysctl_Settings.md b/doc/user/Useful_Sysctl_Settings.md index eaf97b969c..0ebf9119b2 100644 --- a/doc/user/Useful_Sysctl_Settings.md +++ b/doc/user/Useful_Sysctl_Settings.md @@ -56,7 +56,4 @@ net.ipv6.neigh.default.base_reachable_time_ms=14400000 # Use neigh information on selection of nexthop for multipath hops net.ipv4.fib_multipath_use_neigh=1 - -# Allows Apps to Work with VRF -net.ipv4.tcp_l3mdev_accept=1 ``` diff --git a/doc/user/installation.rst b/doc/user/installation.rst index dbd95aca40..f7e45a6231 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -541,20 +541,15 @@ Additional kernel modules are also needed to support MPLS forwarding. :makevar:`VRF forwarding` General information on Linux VRF support can be found in - https://www.kernel.org/doc/Documentation/networking/vrf.txt. Kernel - 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. + https://www.kernel.org/doc/Documentation/networking/vrf.txt. - 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 - 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. + .. seealso:: :ref:`zebra-vrf` Building ^^^^^^^^ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 1539f9a9d1..ae2235820c 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -261,8 +261,6 @@ Link Parameters Commands 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. -.. _zebra-vrf: - 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 Kernel. +.. _zebra-vrf: + Virtual Routing and Forwarding ============================== diff --git a/lib/vrf.c b/lib/vrf.c index d99ec12ba8..185a11664a 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -994,25 +994,50 @@ const char *vrf_get_default_name(void) 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; struct interface *ifp; + struct vrf *vrf; + + if (fd < 0) + return -1; + + if (vrf_id == VRF_UNKNOWN) + return -1; + + /* 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) + 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; + } - if (fd < 0 || name == NULL) - return fd; - /* the device should exist - * otherwise we should return - * case ifname = vrf in netns mode => return - */ - ifp = if_lookup_by_name(name, vrf_id); - if (!ifp) - return fd; #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) - zlog_debug("bind to interface %s failed, errno=%d", name, - errno); + zlog_err("bind to interface %s failed, errno=%d", ifname, + errno); #endif /* SO_BINDTODEVICE */ return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index c79dd99b9a..7ce03079dd 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -251,15 +251,14 @@ extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, 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 - * arguments. + * If ifname is NULL or is equal to the VRF name then bind to a VRF device. + * Otherwise, bind to the specified interface in the specified VRF. * - * name should be the name of the VRF device. vrf_id should be the - * corresponding vrf_id (the ifindex of the device). + * Returns 0 on success and -1 on failure. */ -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 */ extern int vrf_getaddrinfo(const char *node, const char *service, diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 320e6d430c..36605d44f0 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -42,7 +42,6 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger -from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -130,7 +129,6 @@ def setup_module(mod): logger.info("result: " + output) router = tgen.gears["r2"] - adjust_router_l3mdev(tgen, "r2") for cmd in cmds_vrflite: logger.info("cmd to r2: " + cmd.format("r2")) output = router.run(cmd.format("r2")) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 5d97537bd0..c2f85c68c4 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -84,7 +84,6 @@ from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.ltemplate import ltemplateRtrCmd -from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -176,9 +175,6 @@ def ltemplatePreRouterStartHook(): "ip link set dev {0}-cust1 up", ] for rtr in rtrs: - # adjust handling of VRF traffic - adjust_router_l3mdev(tgen, rtr) - for cmd in cmds: cc.doCmd(tgen, rtr, cmd.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", ] for rtr in rtrs: - # adjust handling of VRF traffic - adjust_router_l3mdev(tgen, rtr) - for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr)) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index f7b9bad5fa..75158b127e 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -1,24 +1,4 @@ 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"] for rtr in rtrs: diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py index 92ee8513e1..a17819f747 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -42,7 +42,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen 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. from mininet.topo import Topo @@ -66,6 +66,12 @@ class BGPIPV6RTADVVRFTopo(Topo): def setup_module(mod): "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.start_topology() @@ -84,9 +90,6 @@ def setup_module(mod): for cmd in cmds: 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(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) diff --git a/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py index b7fe0c2ddb..ff1544e4a2 100644 --- a/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py +++ b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py @@ -41,10 +41,7 @@ from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.topotest import iproute2_is_vrf_capable -from lib.common_config import ( - required_linux_kernel_version, - adjust_router_l3mdev, -) +from lib.common_config import required_linux_kernel_version from mininet.topo import Topo @@ -124,9 +121,6 @@ def setup_module(mod): for cmd in cmds: 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(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 5a904423c2..ee7cd6a7af 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -4487,51 +4487,3 @@ def verify_ip_nht(tgen, input_dict): logger.debug("Exiting lib API: verify_ip_nht()") 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 diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py index fa2784ee7e..e1857abc4f 100755 --- a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py +++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py @@ -92,10 +92,7 @@ from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.topotest import iproute2_is_vrf_capable -from lib.common_config import ( - required_linux_kernel_version, - adjust_router_l3mdev, -) +from lib.common_config import required_linux_kernel_version ##################################################### ## @@ -159,6 +156,11 @@ class NetworkTopo(Topo): def setup_module(mod): "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.start_topology() @@ -197,9 +199,6 @@ def setup_module(mod): for cmd in cmds2: 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(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))