diff mbox series

[ovs-dev,v3] northd: Add ipv6_{src, dst} to selection_fields column in the NB db.

Message ID e8b583798dfddf20f31f8e3929df9e473e486c5a.1737289772.git.lorenzo.bianconi@redhat.com
State Changes Requested
Headers show
Series [ovs-dev,v3] northd: Add ipv6_{src, dst} to selection_fields column in the NB db. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Lorenzo Bianconi Jan. 19, 2025, 12:31 p.m. UTC
Introduce ipv6_src and ipv6_dst to selection_fields column in
Load_Balancer Logical_Router_Static_Route tables in order to properly
load-balance IPv6 traffic if these fields are selected in group hash
algorithm.

Reported-at: https://issues.redhat.com/browse/FDP-1032
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
Changes in v3:
- improve documentation
- improve self-tests
Changes in v2:
- add new load-balancer Dual stack system test
---
 ovn-nb.ovsschema    |   9 +-
 ovn-nb.xml          |  10 ++
 tests/ovn.at        |   6 +-
 tests/system-ovn.at | 262 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 276 insertions(+), 11 deletions(-)

Comments

Ilya Maximets Jan. 31, 2025, 10:03 a.m. UTC | #1
On 1/19/25 13:31, Lorenzo Bianconi wrote:
> Introduce ipv6_src and ipv6_dst to selection_fields column in
> Load_Balancer Logical_Router_Static_Route tables in order to properly
> load-balance IPv6 traffic if these fields are selected in group hash
> algorithm.
> 
> Reported-at: https://issues.redhat.com/browse/FDP-1032
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
> Changes in v3:
> - improve documentation
> - improve self-tests
> Changes in v2:
> - add new load-balancer Dual stack system test
> ---
>  ovn-nb.ovsschema    |   9 +-
>  ovn-nb.xml          |  10 ++
>  tests/ovn.at        |   6 +-
>  tests/system-ovn.at | 262 +++++++++++++++++++++++++++++++++++++++++++-
>  4 files changed, 276 insertions(+), 11 deletions(-)

Hi, Lorenzo.  Thanks for the update!
See some comments inline.

Best regards, Ilya Maximets.

