diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index a1312b97b6..11e872f6b4 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1144,7 +1144,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - api_nh->gate.ipv6 = *nexthop; + if (nexthop) + api_nh->gate.ipv6 = *nexthop; return true; } diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 427349c037..9983911581 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2540,6 +2540,26 @@ the same behavior of using same next-hop and RMAC values. Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC parameters. ++Support with VRF network namespace backend ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to separate overlay networks contained in VXLAN interfaces from +underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for +that. In the latter case, it is necessary to set both bridge and vxlan interface +in the same network namespace, as below example illustrates: + +.. code-block:: shell + + # linux shell + ip netns add vrf1 + ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1 + ip link set dev vxlan101 netns vrf1 + ip netns exec vrf1 ip link set dev lo up + ip netns exec vrf1 brctl addbr bridge101 + ip netns exec vrf1 brctl addif bridge101 vxlan101 + +This makes it possible to separate not only layer 3 networks like VRF-lite networks. +Also, VRF netns based make possible to separate layer 2 networks on separate VRF +instances. .. _bgp-debugging: diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h index 0187c74d88..0ed9dd61d3 100644 --- a/include/linux/net_namespace.h +++ b/include/linux/net_namespace.h @@ -16,6 +16,7 @@ enum { NETNSA_NSID, NETNSA_PID, NETNSA_FD, + NETNSA_TARGET_NSID, __NETNSA_MAX, }; diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 98f359401e..e8d549b4e0 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -379,12 +379,20 @@ struct ns *ns_lookup(ns_id_t ns_id) return ns_lookup_internal(ns_id); } -void ns_walk_func(int (*func)(struct ns *)) +void ns_walk_func(int (*func)(struct ns *, + void *param_in, + void **param_out), + void *param_in, + void **param_out) { struct ns *ns = NULL; + int ret; - RB_FOREACH (ns, ns_head, &ns_tree) - func(ns); + RB_FOREACH (ns, ns_head, &ns_tree) { + ret = func(ns, param_in, param_out); + if (ret == NS_WALK_STOP) + return; + } } const char *ns_get_name(struct ns *ns) @@ -584,9 +592,35 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } +/* if relative link_nsid matches default netns, + * then return default absolute netns value + * otherwise, return NS_UNKNOWN + */ +ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) +{ + struct ns *ns; + + ns = ns_lookup(ns_id_reference); + if (!ns) + return NS_UNKNOWN; + + if (ns->relative_default_ns != link_nsid) + return NS_UNKNOWN; + + ns = ns_get_default(); + assert(ns); + return ns->ns_id; +} + ns_id_t ns_get_default_id(void) { if (default_ns) return default_ns->ns_id; return NS_DEFAULT_INTERNAL; } + +struct ns *ns_get_default(void) +{ + return default_ns; +} + diff --git a/lib/ns.h b/lib/ns.h index 20e0a38e3b..286ff5b295 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -53,6 +53,11 @@ struct ns { /* Identifier, mapped on the NSID value */ ns_id_t internal_ns_id; + /* Identifier, value of NSID of default netns, + * relative value in that local netns + */ + ns_id_t relative_default_ns; + /* Name */ char *name; @@ -120,7 +125,14 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); /* Parse and execute a function on all the NETNS */ -extern void ns_walk_func(int (*func)(struct ns *)); +#define NS_WALK_CONTINUE 0 +#define NS_WALK_STOP 1 + +extern void ns_walk_func(int (*func)(struct ns *, + void *, + void **), + void *param_in, + void **param_out); /* API to get the NETNS name, from the ns pointer */ extern const char *ns_get_name(struct ns *ns); @@ -174,7 +186,9 @@ extern struct ns *ns_lookup_name(const char *name); */ extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid); extern void ns_disable(struct ns *ns); +extern struct ns *ns_get_default(void); #ifdef __cplusplus } diff --git a/lib/vrf.c b/lib/vrf.c index 6e4c983c66..cc7445558c 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -659,7 +659,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id, + ns_id_t rel_def_ns_id) { struct ns *ns = NULL; @@ -706,6 +707,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, } ns = ns_get_created(ns, pathname, ns_id); ns->internal_ns_id = internal_ns_id; + ns->relative_default_ns = rel_def_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -801,7 +803,9 @@ DEFUN_NOSH (vrf_netns, frr_with_privs(vrf_daemon_privs) { ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, NS_UNKNOWN); + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); } return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index 83ed16b48e..a8514d74ed 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -315,7 +315,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name, */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id); + ns_id_t ns_id, ns_id_t rel_def_ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF diff --git a/tests/topotests/bgp_evpn_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf new file mode 100644 index 0000000000..9237682067 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf @@ -0,0 +1,26 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.21 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.41 remote-as 65000 + neighbor 192.168.100.41 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.41 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r1-vrf-101 + bgp router-id 192.168.102.21 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.21/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf new file mode 100644 index 0000000000..f5eaab1953 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf @@ -0,0 +1,22 @@ +log stdout + +hostname r1 +password zebra + +debug zebra vxlan +debug zebra kernel +debug zebra dplane +debug zebra rib +log stdout +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 +! + + diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf new file mode 100644 index 0000000000..6dcacd288d --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf @@ -0,0 +1,27 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.41 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.21 peer-group + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r2-vrf-101 + bgp router-id 192.168.101.41 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.101.41/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf new file mode 100644 index 0000000000..e5f962d254 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf @@ -0,0 +1,18 @@ +log stdout + +hostname r2 +password zebra + +debug zebra vxlan + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py new file mode 100755 index 0000000000..69ef7e9fae --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_evpn.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest +import platform + +# 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.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +l3mdev_accept = 0 +krel = '' + +class BGPEVPNTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + global l3mdev_accept + global krel + + tgen = Topogen(BGPEVPNTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + krel = platform.release() + if topotest.version_cmp(krel, '4.18') < 0: + logger.info('BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format(krel)) + return pytest.skip('Skipping BGP EVPN RT5 NETNS Test. Kernel not supported') + + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + + # create VRF vrf-101 on R1 and R2 + # create loop101 + cmds_vrflite = ['sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add {}-vrf-101 type vrf table 101', + 'ip ru add oif {}-vrf-101 table 101', + 'ip ru add iif {}-vrf-101 table 101', + 'ip link set dev {}-vrf-101 up', + 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add loop101 type dummy', + 'ip link set dev loop101 master {}-vrf-101', + 'ip link set dev loop101 up'] + cmds_netns = ['ip netns add {}-vrf-101', + 'ip link add loop101 type dummy', + 'ip link set dev loop101 netns {}-vrf-101', + 'ip netns exec {}-vrf-101 ip link set dev loop101 up'] + + cmds_r2 = [ # config routing 101 + 'ip link add name bridge-101 up type bridge stp_state 0', + 'ip link set bridge-101 master {}-vrf-101', + 'ip link set dev bridge-101 up', + 'ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41', + 'ip link set dev vxlan-101 master bridge-101', + 'ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off'] + + cmds_r1_netns_method3 = ['ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21', + 'ip link set dev vxlan-{1} netns {0}-vrf-{1}', + 'ip netns exec {0}-vrf-{1} ip li set dev lo up', + 'ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0', + 'ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}', + 'ip netns exec {0}-vrf-{1} ip link set bridge-{1} up', + 'ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up'] + + router = tgen.gears['r1'] + for cmd in cmds_netns: + logger.info('cmd to r1: '+cmd); + output = router.run(cmd.format('r1')) + logger.info('result: '+output); + + router = tgen.gears['r2'] + for cmd in cmds_vrflite: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + for cmd in cmds_r2: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + router = tgen.gears['r1'] + bridge_id = '101' + for cmd in cmds_r1_netns_method3: + logger.info('cmd to r1: '+cmd.format('r1', bridge_id)); + output = router.run(cmd.format('r1', bridge_id)) + logger.info('result: '+output); + router = tgen.gears['r1'] + + for rname, router in router_list.iteritems(): + if rname == 'r1': + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + '--vrfwnetns -o vrf0' + ) + else: + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + cmds_rx_netns = ['ip netns del {}-vrf-101'] + + router = tgen.gears['r1'] + for cmd in cmds_rx_netns: + logger.info('cmd to r1: '+cmd.format('r1')); + output = router.run(cmd.format('r1')) + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + topotest.sleep(4, 'waiting 4 seconds for bgp convergence') + # Check IPv4/IPv6 routing tables. + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn', isjson=False) + logger.info('==== result from show bgp l2vpn evpn') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn route detail', isjson=False) + logger.info('==== result from show bgp l2vpn evpn route detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101 ipv4', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ipv4') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show ip route vrf r1-vrf-101', isjson=False) + logger.info('==== result from show ip route vrf r1-vrf-101') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn vni detail', isjson=False) + logger.info('==== result from show evpn vni detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn next-hops vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn rmac vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)') + output = pingrouter.run('ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK') + +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)) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 5cd3e69299..90a08bbd6c 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -798,8 +798,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } /* Add interface. * We add by index first because in some cases such as the master @@ -848,7 +850,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -1349,9 +1351,10 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); - + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } @@ -1439,7 +1442,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { @@ -1540,7 +1544,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } diff --git a/zebra/interface.c b/zebra/interface.c index b824e313ec..d29f61450e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1242,6 +1242,23 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } +static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) +{ + switch (zif_slave_type) { + case ZEBRA_IF_SLAVE_BRIDGE: + return "Bridge"; + case ZEBRA_IF_SLAVE_VRF: + return "Vrf"; + case ZEBRA_IF_SLAVE_BOND: + return "Bond"; + case ZEBRA_IF_SLAVE_OTHER: + return "Other"; + case ZEBRA_IF_SLAVE_NONE: + return "None"; + } + return "None"; +} + static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { @@ -1469,6 +1486,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); + vty_out(vty, " Interface Slave Type %s\n", + zebra_zifslavetype_2str(zebra_if->zif_slave_type)); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; diff --git a/zebra/main.c b/zebra/main.c index cfc45567d7..2afef46bb2 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -183,7 +183,7 @@ static void sigint(void) vrf_terminate(); rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); zebra_ns_notify_close(); access_list_reset(); @@ -214,7 +214,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 653783f28c..56699c4e83 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -622,32 +622,31 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, return; } -/* - * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) +static int zebra_evpn_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) { - struct zebra_ns *zns; + struct zebra_ns *zns = ns->info; struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_bridge *br; struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_evpn_t *zevpn; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; int found = 0; - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; + assert(br_if); /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) @@ -662,17 +661,101 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, if (zif->brslave_info.br_if != br_if) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { + found = 1; + break; + } + } + if (!found) + return NS_WALK_CONTINUE; + + zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; +} + +/* + * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + zebra_evpn_t **p_zevpn; + zebra_evpn_t *zevpn = NULL; + struct zebra_from_svi_param in_param; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; + + ns_walk_func(zebra_evpn_map_vlan_ns, + (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zebra_evpn_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); + + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { found = 1; break; } } if (!found) - return NULL; + return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vxl->vni); - return zevpn; + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; } /* @@ -682,16 +765,11 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_evpn_t *zevpn; - int found = 0; + zebra_evpn_t *zevpn = NULL; + zebra_evpn_t **p_zevpn; + struct zebra_if *zif; + struct zebra_from_svi_param in_param; if (!br_if) return NULL; @@ -704,8 +782,10 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = 0; + + if (in_param.bridge_vlan_aware) { struct zebra_l2info_vlan *vl; if (!IS_ZEBRA_IF_VLAN(ifp)) @@ -714,37 +794,52 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, zif = ifp->info; assert(zif); vl = &zif->l2info.vl; - vid = vl->vid; + in_param.vid = vl->vid; } + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); + ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* Identify corresponding VLAN interface. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; - if (!tmp_if) + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - if (zif->brslave_info.br_if != br_if) + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; + if (zif->link == in_param->svi_if) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; } } - if (!found) - return NULL; - - zevpn = zebra_evpn_lookup(vxl->vni); - return zevpn; + return NS_WALK_CONTINUE; } /* Map to MAC-VLAN interface corresponding to specified SVI interface. @@ -752,11 +847,10 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; - int found = 0; + struct interface **p_ifp; + struct zebra_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) @@ -771,25 +865,17 @@ struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, zif = br_if->info; assert(zif); + in_param.vid = 0; + in_param.br_if = br_if; + in_param.zif = NULL; + in_param.svi_if = svi_if; + p_ifp = &tmp_if; + /* Identify corresponding VLAN interface. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; - - if (zif->link == svi_if) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + ns_walk_func(zvni_map_to_macvlan_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; } /* @@ -812,6 +898,7 @@ void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) { struct zebra_ns *zns; + struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; @@ -819,7 +906,10 @@ void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) zif = ifp->info; vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -1315,7 +1405,7 @@ void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, } } - zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 3b6a5b21e8..27392ec85c 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -123,6 +123,15 @@ struct zebra_evpn_t_ { struct list *local_es_evi_list; }; +/* for parsing evpn and vni contexts */ +struct zebra_from_svi_param { + struct interface *br_if; + struct interface *svi_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index eb4e4f9a57..75031ddba6 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -249,7 +249,7 @@ static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, struct zebra_ns *zns; *vid = mac->fwd_info.local.vid; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); *ifpP = if_lookup_by_index_per_ns(zns, mac->fwd_info.local.ifindex); } @@ -1610,6 +1610,12 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, { struct zebra_if *zif = ifp->info; bool es_change; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); @@ -1618,6 +1624,7 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, if (!mac->es) { /* if es is set fwd_info is not-relevant/taped-out */ mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; } @@ -2206,6 +2213,12 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, { char buf[ETHER_ADDR_STRLEN]; zebra_mac_t *mac; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { @@ -2225,6 +2238,7 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vlan_id; *macp = mac; diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index 39aaf1fb30..f9ca81445f 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -91,6 +91,7 @@ struct zebra_mac_t_ { union { struct { ifindex_t ifindex; + ns_id_t ns_id; vlanid_t vid; } local; diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 1758c8f96a..417056ecb0 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -54,7 +54,13 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); + assert(zvrf); + zns = zvrf->zns; + assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -73,7 +79,8 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex) + if (br_slave->bridge_ifindex == br_if->ifindex && + br_slave->ns_id == zns->ns_id) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -84,12 +91,14 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns) { struct interface *br_if; /* TODO: Handle change of master */ - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + assert(zns); + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -248,23 +257,32 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex) + ifindex_t bridge_ifindex, + ns_id_t ns_id) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + ns_id_t old_ns_id; + struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); - old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - if (old_bridge_ifindex == bridge_ifindex) + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) return; - zif->brslave_info.bridge_ifindex = bridge_ifindex; + old_bridge_ifindex = zif->brslave_info.bridge_ifindex; + old_ns_id = zif->brslave_info.ns_id; + if (old_bridge_ifindex == bridge_ifindex && + old_ns_id == zif->brslave_info.ns_id) + return; + zif->brslave_info.ns_id = ns_id; + zif->brslave_info.bridge_ifindex = bridge_ifindex; /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info); + zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 2735d915ec..f3b15c7770 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,6 +37,7 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ + ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -81,7 +82,8 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void @@ -101,7 +103,8 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex); + ifindex_t bridge_ifindex, + ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 8de4daf439..79121bb086 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,27 +159,34 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -ns_id_t zebra_ns_id_get(const char *netnspath) +/* fd_param = -1 is ignored. + * netnspath set to null is ignored. + * one of the 2 params is mandatory. netnspath is looked in priority + */ +ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) { int ns_id = -1; struct sockaddr_nl snl; - int fd, sock, ret; + int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath) + if (!netnspath && fd_param == -1) return NS_UNKNOWN; - fd = open(netnspath, O_RDONLY); - if (fd == -1) - return NS_UNKNOWN; - + if (netnspath) { + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + } else if (fd_param != -1) + fd = fd_param; /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -192,7 +199,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } @@ -214,7 +222,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -258,7 +267,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -279,7 +289,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -310,16 +321,18 @@ ns_id_t zebra_ns_id_get(const char *netnspath) } while (len != 0 && ret == 0); } - close(fd); + if (fd_param == -1) + close(fd); close(sock); return return_nsid; } #else -ns_id_t zebra_ns_id_get(const char *netnspath) +ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) { return zebra_ns_id_get_fallback(netnspath); } + #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -355,7 +368,7 @@ ns_id_t zebra_ns_id_get_default(void) return NS_DEFAULT_INTERNAL; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); #else /* HAVE_NETNS */ return NS_DEFAULT_INTERNAL; #endif /* !HAVE_NETNS */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 7a5f6851f4..dd9eab18e0 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,7 +24,7 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); extern ns_id_t zebra_ns_id_get_default(void); #ifdef __cplusplus diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index ec7681bf23..995fa6fb5a 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,13 +72,14 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external; + ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; + struct ns *default_ns; if (netnspath == NULL) return; frr_with_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath); + ns_id = zebra_ns_id_get(netnspath, -1); } if (ns_id == NS_UNKNOWN) return; @@ -97,9 +98,21 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } + + default_ns = ns_get_default(); + + /* force kernel ns_id creation in that new vrf */ + frr_with_privs(&zserv_privs) { + ns_switch_to_netns(netnspath); + ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); + ns_switchback_to_initial(); + } + frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, ns_id); + ns_id_external, + ns_id, + ns_id_relative); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 4e51437337..6462daf687 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -153,20 +153,25 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns) +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - return zebra_ns_disable_internal(zns, false); + zebra_ns_disable_internal(zns, false); + return NS_WALK_CONTINUE; } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns) +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; @@ -175,7 +180,7 @@ int zebra_ns_final_shutdown(struct ns *ns) kernel_terminate(zns, true); - return 0; + return NS_WALK_CONTINUE; } int zebra_ns_init(const char *optional_default_name) @@ -183,12 +188,16 @@ int zebra_ns_init(const char *optional_default_name) struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; + struct ns *ns; frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); + ns = ns_get_default(); + if (ns) + ns->relative_default_ns = ns_id; default_ns = ns_lookup(ns_get_default_id()); if (!default_ns) { diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index dc79a83db0..f7d1f40782 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,9 +67,12 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns); -int zebra_ns_final_shutdown(struct ns *ns); - +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); #ifdef __cplusplus diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 13a58bc34a..61498973e9 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -782,6 +782,43 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } +static int zvni_map_to_svi_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct zebra_l2info_vlan *vl; + struct interface *tmp_if = NULL; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != in_param->br_if) + continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; + + if (vl->vid == in_param->vid) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; + } + } + return NS_WALK_CONTINUE; +} + /* Map to SVI on bridge corresponding to specified VLAN. This can be one * of two cases: * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface @@ -791,15 +828,11 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) */ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vlan *vl; - uint8_t bridge_vlan_aware; - int found = 0; - + struct zebra_from_svi_param in_param; + struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -808,33 +841,19 @@ struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - + in_param.bridge_vlan_aware = br->vlan_aware; /* Check oper status of the SVI. */ - if (!bridge_vlan_aware) + if (!in_param.bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = NULL; + p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != br_if) - continue; - vl = &zif->l2info.vl; - - if (vl->vid == vid) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + ns_walk_func(zvni_map_to_svi_ns, (void *)&in_param, + (void **)p_ifp); + return tmp_if; } static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) @@ -846,18 +865,22 @@ static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) return zebra_evpn_del(zevpn); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zevpn_build_hash_table(void) + +static int zevpn_build_hash_table_zns(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { - struct zebra_ns *zns; + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *ifp; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* Walk VxLAN interfaces and create EVPN hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; zebra_evpn_t *zevpn = NULL; @@ -874,7 +897,15 @@ static void zevpn_build_hash_table(void) vxl = &zif->l2info.vxl; vni = vxl->vni; - + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vni); + continue; + } /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -943,7 +974,7 @@ static void zevpn_build_hash_table(void) zlog_debug( "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; + return NS_WALK_CONTINUE; } if (zevpn->local_vtep_ip.s_addr != @@ -985,6 +1016,19 @@ static void zevpn_build_hash_table(void) } } } + return NS_WALK_CONTINUE; +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ + +static void zevpn_build_hash_table(void) +{ + ns_walk_func(zevpn_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* @@ -1617,14 +1661,22 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, + void *_zl3vni, + void **_pifp) { - struct zebra_ns *zns = NULL; + struct zebra_ns *zns = ns->info; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* loop through all vxlan-interface */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -1639,13 +1691,39 @@ struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) continue; vxl = &zif->l2info.vxl; - if (vxl->vni == zl3vni->vni) { - zl3vni->local_vtep_ip = vxl->vtep_ip; - return ifp; + if (vxl->vni != zl3vni->vni) + continue; + + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vxl->vni); + continue; } + + + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (_pifp) + *_pifp = (void *)ifp; + return NS_WALK_STOP; } - return NULL; + return NS_WALK_CONTINUE; +} + +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct interface **p_ifp; + struct interface *ifp = NULL; + + p_ifp = &ifp; + + ns_walk_func(zl3vni_map_to_vxlan_if_ns, + (void *)zl3vni, (void **)p_ifp); + return ifp; } struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) @@ -3987,11 +4065,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } - zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" No Vrf found for vrf_id: %d", - zevpn->vxlan_if->vrf_id); + zlog_debug(" No Evpn Global Vrf found"); return -1; } @@ -5447,6 +5524,25 @@ stream_failure: return; } +static int macfdb_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + macfdb_read(zns); + return NS_WALK_CONTINUE; +} + +static int neigh_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + neigh_read(zns); + return NS_WALK_CONTINUE; +} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -5499,10 +5595,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) zebra_evpn_gw_macip_add_for_evpn_hash, NULL); /* Read the MAC FDB */ - macfdb_read(zvrf->zns); + ns_walk_func(macfdb_read_ns, NULL, NULL); /* Read neighbors */ - neigh_read(zvrf->zns); + ns_walk_func(neigh_read_ns, NULL, NULL); } else { /* Cleanup VTEPs for all EVPNs - uninstall from * kernel and free entries.