Skip to content

Commit

Permalink
northd: Add ipv6_{src, dst} to selection_fields column in the NB db.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
LorenzoBianconi authored and igsilya committed Feb 6, 2025
1 parent 33ecea5 commit 498f3f6
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 12 deletions.
9 changes: 5 additions & 4 deletions ovn-nb.ovsschema
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
"version": "7.9.1",
"cksum": "414526704 38879",
"version": "7.10.0",
"cksum": "1541935526 38961",
"tables": {
"NB_Global": {
"columns": {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
10 changes: 9 additions & 1 deletion ovn-nb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,13 @@
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 an 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>
</p>

<p>
<code>dp_hash</code> selection method uses the assistance of
datapath to calculate the hash and it is expected to be
Expand Down Expand Up @@ -3914,8 +3921,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 an IPv6 match.
Example: <code>{ip_proto,ip_src,ip_dst,tp_src,tp_dst}</code>
for a 5-tuple match.
Example: <code>{ip_src,ip_dst,ipv6_src,ipv6_dst,tp_src,tp_dst}</code>
</p>
</column>

Expand Down
6 changes: 3 additions & 3 deletions tests/ovn.at
Original file line number Diff line number Diff line change
Expand Up @@ -25834,7 +25834,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]]\"
Expand Down Expand Up @@ -25877,14 +25877,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.
Expand Down
256 changes: 252 additions & 4 deletions tests/system-ovn.at
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -1847,6 +1847,254 @@ 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")
NS_EXEC([foo1],[ip addr add fd01::2/64 dev foo1 nodad])
NS_EXEC([foo1],[ip -6 route add default via fd01::1])
check ovn-nbctl lsp-add foo foo1 \
-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2 fd01::2"

# Create logical ports 'bar1', 'bar2', 'bar3', 'bar6', 'bar7', 'bar8' 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(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(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(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(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(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>)
])

OVS_WAIT_FOR_OUTPUT([
dnl Test load-balancing that includes L4 ports in NAT.
for i in $(seq 1 10); do
NS_EXEC([foo1], [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>)
])

OVS_WAIT_FOR_OUTPUT([
dnl Test load-balancing that includes L4 ports in NAT.
for i in $(seq 1 10); do
NS_EXEC([foo1], [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
NS_EXEC([foo1], [nc -p 30000 30.0.0.1 8080 < foo])
done

# dnl Only one backend should be chosen.
AT_CHECK([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>)
])

for i in $(seq 1 20); do
echo Request $i
NS_EXEC([foo1], [nc -6 -p 30000 fd03::2 8090 < foo])
done

# dnl Only one backend should be chosen.
AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | \
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>)
])

# Test oad-balancing that using IP src and IP dst
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 20); do
h=$(printf '%02x' $p)
NS_EXEC([foo1],[ip addr add 192.168.1.$p/24 dev foo1])
NS_EXEC([foo1],[ip addr add fd01::$h/64 dev foo1 nodad])
NS_EXEC([foo1],[ip -6 route add default via fd01::1])
done

AT_CHECK([ovs-appctl dpctl/flush-conntrack])
for p in $(seq 3 20); do
NS_EXEC([foo1], [nc -s 192.168.1.$p 30.0.0.1 8080 < foo])
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 20); do
h=$(printf '%02x' $p)
NS_EXEC([foo1], [nc -6 -s fd01::$h fd03::2 8090 < foo])
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])
Expand Down

0 comments on commit 498f3f6

Please sign in to comment.