Skip to content

Commit 7f00af7

Browse files
lucassdiassdceara
authored andcommitted
controller: Flush dynamic routes learned in uninterested LRP.
If logical router has more than one LRP as gateway router port and dynamic routing configured, dynamic-routing-port-name could be used to specify the LRP that will be used to dynamic routing. However, if all LRPs learning routes, routes from LRP without dynamic-routing-port-name must be flushed. This happens when LRPs are scheduled in the same chassis. Signed-off-by: Lucas Vargas Dias <lucas.vdias@magalu.cloud> Signed-off-by: Dumitru Ceara <dceara@redhat.com> (cherry picked from commit b93685b)
1 parent 8968ec5 commit 7f00af7

5 files changed

Lines changed: 355 additions & 6 deletions

File tree

controller/ovn-controller.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5459,10 +5459,24 @@ en_route_exchange_run(struct engine_node *node, void *data)
54595459
return EN_STALE;
54605460
}
54615461

5462+
const struct ovsrec_open_vswitch_table *ovs_table =
5463+
EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node));
5464+
const char *chassis_id = get_ovs_chassis_id(ovs_table);
5465+
ovs_assert(chassis_id);
5466+
5467+
struct ovsdb_idl_index *sbrec_chassis_by_name =
5468+
engine_ovsdb_node_get_index(
5469+
engine_get_input("SB_chassis", node),
5470+
"name");
5471+
const struct sbrec_chassis *chassis
5472+
= chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
5473+
ovs_assert(chassis);
5474+
54625475
struct route_exchange_ctx_in r_ctx_in = {
54635476
.ovnsb_idl_txn = engine_get_context()->ovnsb_idl_txn,
54645477
.sbrec_learned_route_by_datapath = sbrec_learned_route_by_datapath,
54655478
.sbrec_port_binding_by_name = sbrec_port_binding_by_name,
5479+
.chassis = chassis,
54665480
.announce_routes = &route_data->announce_routes,
54675481
};
54685482
struct route_exchange_ctx_out r_ctx_out = {
@@ -6755,6 +6769,8 @@ main(int argc, char *argv[])
67556769
engine_add_input(&en_route, &en_sb_advertised_route,
67566770
route_sb_advertised_route_data_handler);
67576771

6772+
engine_add_input(&en_route_exchange, &en_ovs_open_vswitch, NULL);
6773+
engine_add_input(&en_route_exchange, &en_sb_chassis, NULL);
67586774
engine_add_input(&en_route_exchange, &en_route, NULL);
67596775
engine_add_input(&en_route_exchange, &en_sb_learned_route,
67606776
engine_noop_handler);

controller/route-exchange.c

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <net/if.h>
2222
#include <stdbool.h>
2323

24+
#include "hmapx.h"
2425
#include "openvswitch/poll-loop.h"
2526
#include "openvswitch/vlog.h"
2627
#include "openvswitch/list.h"
@@ -86,7 +87,7 @@ maintained_route_table_add(uint32_t table_id)
8687
hmap_insert(&_maintained_route_tables, &mrt->node, hash);
8788
}
8889

89-
static void
90+
static struct route_entry *
9091
route_add_entry(struct hmap *routes,
9192
const struct sbrec_learned_route *sb_route,
9293
bool stale)
@@ -102,6 +103,7 @@ route_add_entry(struct hmap *routes,
102103
hash = hash_string(sb_route->ip_prefix, hash);
103104

104105
hmap_insert(routes, &route_e->hmap_node, hash);
106+
return route_e;
105107
}
106108