> 
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index e7aa6b2b1..b03d22333 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
> -    "version": "7.9.0",
> -    "cksum": "2414335430 38682",
> +    "version": "7.10.0",
> +    "cksum": "963912051 38764",
>      "tables": {
>          "NB_Global": {
>              "columns": {
> @@ -247,7 +247,7 @@
>                      "type": {"key": {"type": "string",
>                               "enum": ["set",
>                                  ["eth_src", "eth_dst", "ip_src", "ip_dst",
> -                                 "tp_src", "tp_dst"]]},
> +                                 "ipv6_src", "ipv6_dst", "tp_src", "tp_dst"]]},
>                               "min": 0, "max": "unlimited"}},
>                  "options": {
>                       "type": {"key": "string",
> @@ -510,7 +510,8 @@
>                      "type": {"key": {"type": "string",
>                               "enum": ["set",
>                                  ["eth_src", "eth_dst", "ip_proto", "ip_src",
> -                                 "ip_dst", "tp_src", "tp_dst"]]},
> +                                 "ip_dst", "ipv6_src", "ipv6_dst", "tp_src",
> +                                 "tp_dst"]]},
>                               "min": 0, "max": "unlimited"}},
>                  "options": {
>                      "type": {"key": "string", "value": "string",
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 24ef12f3b..714ca10c8 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -2166,6 +2166,14 @@
>          specified fields in generating the hash.
>        </p>
>  
> +      <p>
> +        Example: <code>{ip_proto,ip_src,ip_dst}</code> for a 3-tuple match.
> +        Example: <code>{ip_proto,ipv6_src,ipv6_dst}</code> for a IPv6 match.

Nit: s/a/an/

> +        Example: <code>{ip_proto,ip_src,ip_dst,tp_src,tp_dst}</code>
> +        Example: <code>{ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst}</code>
> +        for a 5-tuple match.

The last one is a 4-tuple match, not 5.

> +      </p>
> +
>        <p>
>          <code>dp_hash</code> selection method uses the assistance of
>          datapath to calculate the hash and it is expected to be
> @@ -3836,7 +3844,9 @@ or
>          </p>
>          <p>
>            Example: <code>{ip_proto,ip_src,ip_dst}</code> for a 3-tuple match.
> +          Example: <code>{ip_proto,ipv6_src,ipv6_dst}</code> for a IPv6 match.

Same here.

>            Example: <code>{ip_proto,ip_src,ip_dst,tp_src,tp_dst}</code>
> +          Example: <code>{ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst}</code>
>            for a 5-tuple match.

And here.

>          </p>
>      </column>
> diff --git a/tests/ovn.at b/tests/ovn.at
> index f0eca6ae4..299cb6aa3 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -25724,7 +25724,7 @@ check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
>  
>  check ovn-nbctl lb-add lb1 [[2001::a]]:80 [[2001::3]]:80,[[2002::3]]:80
>  OVN_LB_ID=$(ovn-nbctl --bare --column _uuid find load_balancer name=lb1)
> -check ovn-nbctl set load_balancer ${OVN_LB_ID} selection_fields="ip_dst,ip_src,tp_dst,tp_src"
> +check ovn-nbctl set load_balancer ${OVN_LB_ID} selection_fields="ipv6_dst,ipv6_src,tp_dst,tp_src"
>  #
>  check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2001::3]]\"=\"sw0-p1:[[2001::2]]\"
>  check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2002::3]]\"=\"sw1-p1:[[2002::2]]\"
> @@ -25767,14 +25767,14 @@ OVS_WAIT_FOR_OUTPUT(
>     ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
>    [dnl
>    (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark;)
> -  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
> +  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ipv6_dst,ipv6_src,tcp_dst,tcp_src");)
>  ])
>  
>  AT_CAPTURE_FILE([sbflows2])
>  OVS_WAIT_FOR_OUTPUT(
>    [ovn-sbctl dump-flows > sbflows2
>     ovn-sbctl dump-flows lr0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
> -  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && ip6.dst == 2001::a && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
> +  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && ip6.dst == 2001::a && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ipv6_dst,ipv6_src,tcp_dst,tcp_src");)
>  ])
>  
>  # get the svc monitor mac.
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 2e59f425b..df29f6e74 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -1755,10 +1755,10 @@ tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd
>  ])
>  
>  # Configure selection_fields.
> -check ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
> +check ovn-nbctl set load_balancer $lb2_uuid selection_fields="ipv6_src,ipv6_dst,tp_src,tp_dst"
>  OVS_WAIT_UNTIL([
>      test $(ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
> +    grep "selection_method=hash,fields(ipv6_src,ipv6_dst,tcp_src,tcp_dst)" -c) -eq 2
>  ])
>  
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -1789,10 +1789,10 @@ done
>  # there should be only one conntrack entry.
>  AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])
>  
> -check ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ip_src"
> +check ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ipv6_src"
>  OVS_WAIT_UNTIL([
>      test $(ovs-ofctl dump-groups br-int | \
> -    grep "selection_method=hash,fields(eth_src,ip_src)" -c) -eq 2
> +    grep "selection_method=hash,fields(eth_src,ipv6_src)" -c) -eq 2
>  ])
>  
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -1847,6 +1847,260 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"])
>  AT_CLEANUP
>  ])
>  
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([load-balancing - Dual Stack])
> +AT_KEYWORDS([ovnlb])
> +
> +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
> +
> +check_uuid ovn-nbctl create Logical_Router name=R1
> +check ovn-nbctl ls-add foo
> +check ovn-nbctl ls-add bar
> +
> +# Connect foo to R1

Nit: period at the end.

> +check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd01::1/64
> +check 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

Nit: period at the end.

> +check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24 fd02::1/64
> +check 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\"
> +
> +# Create 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")
> +check ovn-nbctl lsp-add foo foo1 \
> +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
> +
> +ADD_NAMESPACES(foo2)
> +ADD_VETH(foo2, foo2, br-int, "fd01::2/64", "f0:00:00:02:02:03", \
> +         "fd01::1", "nodad")
> +check ovn-nbctl lsp-add foo foo2 \
> +-- lsp-set-addresses foo2 "f0:00:00:02:02:03 fd01::2"
> +
> +# Create logical ports 'bar1', 'bar2', 'bar3' in switch 'bar'.

