When adding the "source" field to "struct ecmp_groups_node" we missed to
also take this field into account in ecmp_groups_find.
This could lead to northd building an ecmp group based on different
routes from different sources (e.g. static and learned routes).
As these routes are designed to have different priorities we need to
ensure that these priorities are still respected for ECMP routes.
Fixes: f8924740f26e ("northd: Move connected routes to route engine.")
Signed-off-by: Felix Huettner <[email protected]>
---
northd/northd.c | 3 +-
tests/ovn-northd.at | 131 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/northd/northd.c b/northd/northd.c
index 1097bb159..6b2733482 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -11391,7 +11391,8 @@ ecmp_groups_find(struct hmap *ecmp_groups, struct
parsed_route *route)
if (ipv6_addr_equals(&eg->prefix, &route->prefix) &&
eg->plen == route->plen &&
eg->is_src_route == route->is_src_route &&
- eg->route_table_id == route->route_table_id) {
+ eg->route_table_id == route->route_table_id &&
+ eg->source == route->source) {
return eg;
}
}
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 64991ff75..62f8834fc 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -15214,6 +15214,137 @@ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows |
ovn_strip_lflows], [0], [dnl
AT_CLEANUP
])
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - route learning ecmp])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# We start with announcing routes on a lr with 2 lrps and 2 ecmp static routes.
+check ovn-nbctl lr-add lr0
+check ovn-nbctl --wait=sb set Logical_Router lr0 option:dynamic-routing=true \
+ option:dynamic-routing-redistribute="connected,static"
+check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+sw0=$(fetch_column port_binding _uuid logical_port=lr0-sw0)
+check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 10.0.1.1/24
+sw1=$(fetch_column port_binding _uuid logical_port=lr0-sw1)
+check ovn-nbctl --wait=sb --ecmp lr-route-add lr0 192.168.0.0/24 10.0.0.10
+check ovn-nbctl --wait=sb --ecmp lr-route-add lr0 192.168.0.0/24 10.0.1.10
+check_row_count Advertised_Route 4
+datapath=$(fetch_column datapath_binding _uuid external_ids:name=lr0)
+
+# Validate the routes.
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra),
action=(drop;)
+ table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
== 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.1.1; eth.src = 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw0" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff01; eth.src = 00:00:00:00:ff:01; outport =
"lr0-sw0"; flags.loopback = 1; reg9[[9]] = 0; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw1" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff02; eth.src = 00:00:00:00:ff:02; outport =
"lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;)
+])
+AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed -e
's/10\.0\..\./10.0.??./g' -e 's/lr0-sw./lr0-sw??/' -e 's/00:ff:0./00:ff:0?/' |
ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 1), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 2), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0),
action=(next;)
+])
+
+# Learn a route to 192.168.1.0/24 via 10.0.0.20 learned on lr0-sw0.
+check_uuid ovn-sbctl create Learned_Route \
+ datapath=$datapath \
+ logical_port=$sw0 \
+ ip_prefix=192.168.1.0/24 \
+ nexthop=10.0.0.20
+check ovn-nbctl --wait=sb sync
+check_row_count Advertised_Route 4
+check_row_count Learned_Route 1
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra),
action=(drop;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
== 192.168.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 10.0.0.20; reg5
= 10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback =
1; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
== 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.1.1; eth.src = 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw0" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff01; eth.src = 00:00:00:00:ff:01; outport =
"lr0-sw0"; flags.loopback = 1; reg9[[9]] = 0; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw1" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff02; eth.src = 00:00:00:00:ff:02; outport =
"lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;)
+])
+AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed -e
's/10\.0\..\./10.0.??./g' -e 's/lr0-sw./lr0-sw??/' -e 's/00:ff:0./00:ff:0?/' |
ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 1), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 2), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0),
action=(next;)
+])
+
+# Learn a route to 192.168.1.0/24 via 10.0.1.20 learned on lr0-sw1.
+# This will create an ecmp flow.
+check_uuid ovn-sbctl create Learned_Route \
+ datapath=$datapath \
+ logical_port=$sw1 \
+ ip_prefix=192.168.1.0/24 \
+ nexthop=10.0.1.20
+check ovn-nbctl --wait=sb sync
+check_row_count Advertised_Route 4
+check_row_count Learned_Route 2
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra),
action=(drop;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
== 192.168.1.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
== 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 2;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.1.1; eth.src = 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw0" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff01; eth.src = 00:00:00:00:ff:01; outport =
"lr0-sw0"; flags.loopback = 1; reg9[[9]] = 0; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw1" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff02; eth.src = 00:00:00:00:ff:02; outport =
"lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;)
+])
+AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed -e
's/10\.0\..\./10.0.??./g' -e 's/lr0-sw./lr0-sw??/' -e 's/00:ff:0./00:ff:0?/' |
ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing_ecmp), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 1), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 1
&& reg8[[16..31]] == 2), action=(reg0 = 10.0.??.20; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
&& reg8[[16..31]] == 1), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=100 , match=(reg8[[0..15]] == 2
&& reg8[[16..31]] == 2), action=(reg0 = 10.0.??.10; reg5 = 10.0.??.1; eth.src =
00:00:00:00:ff:0?; outport = "lr0-sw??"; reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing_ecmp), priority=150 , match=(reg8[[0..15]] == 0),
action=(next;)
+])
+
+# New learn two route both of them to 192.168.0.0/24:
+# One via 10.0.0.30 on lr0-sw0
+# One via 10.0.1.30 on lr0-sw1
+# The prefix is the same as with the static routes. But because learned routes
+# have a lower priority than static routes we will get two separate ECMP
+# groups.
+# Since the ecmp group IDs are not stable we do not validate
+# lr_in_ip_routing_ecmp. Having 3 separate ecmp selects in lr_in_ip_routing is
+# sufficient proof that there are in fact separate ECMP groups.
+check_uuid ovn-sbctl create Learned_Route \
+ datapath=$datapath \
+ logical_port=$sw0 \
+ ip_prefix=192.168.0.0/24 \
+ nexthop=10.0.0.30
+check_uuid ovn-sbctl create Learned_Route \
+ datapath=$datapath \
+ logical_port=$sw1 \
+ ip_prefix=192.168.0.0/24 \
+ nexthop=10.0.1.30
+check ovn-nbctl --wait=sb sync
+check_row_count Advertised_Route 4
+check_row_count Learned_Route 4
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | sed -e 's/reg8\[[0..15\]] =
[[123]]/reg8\[[0..15\]] = ??/' | ovn_strip_lflows], [0], [dnl
+ table=??(lr_in_ip_routing ), priority=0 , match=(1), action=(drop;)
+ table=??(lr_in_ip_routing ), priority=10550, match=(nd_rs || nd_ra),
action=(drop;)
+ table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
== 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = ??;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=194 , match=(reg7 == 0 && ip4.dst
== 192.168.1.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = ??;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=196 , match=(reg7 == 0 && ip4.dst
== 192.168.0.0/24), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = ??;
reg8[[16..31]] = select(1, 2);)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.0.1; eth.src = 00:00:00:00:ff:01; outport = "lr0-sw0"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=198 , match=(ip4.dst ==
10.0.1.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg5 =
10.0.1.1; eth.src = 00:00:00:00:ff:02; outport = "lr0-sw1"; flags.loopback = 1;
reg9[[9]] = 1; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw0" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff01; eth.src = 00:00:00:00:ff:01; outport =
"lr0-sw0"; flags.loopback = 1; reg9[[9]] = 0; next;)
+ table=??(lr_in_ip_routing ), priority=518 , match=(inport == "lr0-sw1" &&
ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst;
xxreg1 = fe80::200:ff:fe00:ff02; eth.src = 00:00:00:00:ff:02; outport =
"lr0-sw1"; flags.loopback = 1; reg9[[9]] = 0; next;)
+])
+
+AT_CLEANUP
+])
+
OVN_FOR_EACH_NORTHD_NO_HV([
AT_SETUP([dynamic-routing - host routes])
AT_KEYWORDS([dynamic-routing])
base-commit: 5ebaf30b5a3c253c850e4ec4116b90f3c9077eb6
--
2.48.1
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev