diff mbox series

[ovs-dev,ovn,v2] Add SCTP load balancer test.

Message ID 20200424151018.104297-1-mmichels@redhat.com
State Accepted
Headers show
Series [ovs-dev,ovn,v2] Add SCTP load balancer test. | expand

Commit Message

Mark Michelson April 24, 2020, 3:10 p.m. UTC
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>
---
v1->v2: rebase
---
 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

Comments

Numan Siddique April 29, 2020, 9 a.m. UTC | #1
On Fri, Apr 24, 2020 at 8:41 PM Mark Michelson <mmichels@redhat.com> wrote:

> 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>
>


Acked-by: Numan Siddique <numans@ovn.org>

Thanks
Numan




> ---
> v1->v2: rebase
> ---
>  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 8f3ff03b9..26681f02d 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 215fb432b..2b587af8b 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 2ccd9f1ce..5ba35babb 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 000000000..1c0ab194d
> --- /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=<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
> diff --git a/tests/test-l7.py b/tests/test-l7.py
> index d7854a1df..701c63f0d 100755
> --- a/tests/test-l7.py
> +++ b/tests/test-l7.py
> @@ -73,12 +73,39 @@ def get_tftpd():
>      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]
> --
> 2.25.1
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Numan Siddique May 12, 2020, 7:06 a.m. UTC | #2
On Wed, Apr 29, 2020 at 2:30 PM Numan Siddique <numans@ovn.org> wrote:

>
>
> On Fri, Apr 24, 2020 at 8:41 PM Mark Michelson <mmichels@redhat.com>
> wrote:
>
>> 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>
>>
>
>
> Acked-by: Numan Siddique <numans@ovn.org>
>

Hi Mark,

Looks like you missed this Ack. I applied this patch to master.

Numan


>
> Thanks
> Numan
>
>
>
>
>> ---
>> v1->v2: rebase
>> ---
>>  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 8f3ff03b9..26681f02d 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 215fb432b..2b587af8b 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 2ccd9f1ce..5ba35babb 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 000000000..1c0ab194d
>> --- /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=<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
>> diff --git a/tests/test-l7.py b/tests/test-l7.py
>> index d7854a1df..701c63f0d 100755
>> --- a/tests/test-l7.py
>> +++ b/tests/test-l7.py
>> @@ -73,12 +73,39 @@ def get_tftpd():
>>      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]
>> --
>> 2.25.1
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>>
diff mbox series

Patch

diff --git a/tests/atlocal.in b/tests/atlocal.in
index 8f3ff03b9..26681f02d 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 215fb432b..2b587af8b 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 2ccd9f1ce..5ba35babb 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 000000000..1c0ab194d
--- /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=<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
diff --git a/tests/test-l7.py b/tests/test-l7.py
index d7854a1df..701c63f0d 100755
--- a/tests/test-l7.py
+++ b/tests/test-l7.py
@@ -73,12 +73,39 @@  def get_tftpd():
     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]