The code below creates bars: 1, 6, 2, 7, 3 and 8, so the comment
is not accurate.  Also, maybe the order should be asjusted.

> +ADD_NAMESPACES(bar1)
> +ADD_VETH(bar1, bar1, br-int, "172.16.1.2/24", "f0:00:0f:01:02:03", \
> +         "172.16.1.1")
> +check ovn-nbctl lsp-add bar bar1 \
> +-- lsp-set-addresses bar1 "f0:00:0f:01:02:03 172.16.1.2"
> +
> +ADD_NAMESPACES(bar6)
> +ADD_VETH(bar6, bar6, br-int, "fd02::2/64", "f0:00:0f:02:02:03", \
> +         "fd02::1", "nodad")
> +check ovn-nbctl lsp-add bar bar6 \
> +-- lsp-set-addresses bar6 "f0:00:0f:02:02:03 fd02::2"
> +
> +ADD_NAMESPACES(bar2)
> +ADD_VETH(bar2, bar2, br-int, "172.16.1.3/24", "f0:00:0f:01:02:04", \
> +         "172.16.1.1")
> +check ovn-nbctl lsp-add bar bar2 \
> +-- lsp-set-addresses bar2 "f0:00:0f:01:02:04 172.16.1.3"
> +
> +ADD_NAMESPACES(bar7)
> +ADD_VETH(bar7, bar7, br-int, "fd02::3/64", "f0:00:0f:02:02:04", \
> +         "fd02::1", "nodad")
> +check ovn-nbctl lsp-add bar bar7 \
> +-- lsp-set-addresses bar7 "f0:00:0f:02:02:04 fd02::3"
> +
> +ADD_NAMESPACES(bar3)
> +ADD_VETH(bar3, bar3, br-int, "172.16.1.4/24", "f0:00:0f:01:02:05", \
> +         "172.16.1.1")
> +check ovn-nbctl lsp-add bar bar3 \
> +-- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4"
> +
> +ADD_NAMESPACES(bar8)
> +ADD_VETH(bar8, bar8, br-int, "fd02::4/64", "f0:00:0f:02:02:05", \
> +         "fd02::1", "nodad")
> +check ovn-nbctl lsp-add bar bar8 \
> +-- lsp-set-addresses bar8 "f0:00:0f:02:02:05 fd02::4"
> +
> +# Config OVN load-balancer with a VIP.
> +check ovn-nbctl lb-add lb1 30.0.0.1:8080 "172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"
> +lb1_uuid=$(fetch_column nb:load_balancer _uuid name="lb1")
> +check ovn-nbctl set load_balancer $lb1_uuid vips:'"[[fd03::2]]:8090"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
> +
> +check ovn-nbctl ls-lb-add foo lb1
> +
> +# Wait for ovn-controller to catch up.
> +check ovn-nbctl --wait=hv sync
> +
> +OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
> +grep 'nat(dst=172.16.1.4:80)'])
> +
> +# Start webservers in 'bar1', 'bar2' and 'bar3' for IPv4 connections.
> +OVS_START_L7([bar1], [http])
> +OVS_START_L7([bar2], [http])
> +OVS_START_L7([bar3], [http])
> +# Start webservers in 'bar6', 'bar7' and 'bar8' for IPv6 connections.
> +OVS_START_L7([bar6], [http6])
> +OVS_START_L7([bar7], [http6])
> +OVS_START_L7([bar8], [http6])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> +
> +dnl Test load-balancing that includes L4 ports in NAT.
> +dnl Each server should have at least one connection.
> +OVS_WAIT_FOR_OUTPUT([
> +    for i in `seq 1 20`; do
> +        NS_EXEC([foo1], [wget 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
> +    done
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
> +      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])

This may not be needed, as we use FORMAT_CT() macro that filters on the address.

> +
> +OVS_WAIT_FOR_OUTPUT([
> +dnl Test load-balancing that includes L4 ports in NAT.
> +for i in `seq 1 10`; do
> +    NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
> +done
> +dnl Each server should have at least one connection.
> +ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
> +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +])
> +
> +# Configure selection_fields.
> +check ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst"
> +OVS_WAIT_UNTIL([
> +    test $(ovs-ofctl dump-groups br-int | \
> +    grep "selection_method=hash,fields(ip_src,ip_dst,ipv6_src,ipv6_dst,tcp_src,tcp_dst)" -c) -eq 2
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> +
> +# dnl Test load-balancing that includes L4 ports in NAT.
> +# dnl Each server should have at least one connection.
> +OVS_WAIT_FOR_OUTPUT([
> +    for i in `seq 1 20`; do
> +        NS_EXEC([foo1], [wget 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
> +    done
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
> +      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])

This one is also unnecessary.

> +
> +OVS_WAIT_FOR_OUTPUT([
> +dnl Test load-balancing that includes L4 ports in NAT.
> +for i in `seq 1 10`; do
> +    NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
> +done
> +dnl Each server should have at least one connection.
> +ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
> +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
> +])
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> +
> +echo "foo" > foo
> +for i in `seq 1 20`; do
> +    echo Request $i
> +    ip netns exec foo1 nc -p 30000 30.0.0.1 8080 < foo

Nit: should be using NS_EXEC for this, or NS_CHECK_EXEC even.
Same for the calls below.

> +done
> +
> +# dnl Only one backend should be chosen.
> +AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.1 -c) -eq 1])

Can use FORMAT_CT here as well.

> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])

Should not be necessary.

> +for i in `seq 1 20`; do
> +    echo Request $i
> +    ip netns exec foo2 nc -6 -p 30000 fd03::2 8090 < foo
> +done
> +
> +# dnl Only one backend should be chosen.
> +AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])

Same here.

> +
> +check ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,ipv6_src,ipv6_dst"

Maybe add a comment on what is being tested in this section.

> +OVS_WAIT_UNTIL([
> +    test $(ovs-ofctl dump-groups br-int | \
> +    grep "selection_method=hash,fields(ip_src,ip_dst,ipv6_src,ipv6_dst)" -c) -eq 2
> +])
> +
> +for p in $(seq 3 9); do
> +    NS_CHECK_EXEC([foo1], [ip addr add 192.168.1.$p/24 dev foo1])
> +done
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> +for i in `seq 1 50`; do

Do we need the outer loop?  We expect that only the source address changes
the hash, so if we send requests from the same source address, they will go
to the same backend.  So, if the 8 source addresses we have do not hash
into 3 different backends, sending 50 packets will not change that.

I'd suggest we increase the number of source addresses from 8 to 15-20 and
remove the outer loop. 

> +    for p in `seq 2 9`; do> +        NS_EXEC([foo1], [wget --bind-address=192.168.1.$p 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget1$i$p.log])
> +    done
> +done
> +
> +bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.2 -c)
> +bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.3 -c)
> +bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.4 -c)
> +
> +AT_CHECK([test $bar1_ct -gt 0])
> +AT_CHECK([test $bar2_ct -gt 0])
> +AT_CHECK([test $bar3_ct -gt 0])
> +
> +for p in $(seq 3 9); do
> +    NS_CHECK_EXEC([foo2], [ip -6 addr add fd01::$p/64 dev foo2])
> +done
> +
> +AT_CHECK([ovs-appctl dpctl/flush-conntrack])

This flush should not be needed.

> +for i in `seq 1 50`; do

Same for the number of IPs here.

