From 94ce5b3404c3c085e4dcf3c52e41a3e274adc482 Mon Sep 17 00:00:00 2001 From: Mark Michelson Date: Fri, 24 Apr 2020 11:10:18 -0400 Subject: [PATCH] Add SCTP load balancer test. This is essentially a copy of existing load balancer tests that use TCP, but with a few modifications for SCTP. Userspace conntrack and SCTP do not currently mix. Therefore, the test has been added to a new kernel-only system testsuite file. If userspace conntrack support for SCTP is added in the future, we can potentially move the SCTP test into the unified system-ovn.at file. Signed-off-by: Mark Michelson Signed-off-by: 0-day Robot --- tests/atlocal.in | 2 + tests/automake.mk | 3 +- tests/system-kmod-testsuite.at | 1 + tests/system-ovn-kmod.at | 217 +++++++++++++++++++++++++++++++++ tests/test-l7.py | 27 ++++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 tests/system-ovn-kmod.at diff --git a/tests/atlocal.in b/tests/atlocal.in index 8f3ff03b93..26681f02d8 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -150,6 +150,8 @@ find_l7_lib() find_l7_lib ftp # HAVE_TFTP find_l7_lib tftp +# HAVE_SCTP +find_l7_lib sctp # Look for a commnand in the system. If it is found, defines # HAVE_COMMAND="yes", otherwise HAVE_COMMAND="no". diff --git a/tests/automake.mk b/tests/automake.mk index 215fb432bf..2b587af8b0 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -45,7 +45,8 @@ SYSTEM_USERSPACE_TESTSUITE_AT = \ SYSTEM_TESTSUITE_AT = \ tests/system-common-macros.at \ - tests/system-ovn.at + tests/system-ovn.at \ + tests/system-ovn-kmod.at check_SCRIPTS += tests/atlocal diff --git a/tests/system-kmod-testsuite.at b/tests/system-kmod-testsuite.at index 2ccd9f1ce5..5ba35babbc 100644 --- a/tests/system-kmod-testsuite.at +++ b/tests/system-kmod-testsuite.at @@ -24,3 +24,4 @@ m4_include([tests/system-common-macros.at]) m4_include([tests/system-kmod-macros.at]) m4_include([tests/system-ovn.at]) +m4_include([tests/system-ovn-kmod.at]) diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at new file mode 100644 index 0000000000..1c0ab194d1 --- /dev/null +++ b/tests/system-ovn-kmod.at @@ -0,0 +1,217 @@ +AT_BANNER([system-ovn-kmod]) + +# SCTP and userspace conntrack do not mix. Therefore this +# test only can be run with kernel datapath. Otherwise, +# this is mostly a copy of existing load balancer tests +# in system-ovn.at +AT_SETUP([ovn -- load balancing in gateway router - SCTP]) +AT_SKIP_IF([test $HAVE_SCTP = no]) +AT_SKIP_IF([test $HAVE_NC = no]) +AT_KEYWORDS([ovnlb sctp]) + +CHECK_CONNTRACK() +CHECK_CONNTRACK_NAT() +ovn_start +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +# Logical network: +# Two LRs - R1 and R2 that are connected to each other via LS "join" +# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and +# bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected +# to it. R2 is a gateway router on which we add load-balancing rules. +# +# foo -- R1 -- join - R2 -- alice +# | +# bar ---- + +ovn-nbctl create Logical_Router name=R1 +ovn-nbctl create Logical_Router name=R2 options:chassis=hv1 + +ovn-nbctl ls-add foo +ovn-nbctl ls-add bar +ovn-nbctl ls-add alice +ovn-nbctl ls-add join + +# Connect foo to R1 +ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 +ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \ + type=router options:router-port=foo addresses=\"00:00:01:01:02:03\" + +# Connect bar to R1 +ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24 +ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \ + type=router options:router-port=bar addresses=\"00:00:01:01:02:04\" + +# Connect alice to R2 +ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24 +ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \ + type=router options:router-port=alice addresses=\"00:00:02:01:02:03\" + +# Connect R1 to join +ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24 +ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \ + type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"' + +# Connect R2 to join +ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24 +ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \ + type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"' + +# Static routes. +ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2 +ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1 + +# Logical port 'foo1' in switch 'foo'. +ADD_NAMESPACES(foo1) +ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ + "192.168.1.1") +ovn-nbctl lsp-add foo foo1 \ +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2" + +# Logical port 'alice1' in switch 'alice'. +ADD_NAMESPACES(alice1) +ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \ + "172.16.1.1") +ovn-nbctl lsp-add alice alice1 \ +-- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2" + +# Logical port 'bar1' in switch 'bar'. +ADD_NAMESPACES(bar1) +ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \ +"192.168.2.1") +ovn-nbctl lsp-add bar bar1 \ +-- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2" + +# Config OVN load-balancer with a VIP. +uuid=`ovn-nbctl create load_balancer protocol=sctp vips:30.0.0.1="192.168.1.2,192.168.2.2"` +ovn-nbctl set logical_router R2 load_balancer=$uuid + +# Config OVN load-balancer with another VIP (this time with ports). +ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:12345,192.168.2.2:12345"' + +# Add SNAT rule to make sure that Load-balancing still works with a SNAT rule. +ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \ + external_ip=30.0.0.2 -- add logical_router R2 nat @nat + +# Wait for ovn-controller to catch up. +ovn-nbctl --wait=hv sync +OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ +grep 'nat(dst=192.168.2.2:12345)']) + +# Start webservers in 'foo1', 'bar1'. +OVS_START_L7([foo1], [sctp]) +OVS_START_L7([bar1], [sctp]) + +on_exit "ovs-ofctl -O OpenFlow13 dump-flows br-int" + +dnl Should work with the virtual IP address through NAT +for i in `seq 1 20`; do + echo Request $i + NS_CHECK_EXEC([alice1], [nc --sctp --recv-only 30.0.0.1 12345 > client$i.log]) +done + +dnl Each server should have at least one connection. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | +sed -e 's/zone=[[0-9]]*/zone=/' | +sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=/' | +sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=/' | uniq], [0], [dnl +sctp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +sctp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +]) + +dnl Test load-balancing that includes L4 ports in NAT. +for i in `seq 1 20`; do + echo Request $i + NS_CHECK_EXEC([alice1], [nc --sctp --recv-only 30.0.0.2 8000 > clients$i.log]) +done + +dnl Each server should have at least one connection. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | +sed -e 's/zone=[[0-9]]*/zone=/' | +sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=/' | +sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=/' | uniq], [0], [dnl +sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +]) + +check_est_flows () { + n=$(ovs-ofctl dump-flows br-int table=14 | grep \ +"priority=120,ct_state=+est+trk,sctp,metadata=0x2,nw_dst=30.0.0.2,tp_dst=8000" \ +| grep nat | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p') + + echo "n_packets=$n" + test "$n" != 0 +} + +OVS_WAIT_UNTIL([check_est_flows], [check established flows]) + + +ovn-nbctl set logical_router R2 options:lb_force_snat_ip="20.0.0.2" + +# Destroy the load balancer and create again. ovn-controller will +# clear the OF flows and re add again and clears the n_packets +# for these flows. +ovn-nbctl destroy load_balancer $uuid +uuid=`ovn-nbctl create load_balancer protocol=sctp vips:30.0.0.1="192.168.1.2,192.168.2.2"` +ovn-nbctl set logical_router R2 load_balancer=$uuid + +# Config OVN load-balancer with another VIP (this time with ports). +ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:12345,192.168.2.2:12345"' + +ovn-nbctl list load_balancer +ovn-sbctl dump-flows R2 +OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-flows br-int table=41 | \ +grep 'nat(src=20.0.0.2)']) + +dnl Test load-balancing that includes L4 ports in NAT. +for i in `seq 1 20`; do + echo Request $i + NS_CHECK_EXEC([alice1], [nc --sctp --recv-only 30.0.0.2 8000 > clients$i.log]) +done + +dnl Each server should have at least one connection. +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | +sed -e 's/zone=[[0-9]]*/zone=/' | +sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=/' | +sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=/' | uniq], [0], [dnl +sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.1.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=,dport=),reply=(src=192.168.2.2,dst=172.16.1.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +]) + +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | +sed -e 's/zone=[[0-9]]*/zone=/' | +sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=/' | +sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=/' | uniq], [0], [dnl +sctp,orig=(src=172.16.1.2,dst=192.168.1.2,sport=,dport=),reply=(src=192.168.1.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +sctp,orig=(src=172.16.1.2,dst=192.168.2.2,sport=,dport=),reply=(src=192.168.2.2,dst=20.0.0.2,sport=,dport=),zone=,protoinfo=(state=,vtag_orig=,vtag_reply=) +]) + +OVS_WAIT_UNTIL([check_est_flows], [check established flows]) + +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d +/connection dropped.*/d"]) +AT_CLEANUP diff --git a/tests/test-l7.py b/tests/test-l7.py index d7854a1df3..701c63f0d2 100755 --- a/tests/test-l7.py +++ b/tests/test-l7.py @@ -73,12 +73,39 @@ def serve_forever(self): return server +def get_sctp(): + try: + import socket + import sctp + except ImportError: + print("Failed to import for SCTP") + server = None + pass + else: + class OVSSCTPServer(object): + def __init__(self, listen, handler=None): + self.sock = sctp.sctpsocket_tcp(socket.AF_INET) + self.sock.bind(listen) + self.sock.listen() + + def serve_forever(self): + while True: + client, _ = self.sock.accept() + client.send(b"SCRAM\r\n") + client.close() + + server = [OVSSCTPServer, None, 12345] + + return server + + def main(): SERVERS = { 'http': [TCPServer, SimpleHTTPRequestHandler, 80], 'http6': [TCPServerV6, SimpleHTTPRequestHandler, 80], 'ftp': get_ftpd(), 'tftp': get_tftpd(), + 'sctp': get_sctp(), } protocols = [srv for srv in SERVERS if SERVERS[srv] is not None]