This adds a number of elementary test for GRE tunneling in a
configuration where overlay and underlay are each in a different VRF.
Several lifetime scenarios are tested, as well as several features, e.g.
tunnel keys and checksums.
Along with this test is added a new library ipip_common, with common
code used across a number of IPIP scenarios to be added next.
Also added is an XML library with description of a GRE network that is
likewise used by several scenarios in the IPIP suite.
Signed-off-by: Petr Machata <petrm(a)mellanox.com>
---
Notes:
Changes from v1 to v2:
- Split out this code to patch of its own.
- Rename TestLibIpIp.py to ipip_common.py.
- Rename ipip-gre-network.xml to ipip_common_topology.xml.
- Replace manual route handling with add_nhs_route() call
recipes/switchdev/ipip-001-gre-hier-basic.py | 197 ++++++++++++++++++++++++++
recipes/switchdev/ipip-001-gre-hier-basic.xml | 12 ++
recipes/switchdev/ipip_common.py | 72 ++++++++++
recipes/switchdev/ipip_common_topology.xml | 58 ++++++++
4 files changed, 339 insertions(+)
create mode 100644 recipes/switchdev/ipip-001-gre-hier-basic.py
create mode 100644 recipes/switchdev/ipip-001-gre-hier-basic.xml
create mode 100644 recipes/switchdev/ipip_common.py
create mode 100644 recipes/switchdev/ipip_common_topology.xml
diff --git a/recipes/switchdev/ipip-001-gre-hier-basic.py
b/recipes/switchdev/ipip-001-gre-hier-basic.py
new file mode 100644
index 0000000..166c5dc
--- /dev/null
+++ b/recipes/switchdev/ipip-001-gre-hier-basic.py
@@ -0,0 +1,197 @@
+"""
+Copyright 2017 Mellanox Technologies. All rights reserved.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+petrm(a)mellanox.com (Petr Machata)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib, vrf, dummy, gre
+from ipip_common import ping_test, encap_route, \
+ add_forward_route, connect_host_ifaces, \
+ test_ip, ipv4, ipv6
+from time import sleep
+import logging
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1.add_nhs_route(ipv4(test_ip(2, 0)), [ipv4(test_ip(1, 1, []))])
+ m1_if1.add_nhs_route(ipv6(test_ip(2, 0)), [ipv6(test_ip(1, 1, []))])
+ m2_if1.add_nhs_route("1.2.3.4/32", [ipv4(test_ip(99, 1, []))])
+
+ vrf_None = None
+ tl = TestLib(ctl, aliases)
+ sw_if1.reset(ip=test_ip(1, 1))
+ sw_if2.reset(ip=test_ip(99,1))
+
+ logging.info("=== Hierarchical configuration")
+ with vrf(sw) as vrf_u, \
+ vrf(sw) as vrf_o:
+ connect_host_ifaces(sw, sw_if1, vrf_o, sw_if2, vrf_u)
+ sw_if1.reset()
+ sw_if2.reset()
+ add_forward_route(sw, vrf_u, "1.2.3.5")
+
+ with encap_route(m2, vrf_None, 1, "gre1", ip=ipv4), \
+ encap_route(m2, vrf_None, 1, "gre1", ip=ipv6):
+ # - Set up encap route before decap route.
+ # - Tear down encap route before decap route.
+ logging.info("--- Eup, Dup, Edown, Ddown")
+ with dummy(sw, vrf_u) as d, \
+ gre(sw, d, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5") as g, \
+ encap_route(sw, vrf_o, 2, g, ip=ipv4), \
+ encap_route(sw, vrf_o, 2, g, ip=ipv6):
+
+ sleep(5)
+ d.set_addresses(["1.2.3.4/32"])
+ sleep(15)
+ ping_test(tl, m1, sw, ipv6(test_ip(2, 33, [])), m1_if1, g,
+ ipv6=True)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g)
+
+ # - Set up decap route before encap route.
+ # - Tear down decap route before encap route.
+ logging.info("--- Dup, Eup, Ddown, Edown")
+ with dummy(sw, vrf_u, ip=["1.2.3.4/32"]) as d, \
+ gre(sw, d, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5") as g:
+
+ with encap_route(sw, vrf_o, 2, g, ip=ipv4), \
+ encap_route(sw, vrf_o, 2, g, ip=ipv6):
+ sleep(15)
+ ping_test(tl, m1, sw, ipv6(test_ip(2, 33, [])), m1_if1, g,
+ ipv6=True)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g)
+
+ d.set_addresses([])
+ g.set_addresses([])
+
+ # - Set up two tunnels and test route replacement.
+ logging.info("--- Route replacement")
+ with dummy(sw, vrf_u, ip=["1.2.3.6/32"]) as d1, \
+ gre(sw, d1, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.6",
+ remote_ip="1.2.3.7") as g1, \
+ dummy(sw, vrf_u, ip=["1.2.3.4/32"]) as d2, \
+ gre(sw, d2, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5") as g2:
+
+ def quick_test(ipv4_fail, ipv6_fail):
+ sleep(5)
+ ping_test(tl, m1, sw, ipv6(test_ip(2, 33, [])), m1_if1, g1,
+ count=25, fail_expected=ipv6_fail, ipv6=True)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g1,
+ count=25, fail_expected=ipv4_fail)
+
+ # Replacing IPv4 route should cause the IPv4 traffic to drop and
+ # not affect the IPv6 one.
+ encap_route(sw, vrf_o, 2, g2, ip=ipv6).do("add")
+ encap_route(sw, vrf_o, 2, g1, ip=ipv4).do("add")
+ quick_test(True, False)
+
+ encap_route(sw, vrf_o, 2, g2, ip=ipv4).do("replace")
+ quick_test(False, False)
+
+ encap_route(sw, vrf_o, 2, g1, ip=ipv4).do("replace")
+ quick_test(True, False)
+
+ encap_route(sw, vrf_o, 2, g2, ip=ipv4).do("replace")
+ quick_test(False, False)
+
+ # And vice versa.
+ encap_route(sw, vrf_o, 2, g1, ip=ipv6).do("replace")
+ quick_test(False, True)
+
+ encap_route(sw, vrf_o, 2, g2, ip=ipv6).do("replace")
+ quick_test(False, False)
+
+ encap_route(sw, vrf_o, 2, g1, ip=ipv6).do("replace")
+ quick_test(False, True)
+
+ encap_route(sw, vrf_o, 2, g2, ip=ipv6).do("replace")
+ quick_test(False, False)
+
+ # Done.
+ encap_route(sw, vrf_o, 2, g2, ip=ipv4).do("del")
+ encap_route(sw, vrf_o, 2, g2, ip=ipv6).do("del")
+
+ with dummy(sw, vrf_u, ip=["1.2.3.4/32"]) as d:
+
+ # - Test with ikey/okey.
+ logging.info("--- ikey/okey")
+ with encap_route(m2, vrf_None, 1, "gre2"), \
+ gre(sw, d, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5",
+ ikey=2222, okey=1111) as g, \
+ encap_route(sw, vrf_o, 2, g):
+
+ sleep(15)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g)
+
+ # - Slow path: non-inherit TOS.
+ logging.info("--- non-inherit TOS (slow path)")
+ with encap_route(m2, vrf_None, 1, "gre1"), \
+ gre(sw, d, vrf_o,
+ tos="0x10",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5") as g, \
+ encap_route(sw, vrf_o, 2, g):
+
+ sleep(15)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g,
+ require_fastpath=False)
+
+ # - Slow path: csum-enabled tunnel.
+ logging.info("--- checksum (slow path)")
+ with encap_route(m2, vrf_None, 1, "gre3"), \
+ gre(sw, d, vrf_o,
+ tos="inherit",
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5",
+ key=3333, csum=True) as g, \
+ encap_route(sw, vrf_o, 2, g):
+
+ sleep(15)
+ ping_test(tl, m1, sw, ipv4(test_ip(2, 33, [])), m1_if1, g,
+ require_fastpath=False)
+
+ # - Enable two dummy devices in different VRFs with the decap address.
+ # The driver crashes on tunnel tear-down if it incorrectly assigned
+ # both decaps to the same tunnel.
+ logging.info("--- the same tunnel local address in two VRFs")
+ with vrf(sw) as vrf3, \
+ dummy(sw, vrf_u) as d, \
+ gre(sw, d, vrf_o,
+ local_ip="1.2.3.4",
+ remote_ip="1.2.3.5") as g, \
+ encap_route(sw, vrf3, 2, g), \
+ dummy(sw, vrf3) as d3:
+
+ sleep(5)
+ d.set_addresses(["1.2.3.4/32"])
+ d3.set_addresses(["1.2.3.4/32"])
+ sleep(5)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/ipip-001-gre-hier-basic.xml
b/recipes/switchdev/ipip-001-gre-hier-basic.xml
new file mode 100644
index 0000000..fce33f1
--- /dev/null
+++ b/recipes/switchdev/ipip-001-gre-hier-basic.xml
@@ -0,0 +1,12 @@
+<lnstrecipe
xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <define>
+ <alias name="onet1" value="192.168.1"/>
+ <alias name="onet2" value="192.168.2"/>
+ <alias name="o6net1" value="2002:1"/>
+ <alias name="o6net2" value="2002:2"/>
+ <alias name="unet" value="192.168.99"/>
+ </define>
+ <xi:include href="ipip_common_topology.xml" />
+ <task python="ipip-001-gre-hier-basic.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/ipip_common.py b/recipes/switchdev/ipip_common.py
new file mode 100644
index 0000000..8d114ad
--- /dev/null
+++ b/recipes/switchdev/ipip_common.py
@@ -0,0 +1,72 @@
+"""
+A helper module for IP-in-IP tests.
+
+Copyright 2017 Mellanox Technologies. All rights reserved.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+petrm(a)mellanox.com (Petr Machata)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import route
+
+def ping_test(tl, m1, sw, addr, m1_if1, gre,
+ require_fastpath=True, fail_expected=False, count=100,
+ ipv6=False):
+ limit = int(0.9 * count)
+ if gre is not None:
+ before_stats = gre.link_stats()["rx_packets"]
+ ping_mod = ctl.get_module("IcmpPing" if not ipv6 else
"Icmp6Ping",
+ options={
+ "addr": addr,
+ "count": count,
+ "interval": 0.2,
+ "iface" : m1_if1.get_devname(),
+ "limit_rate": limit,
+ })
+ m1.run(ping_mod, fail_expected=fail_expected)
+
+ if not fail_expected and gre is not None:
+ after_stats = gre.link_stats()["rx_packets"]
+
+ delta = after_stats - before_stats
+ if require_fastpath and delta > 10:
+ # Allow a few packets of control plane traffic to go through slow
+ # path. All the data plane traffic should go through fast path.
+ tl.custom(sw, "ipip",
+ "Too many packets (%d) observed at GRE netdevice" %
delta)
+
+def ipv4(test_ip):
+ return test_ip[0]
+
+def ipv6(test_ip):
+ return test_ip[1]
+
+def encap_route(m, vrf, subnet, if_or_name, ip=ipv4, src=None):
+ if type(if_or_name) is str:
+ devname = m.get_interface(if_or_name).get_devname()
+ else:
+ devname = if_or_name.get_devname()
+ if src is not None:
+ srcstr = " src %s" % src
+ else:
+ srcstr = ""
+ return route(m, vrf, "%s dev %s%s" %
+ (ip(test_ip(subnet, 0)), devname, srcstr))
+
+def test_ip(major, minor, prefix=[24,64]):
+ return ["192.168.%d.%d%s" % (major, minor,
+ "/" + str(prefix[0]) if len(prefix) > 0 else ""),
+ "2002:%d::%d%s" % (major, minor,
+ "/" + str(prefix[1]) if len(prefix) > 1 else "")]
+
+def add_forward_route(m, vrf, remote_ip, via=ipv4(test_ip(99, 2, []))):
+ route(m, vrf, "%s/32 via %s"
+ % (remote_ip, via)).__enter__()
+
+def connect_host_ifaces(sw, if_o, vrf_o, if_u, vrf_u):
+ sw.run("ip l set dev %s master %s" % (if_o.get_devname(), vrf_o))
+ sw.run("ip l set dev %s master %s" % (if_u.get_devname(), vrf_u))
diff --git a/recipes/switchdev/ipip_common_topology.xml
b/recipes/switchdev/ipip_common_topology.xml
new file mode 100644
index 0000000..dc1c307
--- /dev/null
+++ b/recipes/switchdev/ipip_common_topology.xml
@@ -0,0 +1,58 @@
+<network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A">
+ <addresses>
+ <address value="{$onet1}.33/24" />
+ <address value="{$o6net1}::33/64" />
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="B">
+ <addresses>
+ <address value="{$unet}.2/24" />
+ </addresses>
+ </eth>
+ <dummy id="d1">
+ <addresses>
+ <address value="1.2.3.5/32" />
+ <address value="{$onet2}.33/32" />
+ <address value="{$o6net2}::33/128" />
+ </addresses>
+ </dummy>
+ <gre id="gre1">
+ <options>
+ <option name="local_ip" value="1.2.3.5"/>
+ <option name="remote_ip" value="1.2.3.4"/>
+ </options>
+ </gre>
+ <gre id="gre2">
+ <options>
+ <option name="local_ip" value="1.2.3.5"/>
+ <option name="remote_ip" value="1.2.3.4"/>
+ <option name="ikey" value="1111" />
+ <option name="okey" value="2222" />
+ </options>
+ </gre>
+ <gre id="gre3">
+ <options>
+ <option name="local_ip" value="1.2.3.5"/>
+ <option name="remote_ip" value="1.2.3.4"/>
+ <option name="key" value="3333" />
+ <option name="csum" value="True" />
+ </options>
+ </gre>
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+</network>
--
2.4.11