> +    for p in `seq 2 9`; do
> +        NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 --bind-address=fd01::$p -t 5 -T 1 --retry-connrefused -v -o wget1$i$p.log])
> +    done
> +done
> +
> +bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::2 -c)
> +bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::3 -c)
> +bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::4 -c)
> +
> +AT_CHECK([test $bar1_ct -gt 0])
> +AT_CHECK([test $bar2_ct -gt 0])
> +AT_CHECK([test $bar3_ct -gt 0])
> +
> +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"])
> +AT_CLEANUP
> +])
> +
>  OVN_FOR_EACH_NORTHD([
>  AT_SETUP([load-balancing - same subnet.])
>  AT_KEYWORDS([ovnlb])
diff mbox series

Patch

diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index e7aa6b2b1..b03d22333 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@ 
 {
     "name": "OVN_Northbound",
-    "version": "7.9.0",
-    "cksum": "2414335430 38682",
+    "version": "7.10.0",
+    "cksum": "963912051 38764",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -247,7 +247,7 @@ 
                     "type": {"key": {"type": "string",
                              "enum": ["set",
                                 ["eth_src", "eth_dst", "ip_src", "ip_dst",
-                                 "tp_src", "tp_dst"]]},
+                                 "ipv6_src", "ipv6_dst", "tp_src", "tp_dst"]]},
                              "min": 0, "max": "unlimited"}},
                 "options": {
                      "type": {"key": "string",
@@ -510,7 +510,8 @@ 
                     "type": {"key": {"type": "string",
                              "enum": ["set",
                                 ["eth_src", "eth_dst", "ip_proto", "ip_src",
-                                 "ip_dst", "tp_src", "tp_dst"]]},
+                                 "ip_dst", "ipv6_src", "ipv6_dst", "tp_src",
+                                 "tp_dst"]]},
                              "min": 0, "max": "unlimited"}},
                 "options": {
                     "type": {"key": "string", "value": "string",
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 24ef12f3b..714ca10c8 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2166,6 +2166,14 @@ 
         specified fields in generating the hash.
       </p>
 
+      <p>
+        Example: <code>{ip_proto,ip_src,ip_dst}</code> for a 3-tuple match.
+        Example: <code>{ip_proto,ipv6_src,ipv6_dst}</code> for a IPv6 match.
+        Example: <code>{ip_proto,ip_src,ip_dst,tp_src,tp_dst}</code>
+        Example: <code>{ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst}</code>
+        for a 5-tuple match.
+      </p>
+
       <p>
         <code>dp_hash</code> selection method uses the assistance of
         datapath to calculate the hash and it is expected to be
@@ -3836,7 +3844,9 @@  or
         </p>
         <p>
           Example: <code>{ip_proto,ip_src,ip_dst}</code> for a 3-tuple match.
+          Example: <code>{ip_proto,ipv6_src,ipv6_dst}</code> for a IPv6 match.
           Example: <code>{ip_proto,ip_src,ip_dst,tp_src,tp_dst}</code>
+          Example: <code>{ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst}</code>
           for a 5-tuple match.
         </p>
     </column>
diff --git a/tests/ovn.at b/tests/ovn.at
index f0eca6ae4..299cb6aa3 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -25724,7 +25724,7 @@  check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
 
 check ovn-nbctl lb-add lb1 [[2001::a]]:80 [[2001::3]]:80,[[2002::3]]:80
 OVN_LB_ID=$(ovn-nbctl --bare --column _uuid find load_balancer name=lb1)
-check ovn-nbctl set load_balancer ${OVN_LB_ID} selection_fields="ip_dst,ip_src,tp_dst,tp_src"
+check ovn-nbctl set load_balancer ${OVN_LB_ID} selection_fields="ipv6_dst,ipv6_src,tp_dst,tp_src"
 #
 check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2001::3]]\"=\"sw0-p1:[[2001::2]]\"
 check ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:\"[[2002::3]]\"=\"sw1-p1:[[2002::2]]\"
@@ -25767,14 +25767,14 @@  OVS_WAIT_FOR_OUTPUT(
    ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
   [dnl
   (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark;)
-  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+  (ls_in_lb           ), priority=120  , match=(ct.new && ip6.dst == 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ipv6_dst,ipv6_src,tcp_dst,tcp_src");)
 ])
 
 AT_CAPTURE_FILE([sbflows2])
 OVS_WAIT_FOR_OUTPUT(
   [ovn-sbctl dump-flows > sbflows2
    ovn-sbctl dump-flows lr0 | grep ct_lb_mark | grep priority=120 | sed 's/table=..//'], 0,
-  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && ip6.dst == 2001::a && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && ip6.dst == 2001::a && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; hash_fields="ipv6_dst,ipv6_src,tcp_dst,tcp_src");)
 ])
 
 # get the svc monitor mac.
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 2e59f425b..df29f6e74 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -1755,10 +1755,10 @@  tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd
 ])
 
 # Configure selection_fields.
-check ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
+check ovn-nbctl set load_balancer $lb2_uuid selection_fields="ipv6_src,ipv6_dst,tp_src,tp_dst"
 OVS_WAIT_UNTIL([
     test $(ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
+    grep "selection_method=hash,fields(ipv6_src,ipv6_dst,tcp_src,tcp_dst)" -c) -eq 2
 ])
 
 AT_CHECK([ovs-appctl dpctl/flush-conntrack])