107109
static struct route_entry *
@@ -144,28 +146,80 @@ sb_sync_learned_routes(const struct vector *learned_routes,
144146
struct ovsdb_idl_txn *ovnsb_idl_txn,
145147
struct ovsdb_idl_index *sbrec_port_binding_by_name,
146148
struct ovsdb_idl_index *sbrec_learned_route_by_datapath,
147-
bool *sb_changes_pending)
149+
bool *sb_changes_pending,
150+
const struct sbrec_chassis *chassis)
148151
{
149152
struct hmap sync_routes = HMAP_INITIALIZER(&sync_routes);
150153
const struct sbrec_learned_route *sb_route;
151-
struct route_entry *route_e;
154+
struct hmapx lrp_with_dr_port_name =
155+
HMAPX_INITIALIZER(&lrp_with_dr_port_name);
152156

153157
struct sbrec_learned_route *filter =
154158
sbrec_learned_route_index_init_row(sbrec_learned_route_by_datapath);
155159
sbrec_learned_route_index_set_datapath(filter, datapath);
156160
SBREC_LEARNED_ROUTE_FOR_EACH_EQUAL (sb_route, filter,
157161
sbrec_learned_route_by_datapath) {
162+
const struct sbrec_port_binding *cr_pb =
163+
lport_get_cr_port(sbrec_port_binding_by_name,
164+
sb_route->logical_port, NULL);
165+
struct route_entry *route_e = NULL;
166+
167+
/* Collect the set of unique logical ports we learned routes on. The
168+
* (potentially expensive) dynamic-routing-port-name lookups are
169+
* postponed until after the loop so that they are performed once per
170+
* logical port instead of once per learned route. */
171+
hmapx_add(&lrp_with_dr_port_name,
172+
CONST_CAST(void *, sb_route->logical_port));
173+
174+
if (sb_route->logical_port->chassis == chassis ||
175+
(cr_pb && cr_pb->chassis == chassis)) {
176+
route_e = route_add_entry(&sync_routes, sb_route, false);
177+
}
178+
158179
/* If the port is not local we don't care about it.
159180
* Some other ovn-controller will handle it.
160181
* We may not use smap_get since the value might be validly NULL. */
161182
if (!smap_get_node(bound_ports,
162183
sb_route->logical_port->logical_port)) {
163184
continue;
164185
}
186+
if (route_e) {
187+
route_e->stale = true;
188+
continue;
189+
}
165190
route_add_entry(&sync_routes, sb_route, true);
166191
}
167192
sbrec_learned_route_index_destroy_row(filter);
168193

194+
/* Drop the logical ports that don't have a dynamic-routing-port-name set,
195+
* either directly or via their distributed gateway port. */
196+
struct hmapx_node *lrp_node;
197+
HMAPX_FOR_EACH_SAFE (lrp_node, &lrp_with_dr_port_name) {
198+
const struct sbrec_port_binding *lrp = lrp_node->data;
199+
const struct sbrec_port_binding *cr_pb =
200+
lport_get_cr_port(sbrec_port_binding_by_name, lrp, NULL);
201+
const char *dynamic_routing_port_name =
202+
smap_get(&lrp->options, "dynamic-routing-port-name");
203+
if (!dynamic_routing_port_name && cr_pb) {
204+
dynamic_routing_port_name =
205+
smap_get(&cr_pb->options, "dynamic-routing-port-name");
206+
}
207+
if (!dynamic_routing_port_name) {
208+
hmapx_delete(&lrp_with_dr_port_name, lrp_node);
209+
}
210+
}
211+
212+
if (!hmapx_is_empty(&lrp_with_dr_port_name)) {
213+
struct route_entry *route_e;
214+
HMAP_FOR_EACH (route_e, hmap_node, &sync_routes) {
215+
if (!hmapx_contains(&lrp_with_dr_port_name,
216+
route_e->sb_route->logical_port)) {
217+
route_e->stale = true;
218+
}
219+
}
220+
}
221+
hmapx_destroy(&lrp_with_dr_port_name);
222+
169223
struct re_nl_received_route_node *learned_route;
170224
VECTOR_FOR_EACH_PTR (learned_routes, learned_route) {
171225
char *ip_prefix = normalize_v46_prefix(&learned_route->prefix,
@@ -187,8 +241,9 @@ sb_sync_learned_routes(const struct vector *learned_routes,
187241
if (!logical_port) {
188242
continue;
189243
}
190-
route_e = route_lookup(&sync_routes, datapath,
191-
logical_port, ip_prefix, nexthop);
244+
struct route_entry *route_e =
245+
route_lookup(&sync_routes, datapath,
246+
logical_port, ip_prefix, nexthop);
192247
if (route_e) {
193248
route_e->stale = false;
194249
} else {
@@ -209,6 +264,7 @@ sb_sync_learned_routes(const struct vector *learned_routes,
209264
free(nexthop);
210265
}
211266

267+
struct route_entry *route_e;
212268
HMAP_FOR_EACH_POP (route_e, hmap_node, &sync_routes) {
213269
if (route_e->stale) {
214270
sbrec_learned_route_delete(route_e->sb_route);
@@ -296,7 +352,8 @@ route_exchange_run(const struct route_exchange_ctx_in *r_ctx_in,
296352
&ad->bound_ports, r_ctx_in->ovnsb_idl_txn,
297353
r_ctx_in->sbrec_port_binding_by_name,
298354
r_ctx_in->sbrec_learned_route_by_datapath,
299-
&r_ctx_out->sb_changes_pending);
355+
&r_ctx_out->sb_changes_pending,
356+
r_ctx_in->chassis);
300357

301358
route_table_add_watch_request(&r_ctx_out->route_table_watches,
302359
table_id);

controller/route-exchange.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct route_exchange_ctx_in {
2424
struct ovsdb_idl_txn *ovnsb_idl_txn;
2525
struct ovsdb_idl_index *sbrec_port_binding_by_name;
2626
struct ovsdb_idl_index *sbrec_learned_route_by_datapath;
27+
const struct sbrec_chassis *chassis;
2728

2829
/* Contains struct advertise_datapath_entry */
2930
const struct hmap *announce_routes;

tests/multinode.at

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,6 +2989,148 @@ OVS_WAIT_UNTIL([m_as ovn-gw-2 ip netns exec frr-ns ping -W 1 -c 1 172.16.10.2])
29892989

29902990
AT_CLEANUP
29912991

2992+
AT_SETUP([ovn multinode dynamic-routing - BGP learned routes with router filter name and multiple DGPs])
2993+
2994+
# This is the multinode counterpart of the system test
2995+
# "dynamic-routing - BGP learned routes with router filter name and multiple
2996+
# DGPs": a single logical router with dynamic routing enabled has two
2997+
# distributed gateway ports scheduled on different chassis. Both LRPs are used
2998+
# for dynamic routing and each has its own dynamic-routing-port-name filter
2999+
# resolving to the interface local to the chassis hosting the port.
3000+
#
3001+
# Each chassis learns a route for the same prefix from its own VRF, so both
3002+
# gateway ports learn it. Because every LRP has a dynamic-routing-port-name
3003+
# that resolves to a local interface, neither route is flushed: the route
3004+
# learned on ovn-gw-1 (lrp-local-bgp-port) and the route learned on ovn-gw-2
3005+
# (lrp-dgp-dummy) both remain.
3006+
#
3007+
# Topology:
3008+
#
3009+
# public ls-dummy
3010+
# (local-bgp-port, ovn-gw-1) (ovn-gw-2)
3011+
# | |
3012+
# lrp-local-bgp-port lrp-dgp-dummy
3013+
# (DGP @ ovn-gw-1, (DGP @ ovn-gw-2,
3014+
# port-name local-bgp-port) port-name dummy-bgp-port)
3015+
# \ /
3016+
# +------------- lr-frr -----------+
3017+
# (dynamic-routing, vrf-id $vrf)
3018+
3019+
# Check that ovn-fake-multinode setup is up and running.
3020+
check_fake_multinode_setup
3021+
3022+
CHECK_VRF()
3023+
3024+
# Delete the multinode NB and OVS resources before starting the test.
3025+
cleanup_multinode_resources
3026+
3027+
CHECK_VRF()
3028+
3029+
vrf=1000
3030+
3031+
# Create the VRF manually on both gateway chassis (dynamic-routing-maintain-vrf
3032+
# is false, so OVN does not create it) and learn/read routes from table $vrf.
3033+
for gw in ovn-gw-1 ovn-gw-2; do
3034+
check m_as $gw ip link add vrf-bgp type vrf table $vrf
3035+
on_exit "m_as $gw ip link del vrf-bgp"
3036+
check m_as $gw ip link set vrf-bgp up
3037+
done
3038+
3039+
# Create the logical router with dynamic routing in VRF $vrf.
3040+
check multinode_nbctl lr-add lr-frr
3041+
check multinode_nbctl set Logical_Router lr-frr \
3042+
options:dynamic-routing=true \
3043+
options:dynamic-routing-vrf-id=$vrf \
3044+
options:dynamic-routing-redistribute=static
3045+
3046+
# The DGP that is used for dynamic routing, scheduled on ovn-gw-1.
3047+
check multinode_nbctl lrp-add lr-frr lrp-local-bgp-port 00:00:00:00:00:03 20.0.0.3/24 \
3048+
-- set Logical_Router_Port lrp-local-bgp-port \
3049+
options:dynamic-routing-maintain-vrf=false \
3050+
-- set Logical_Router_Port lrp-local-bgp-port \
3051+
options:routing-protocol-redirect=local-bgp-port
3052+
check multinode_nbctl lrp-set-gateway-chassis lrp-local-bgp-port ovn-gw-1
3053+
3054+
check multinode_nbctl ls-add public
3055+
check multinode_nbctl lsp-add-router-port public public-lr-frr lrp-local-bgp-port
3056+
check multinode_nbctl lsp-add public local-bgp-port \
3057+
-- lsp-set-addresses local-bgp-port unknown
3058+
3059+
# The second DGP used for dynamic routing, scheduled on ovn-gw-2. bgp-dummy is
3060+
# not an OVN logical port, so the dynamic-routing-port-name filter is resolved
3061+
# through the dynamic-routing-port-mapping configured on ovn-gw-2 below.
3062+
check multinode_nbctl lrp-add lr-frr lrp-dgp-dummy 00:00:00:00:00:04 20.0.1.3/24 \
3063+
-- set Logical_Router_Port lrp-dgp-dummy \
3064+
options:dynamic-routing-maintain-vrf=false
3065+
check multinode_nbctl lrp-set-gateway-chassis lrp-dgp-dummy ovn-gw-2
3066+
3067+
check multinode_nbctl ls-add ls-dummy
3068+
check multinode_nbctl lsp-add-router-port ls-dummy lsp-dummy lrp-dgp-dummy
3069+
3070+
check multinode_nbctl --wait=hv sync
3071+
3072+
# Bind local-bgp-port on ovn-gw-1 and move its interface into the VRF. The
3073+
# dynamic-routing-port-name filter will be resolved to this interface name.
3074+
check m_as ovn-gw-1 ovs-vsctl add-port br-int local-bgp-port \
3075+
-- set Interface local-bgp-port type=internal \
3076+
-- set Interface local-bgp-port external_ids:iface-id=local-bgp-port
3077+
on_exit "m_as ovn-gw-1 ovs-vsctl del-port br-int local-bgp-port"
3078+
check m_as ovn-gw-1 ip link set local-bgp-port master vrf-bgp
3079+
check m_as ovn-gw-1 ip link set local-bgp-port address 00:00:00:00:00:03
3080+
check m_as ovn-gw-1 ip addr add dev local-bgp-port 20.0.0.3/24
3081+
check m_as ovn-gw-1 ip link set local-bgp-port up
3082+
3083+
# ovn-gw-2 hosts lrp-dgp-dummy. Add an interface to its VRF so a route can be
3084+
# present in the VRF table and get learned on this gateway port, and map the
3085+
# dynamic-routing-port-name (dummy-bgp-port) to this interface so the filter
3086+
# resolves locally on ovn-gw-2.
3087+
check m_as ovn-gw-2 ip link add bgp-dummy type dummy
3088+
on_exit "m_as ovn-gw-2 ip link del bgp-dummy"
3089+
check m_as ovn-gw-2 ip link set bgp-dummy master vrf-bgp
3090+
check m_as ovn-gw-2 ip addr add dev bgp-dummy 20.0.1.3/24
3091+
check m_as ovn-gw-2 ip link set bgp-dummy up
3092+
3093+
check m_as ovn-gw-2 ovs-vsctl set open . \
3094+
external-ids:dynamic-routing-port-mapping="dummy-bgp-port=bgp-dummy"
3095+
on_exit "m_as ovn-gw-2 ovs-vsctl remove open . external-ids dynamic-routing-port-mapping"
3096+
3097+
m_wait_for_ports_up
3098+
check multinode_nbctl --wait=hv sync
3099+
3100+
# Both gateway ports are flagged for dynamic routing.
3101+
m_check_row_count Port_Binding 1 logical_port=cr-lrp-local-bgp-port 'options:dynamic-routing=true'
3102+
m_check_row_count Port_Binding 1 logical_port=cr-lrp-dgp-dummy 'options:dynamic-routing=true'
3103+
3104+
# Simulate a route learned via a dynamic routing protocol in each chassis' VRF
3105+
# for the same prefix.
3106+
check m_as ovn-gw-1 ip route add 10.10.3.0/24 via 20.0.0.25 vrf vrf-bgp proto bgp
3107+
check m_as ovn-gw-2 ip route add 10.10.3.0/24 via 20.0.1.25 vrf vrf-bgp proto bgp
3108+
3109+
# Each chassis learns the route on its own gateway port, so there are two
3110+
# Learned_Route rows for the prefix.
3111+
m_wait_row_count Learned_Route 2 ip_prefix=10.10.3.0/24
3112+
3113+
# Configure a dynamic-routing-port-name filter on both LRPs, each resolving to
3114+
# the interface local to the chassis hosting the port: lrp-local-bgp-port ->
3115+
# local-bgp-port (ovn-gw-1) and lrp-dgp-dummy -> dummy-bgp-port (ovn-gw-2, via
3116+
# the port-mapping configured above).
3117+
check multinode_nbctl set Logical_Router_Port lrp-local-bgp-port \
3118+
options:dynamic-routing-port-name=local-bgp-port
3119+
check multinode_nbctl --wait=hv set Logical_Router_Port lrp-dgp-dummy \
3120+
options:dynamic-routing-port-name=dummy-bgp-port
3121+
3122+
local_lp=$(m_fetch_column port_binding _uuid logical_port=lrp-local-bgp-port)
3123+
dummy_lp=$(m_fetch_column port_binding _uuid logical_port=lrp-dgp-dummy)
3124+
3125+
# Both filters resolve to a local interface, so no route is flushed: the route
3126+
# learned on ovn-gw-1 (lrp-local-bgp-port) and the route learned on ovn-gw-2
3127+
# (lrp-dgp-dummy) both remain.
3128+
m_wait_row_count Learned_Route 2 ip_prefix=10.10.3.0/24
3129+
m_wait_row_count Learned_Route 1 ip_prefix=10.10.3.0/24 logical_port=$local_lp
3130+
m_wait_row_count Learned_Route 1 ip_prefix=10.10.3.0/24 logical_port=$dummy_lp
3131+
3132+
AT_CLEANUP
3133+
29923134
AT_SETUP([HA: Check for missing garp on leader when BFD goes back up])
29933135
# Network topology
29943136
# ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐

0 commit comments

Comments
 (0)