Skip to content

Commit

Permalink
Add SCTP load balancer test.
Browse files Browse the repository at this point in the history
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 <mmichels@redhat.com>
Signed-off-by: 0-day Robot <robot@bytheb.org>
  • Loading branch information
putnopvut authored and ovsrobot committed Apr 24, 2020
1 parent 94cb764 commit 94ce5b3
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 1 deletion.
2 changes: 2 additions & 0 deletions tests/atlocal.in
Expand Up @@ -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".
Expand Down
3 changes: 2 additions & 1 deletion tests/automake.mk
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions tests/system-kmod-testsuite.at
Expand Up @@ -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])
217 changes: 217 additions & 0 deletions 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=<cleared>/' |
sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=<cleared>/' |
sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=<cleared>/' | uniq], [0], [dnl
sctp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
sctp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
])

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=<cleared>/' |
sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=<cleared>/' |
sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=<cleared>/' | uniq], [0], [dnl
sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
])

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=<cleared>/' |
sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=<cleared>/' |
sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=<cleared>/' | uniq], [0], [dnl
sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
sctp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
])

AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) |
sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
sed -e 's/vtag_orig=[[0-9]]*/vtag_orig=<cleared>/' |
sed -e 's/vtag_reply=[[0-9]]*/vtag_reply=<cleared>/' | uniq], [0], [dnl
sctp,orig=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
sctp,orig=(src=172.16.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>,vtag_orig=<cleared>,vtag_reply=<cleared>)
])

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
27 changes: 27 additions & 0 deletions tests/test-l7.py
Expand Up @@ -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]
Expand Down

0 comments on commit 94ce5b3

Please sign in to comment.