@@ -1789,10 +1789,10 @@  done
 # there should be only one conntrack entry.
 AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])
 
-check ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ip_src"
+check ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ipv6_src"
 OVS_WAIT_UNTIL([
     test $(ovs-ofctl dump-groups br-int | \
-    grep "selection_method=hash,fields(eth_src,ip_src)" -c) -eq 2
+    grep "selection_method=hash,fields(eth_src,ipv6_src)" -c) -eq 2
 ])
 
 AT_CHECK([ovs-appctl dpctl/flush-conntrack])
@@ -1847,6 +1847,260 @@  OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"])
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([load-balancing - Dual Stack])
+AT_KEYWORDS([ovnlb])
+
+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
+
+check_uuid ovn-nbctl create Logical_Router name=R1
+check ovn-nbctl ls-add foo
+check ovn-nbctl ls-add bar
+
+# Connect foo to R1
+check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24 fd01::1/64
+check 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
+check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24 fd02::1/64
+check 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\"
+
+# Create 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")
+check ovn-nbctl lsp-add foo foo1 \
+-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
+
+ADD_NAMESPACES(foo2)
+ADD_VETH(foo2, foo2, br-int, "fd01::2/64", "f0:00:00:02:02:03", \
+         "fd01::1", "nodad")
+check ovn-nbctl lsp-add foo foo2 \
+-- lsp-set-addresses foo2 "f0:00:00:02:02:03 fd01::2"
+
+# Create logical ports 'bar1', 'bar2', 'bar3' in switch 'bar'.
+ADD_NAMESPACES(bar1)
+ADD_VETH(bar1, bar1, br-int, "172.16.1.2/24", "f0:00:0f:01:02:03", \
+         "172.16.1.1")
+check ovn-nbctl lsp-add bar bar1 \
+-- lsp-set-addresses bar1 "f0:00:0f:01:02:03 172.16.1.2"
+
+ADD_NAMESPACES(bar6)
+ADD_VETH(bar6, bar6, br-int, "fd02::2/64", "f0:00:0f:02:02:03", \
+         "fd02::1", "nodad")
+check ovn-nbctl lsp-add bar bar6 \
+-- lsp-set-addresses bar6 "f0:00:0f:02:02:03 fd02::2"
+
+ADD_NAMESPACES(bar2)
+ADD_VETH(bar2, bar2, br-int, "172.16.1.3/24", "f0:00:0f:01:02:04", \
+         "172.16.1.1")
+check ovn-nbctl lsp-add bar bar2 \
+-- lsp-set-addresses bar2 "f0:00:0f:01:02:04 172.16.1.3"
+
+ADD_NAMESPACES(bar7)
+ADD_VETH(bar7, bar7, br-int, "fd02::3/64", "f0:00:0f:02:02:04", \
+         "fd02::1", "nodad")
+check ovn-nbctl lsp-add bar bar7 \
+-- lsp-set-addresses bar7 "f0:00:0f:02:02:04 fd02::3"
+
+ADD_NAMESPACES(bar3)
+ADD_VETH(bar3, bar3, br-int, "172.16.1.4/24", "f0:00:0f:01:02:05", \
+         "172.16.1.1")
+check ovn-nbctl lsp-add bar bar3 \
+-- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4"
+
+ADD_NAMESPACES(bar8)
+ADD_VETH(bar8, bar8, br-int, "fd02::4/64", "f0:00:0f:02:02:05", \
+         "fd02::1", "nodad")
+check ovn-nbctl lsp-add bar bar8 \
+-- lsp-set-addresses bar8 "f0:00:0f:02:02:05 fd02::4"
+
+# Config OVN load-balancer with a VIP.
+check ovn-nbctl lb-add lb1 30.0.0.1:8080 "172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"
+lb1_uuid=$(fetch_column nb:load_balancer _uuid name="lb1")
+check ovn-nbctl set load_balancer $lb1_uuid vips:'"[[fd03::2]]:8090"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
+
+check ovn-nbctl ls-lb-add foo lb1
+
+# Wait for ovn-controller to catch up.
+check ovn-nbctl --wait=hv sync
+
+OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
+grep 'nat(dst=172.16.1.4:80)'])
+
+# Start webservers in 'bar1', 'bar2' and 'bar3' for IPv4 connections.
+OVS_START_L7([bar1], [http])
+OVS_START_L7([bar2], [http])
+OVS_START_L7([bar3], [http])
+# Start webservers in 'bar6', 'bar7' and 'bar8' for IPv6 connections.
+OVS_START_L7([bar6], [http6])
+OVS_START_L7([bar7], [http6])
+OVS_START_L7([bar8], [http6])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+dnl Test load-balancing that includes L4 ports in NAT.
+dnl Each server should have at least one connection.
+OVS_WAIT_FOR_OUTPUT([
+    for i in `seq 1 20`; do
+        NS_EXEC([foo1], [wget 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+    done
+    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
+      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+OVS_WAIT_FOR_OUTPUT([
+dnl Test load-balancing that includes L4 ports in NAT.
+for i in `seq 1 10`; do
+    NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+done
+dnl Each server should have at least one connection.
+ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+])
+
+# Configure selection_fields.
+check ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst"
+OVS_WAIT_UNTIL([
+    test $(ovs-ofctl dump-groups br-int | \
+    grep "selection_method=hash,fields(ip_src,ip_dst,ipv6_src,ipv6_dst,tcp_src,tcp_dst)" -c) -eq 2
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+# dnl Test load-balancing that includes L4 ports in NAT.
+# dnl Each server should have at least one connection.
+OVS_WAIT_FOR_OUTPUT([
+    for i in `seq 1 20`; do
+        NS_EXEC([foo1], [wget 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+    done
+    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
+      sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+OVS_WAIT_FOR_OUTPUT([
+dnl Test load-balancing that includes L4 ports in NAT.
+for i in `seq 1 10`; do
+    NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
+done
+dnl Each server should have at least one connection.
+ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+echo "foo" > foo
+for i in `seq 1 20`; do
+    echo Request $i
+    ip netns exec foo1 nc -p 30000 30.0.0.1 8080 < foo
+done
+
+# dnl Only one backend should be chosen.
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.1 -c) -eq 1])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+for i in `seq 1 20`; do
+    echo Request $i
+    ip netns exec foo2 nc -6 -p 30000 fd03::2 8090 < foo
+done
+
+# dnl Only one backend should be chosen.
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])
+
+check ovn-nbctl set load_balancer $lb1_uuid selection_fields="ip_src,ip_dst,ipv6_src,ipv6_dst"
+OVS_WAIT_UNTIL([
+    test $(ovs-ofctl dump-groups br-int | \
+    grep "selection_method=hash,fields(ip_src,ip_dst,ipv6_src,ipv6_dst)" -c) -eq 2
+])
+
+for p in $(seq 3 9); do
+    NS_CHECK_EXEC([foo1], [ip addr add 192.168.1.$p/24 dev foo1])
+done
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+for i in `seq 1 50`; do
+    for p in `seq 2 9`; do
+        NS_EXEC([foo1], [wget --bind-address=192.168.1.$p 30.0.0.1:8080 -t 5 -T 1 --retry-connrefused -v -o wget1$i$p.log])
+    done
+done
+
+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.2 -c)
+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.3 -c)
+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 172.16.1.4 -c)
+
+AT_CHECK([test $bar1_ct -gt 0])
+AT_CHECK([test $bar2_ct -gt 0])
+AT_CHECK([test $bar3_ct -gt 0])
+
+for p in $(seq 3 9); do
+    NS_CHECK_EXEC([foo2], [ip -6 addr add fd01::$p/64 dev foo2])
+done
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+for i in `seq 1 50`; do
+    for p in `seq 2 9`; do
+        NS_EXEC([foo2], [wget http://[[fd03::2]]:8090 --bind-address=fd01::$p -t 5 -T 1 --retry-connrefused -v -o wget1$i$p.log])
+    done
+done
+
+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::2 -c)
+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::3 -c)
+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd02::4 -c)
+
+AT_CHECK([test $bar1_ct -gt 0])
+AT_CHECK([test $bar2_ct -gt 0])
+AT_CHECK([test $bar3_ct -gt 0])
+
+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"])
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([load-balancing - same subnet.])
 AT_KEYWORDS([ovnlb])