1 /* Copyright (c) 2015 Nicira, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
22 #include "lib/shash.h"
26 #include "ovn-controller-vtep.h"
27 #include "openvswitch/vlog.h"
28 #include "ovn/lib/ovn-sb-idl.h"
29 #include "vtep/vtep-idl.h"
31 VLOG_DEFINE_THIS_MODULE(vtep
);
33 struct vtep_rec_physical_locator_list_entry
{
34 struct ovs_list locators_node
;
35 const struct vteprec_physical_locator
*vteprec_ploc
;
38 struct mmr_hash_node_data
{
39 const struct vteprec_mcast_macs_remote
*mmr
;
40 struct shash physical_locators
;
44 * Scans through the Binding table in ovnsb, and updates the vtep logical
45 * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP
50 /* Searches the 'chassis_rec->encaps' for the first vtep tunnel
51 * configuration, returns the 'ip'. Unless duplicated, the returned
52 * pointer cannot live past current vtep_run() execution. */
54 get_chassis_vtep_ip(const struct sbrec_chassis
*chassis_rec
)
59 for (i
= 0; i
< chassis_rec
->n_encaps
; i
++) {
60 if (!strcmp(chassis_rec
->encaps
[i
]->type
, "vxlan")) {
61 return chassis_rec
->encaps
[i
]->ip
;
69 /* Creates a new 'Ucast_Macs_Remote'. */
70 static struct vteprec_ucast_macs_remote
*
71 create_umr(struct ovsdb_idl_txn
*vtep_idl_txn
, const char *mac
,
72 const struct vteprec_logical_switch
*vtep_ls
)
74 struct vteprec_ucast_macs_remote
*new_umr
=
75 vteprec_ucast_macs_remote_insert(vtep_idl_txn
);
77 vteprec_ucast_macs_remote_set_MAC(new_umr
, mac
);
78 vteprec_ucast_macs_remote_set_logical_switch(new_umr
, vtep_ls
);
83 /* Creates a new 'Physical_Locator'. */
84 static struct vteprec_physical_locator
*
85 create_pl(struct ovsdb_idl_txn
*vtep_idl_txn
, const char *chassis_ip
)
87 struct vteprec_physical_locator
*new_pl
=
88 vteprec_physical_locator_insert(vtep_idl_txn
);
90 vteprec_physical_locator_set_dst_ip(new_pl
, chassis_ip
);
91 vteprec_physical_locator_set_encapsulation_type(new_pl
, VTEP_ENCAP_TYPE
);
96 /* Creates a new 'Mcast_Macs_Remote'. */
98 vtep_create_mmr(struct ovsdb_idl_txn
*vtep_idl_txn
, const char *mac
,
99 const struct vteprec_logical_switch
*vtep_ls
,
100 const struct vteprec_physical_locator_set
*ploc_set
)
102 struct vteprec_mcast_macs_remote
*new_mmr
=
103 vteprec_mcast_macs_remote_insert(vtep_idl_txn
);
105 vteprec_mcast_macs_remote_set_MAC(new_mmr
, mac
);
106 vteprec_mcast_macs_remote_set_logical_switch(new_mmr
, vtep_ls
);
107 vteprec_mcast_macs_remote_set_locator_set(new_mmr
, ploc_set
);
110 /* Compares previous and new mmr locator sets and returns true if they
111 * differ and false otherwise. This function also preps a new locator
112 * set for database write.
114 * 'locators_list' is the new set of locators for the associated
115 * 'Mcast_Macs_Remote' entry passed in and is queried to generate the
116 * new set of locators in vtep database format. */
118 vtep_process_pls(const struct ovs_list
*locators_list
,
119 const struct mmr_hash_node_data
*mmr_ext
,
120 struct vteprec_physical_locator
**locators
)
122 size_t n_locators_prev
= 0;
123 size_t n_locators_new
= ovs_list_size(locators_list
);
124 bool locator_lists_differ
= false;
127 n_locators_prev
= mmr_ext
->mmr
->locator_set
->n_locators
;
129 if (n_locators_prev
!= n_locators_new
) {
130 locator_lists_differ
= true;
133 if (n_locators_new
) {
135 struct vtep_rec_physical_locator_list_entry
*ploc_entry
;
136 LIST_FOR_EACH (ploc_entry
, locators_node
, locators_list
) {
137 locators
[i
] = (struct vteprec_physical_locator
*)
138 ploc_entry
->vteprec_ploc
;
139 if (mmr_ext
&& !shash_find_data(&mmr_ext
->physical_locators
,
140 locators
[i
]->dst_ip
)) {
141 locator_lists_differ
= true;
147 return locator_lists_differ
;
150 /* Creates a new 'Mcast_Macs_Remote' entry if needed and also cleans up
151 * out-dated remote mcast mac entries as needed. */
153 vtep_update_mmr(struct ovsdb_idl_txn
*vtep_idl_txn
,
154 struct ovs_list
*locators_list
,
155 const struct vteprec_logical_switch
*vtep_ls
,
156 const struct mmr_hash_node_data
*mmr_ext
)
158 struct vteprec_physical_locator
**locators
= NULL
;
159 size_t n_locators_new
= ovs_list_size(locators_list
);
162 locators
= xmalloc(n_locators_new
* sizeof *locators
);
164 mmr_changed
= vtep_process_pls(locators_list
, mmr_ext
, locators
);
166 if (mmr_ext
&& !n_locators_new
) {
167 vteprec_mcast_macs_remote_delete(mmr_ext
->mmr
);
168 } else if ((mmr_ext
&& mmr_changed
) ||
169 (!mmr_ext
&& n_locators_new
)) {
171 const struct vteprec_physical_locator_set
*ploc_set
=
172 vteprec_physical_locator_set_insert(vtep_idl_txn
);
174 vtep_create_mmr(vtep_idl_txn
, "unknown-dst", vtep_ls
, ploc_set
);
176 vteprec_physical_locator_set_set_locators(ploc_set
, locators
,
182 /* Updates the vtep Logical_Switch table entries' tunnel keys based
183 * on the port bindings. */
185 vtep_lswitch_run(struct shash
*vtep_pbs
, struct sset
*vtep_pswitches
,
186 struct shash
*vtep_lswitches
)
188 struct sset used_ls
= SSET_INITIALIZER(&used_ls
);
189 struct shash_node
*node
;
191 /* Collects the logical switch bindings from port binding entries.
192 * Since the binding module has already guaranteed that each vtep
193 * logical switch is bound only to one ovn-sb logical datapath,
194 * we can just iterate and assign tunnel key to vtep logical switch. */
195 SHASH_FOR_EACH (node
, vtep_pbs
) {
196 const struct sbrec_port_binding
*port_binding_rec
= node
->data
;
197 const char *pswitch_name
= smap_get(&port_binding_rec
->options
,
198 "vtep-physical-switch");
199 const char *lswitch_name
= smap_get(&port_binding_rec
->options
,
200 "vtep-logical-switch");
201 const struct vteprec_logical_switch
*vtep_ls
;
203 /* If 'port_binding_rec->chassis' exists then 'pswitch_name'
204 * and 'lswitch_name' must also exist. */
205 if (!pswitch_name
|| !lswitch_name
) {
206 /* This could only happen when someone directly modifies the
207 * database, (e.g. using ovn-sbctl). */
208 VLOG_ERR("logical port (%s) with no 'options:vtep-physical-"
209 "switch' or 'options:vtep-logical-switch' specified "
210 "is bound to chassis (%s).",
211 port_binding_rec
->logical_port
,
212 port_binding_rec
->chassis
->name
);
215 vtep_ls
= shash_find_data(vtep_lswitches
, lswitch_name
);
216 /* Also checks 'pswitch_name' since the same 'lswitch_name' could
217 * exist in multiple vtep database instances and be bound to different
218 * ovn logical networks. */
219 if (vtep_ls
&& sset_find(vtep_pswitches
, pswitch_name
)) {
222 if (sset_find(&used_ls
, lswitch_name
)) {
226 tnl_key
= port_binding_rec
->datapath
->tunnel_key
;
227 if (vtep_ls
->n_tunnel_key
228 && vtep_ls
->tunnel_key
[0] != tnl_key
) {
229 VLOG_DBG("set vtep logical switch (%s) tunnel key from "
230 "(%"PRId64
") to (%"PRId64
")", vtep_ls
->name
,
231 vtep_ls
->tunnel_key
[0], tnl_key
);
233 vteprec_logical_switch_set_tunnel_key(vtep_ls
, &tnl_key
, 1);
234 sset_add(&used_ls
, lswitch_name
);
237 /* Resets the tunnel keys for unused vtep logical switches. */
238 SHASH_FOR_EACH (node
, vtep_lswitches
) {
239 if (!sset_find(&used_ls
, node
->name
)) {
241 vteprec_logical_switch_set_tunnel_key(node
->data
, &tnl_key
, 1);
244 sset_destroy(&used_ls
);
247 /* Updates the vtep 'Ucast_Macs_Remote' and 'Mcast_Macs_Remote' tables based
248 * on non-vtep port bindings. */
250 vtep_macs_run(struct ovsdb_idl_txn
*vtep_idl_txn
, struct shash
*ucast_macs_rmts
,
251 struct shash
*mcast_macs_rmts
, struct shash
*physical_locators
,
252 struct shash
*vtep_lswitches
, struct shash
*non_vtep_pbs
)
254 struct shash_node
*node
;
257 /* Maps from ovn logical datapath tunnel key (which is also the vtep
258 * logical switch tunnel key) to the corresponding vtep logical switch
259 * instance. Also, the shash map 'added_macs' is used for checking
260 * duplicated MAC addresses in the same ovn logical datapath. 'mmr_ext'
261 * is used to track mmr info per LS that needs creation/update and
262 * 'locators_list' collects the new physical locators to be bound for
263 * an mmr_ext; 'physical_locators' is used to track existing locators and
264 * filter duplicates per logical switch. */
265 struct ls_hash_node
{
266 struct hmap_node hmap_node
;
268 const struct vteprec_logical_switch
*vtep_ls
;
269 struct shash added_macs
;
271 struct ovs_list locators_list
;
272 struct shash physical_locators
;
273 struct mmr_hash_node_data
*mmr_ext
;
277 SHASH_FOR_EACH (node
, vtep_lswitches
) {
278 const struct vteprec_logical_switch
*vtep_ls
= node
->data
;
279 struct ls_hash_node
*ls_node
;
281 if (!vtep_ls
->n_tunnel_key
) {
284 ls_node
= xmalloc(sizeof *ls_node
);
285 ls_node
->vtep_ls
= vtep_ls
;
286 shash_init(&ls_node
->added_macs
);
287 shash_init(&ls_node
->physical_locators
);
288 ovs_list_init(&ls_node
->locators_list
);
289 ls_node
->mmr_ext
= NULL
;
290 hmap_insert(&ls_map
, &ls_node
->hmap_node
,
291 hash_uint64((uint64_t) vtep_ls
->tunnel_key
[0]));
294 SHASH_FOR_EACH (node
, non_vtep_pbs
) {
295 const struct sbrec_port_binding
*port_binding_rec
= node
->data
;
296 const struct sbrec_chassis
*chassis_rec
;
297 struct ls_hash_node
*ls_node
;
298 const char *chassis_ip
;
302 chassis_rec
= port_binding_rec
->chassis
;
307 tnl_key
= port_binding_rec
->datapath
->tunnel_key
;
308 HMAP_FOR_EACH_WITH_HASH (ls_node
, hmap_node
,
309 hash_uint64((uint64_t) tnl_key
),
311 if (ls_node
->vtep_ls
->tunnel_key
[0] == tnl_key
) {
315 /* If 'ls_node' is NULL, that means no vtep logical switch is
316 * attached to the corresponding ovn logical datapath, so pass.
322 chassis_ip
= get_chassis_vtep_ip(chassis_rec
);
323 /* Unreachable chassis, continue. */
325 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
326 VLOG_INFO_RL(&rl
, "VTEP tunnel encap on chassis (%s) not found",
331 const struct vteprec_physical_locator
*pl
=
332 shash_find_data(physical_locators
, chassis_ip
);
334 pl
= create_pl(vtep_idl_txn
, chassis_ip
);
335 shash_add(physical_locators
, chassis_ip
, pl
);
338 const struct vteprec_physical_locator
*ls_pl
=
339 shash_find_data(&ls_node
->physical_locators
, chassis_ip
);
341 struct vtep_rec_physical_locator_list_entry
*ploc_entry
=
342 xmalloc(sizeof *ploc_entry
);
343 ploc_entry
->vteprec_ploc
= pl
;
344 ovs_list_push_back(&ls_node
->locators_list
,
345 &ploc_entry
->locators_node
);
346 shash_add(&ls_node
->physical_locators
, chassis_ip
, pl
);
349 char *mac_tnlkey
= xasprintf("%s_%"PRId64
, "unknown-dst", tnl_key
);
350 ls_node
->mmr_ext
= shash_find_data(mcast_macs_rmts
, mac_tnlkey
);
352 if (ls_node
->mmr_ext
&&
353 ls_node
->mmr_ext
->mmr
->logical_switch
== ls_node
->vtep_ls
) {
355 /* Delete the entry from the hash table so the mmr does not get
356 * removed from the DB later on during stale checking. */
357 shash_find_and_delete(mcast_macs_rmts
, mac_tnlkey
);
361 for (i
= 0; i
< port_binding_rec
->n_mac
; i
++) {
362 const struct vteprec_ucast_macs_remote
*umr
;
363 const struct sbrec_port_binding
*conflict
;
364 char *mac
= port_binding_rec
->mac
[i
];
366 /* Checks for duplicate MAC in the same vtep logical switch. */
367 conflict
= shash_find_data(&ls_node
->added_macs
, mac
);
369 VLOG_WARN("MAC address (%s) has already been known to be "
370 "on logical port (%s) in the same logical "
371 "datapath, so just ignore this logical port (%s)",
372 mac
, conflict
->logical_port
,
373 port_binding_rec
->logical_port
);
376 shash_add(&ls_node
->added_macs
, mac
, port_binding_rec
);
378 char *mac_ip_tnlkey
= xasprintf("%s_%s_%"PRId64
, mac
, chassis_ip
,
380 umr
= shash_find_data(ucast_macs_rmts
, mac_ip_tnlkey
);
381 /* If finds the 'umr' entry for the mac, ip, and tnl_key, deletes
382 * the entry from shash so that it is not gargage collected.
384 * If not found, creates a new 'umr' entry. */
385 if (umr
&& umr
->logical_switch
== ls_node
->vtep_ls
) {
386 shash_find_and_delete(ucast_macs_rmts
, mac_ip_tnlkey
);
388 const struct vteprec_ucast_macs_remote
*new_umr
;
389 new_umr
= create_umr(vtep_idl_txn
, mac
, ls_node
->vtep_ls
);
390 vteprec_ucast_macs_remote_set_locator(new_umr
, pl
);
396 /* Removes all remaining 'umr's, since they do not exist anymore. */
397 SHASH_FOR_EACH (node
, ucast_macs_rmts
) {
398 vteprec_ucast_macs_remote_delete(node
->data
);
400 struct ls_hash_node
*iter
, *next
;
401 HMAP_FOR_EACH_SAFE (iter
, next
, hmap_node
, &ls_map
) {
402 struct vtep_rec_physical_locator_list_entry
*ploc_entry
;
403 vtep_update_mmr(vtep_idl_txn
, &iter
->locators_list
,
404 iter
->vtep_ls
, iter
->mmr_ext
);
405 LIST_FOR_EACH_POP(ploc_entry
, locators_node
,
406 &iter
->locators_list
) {
409 hmap_remove(&ls_map
, &iter
->hmap_node
);
410 shash_destroy(&iter
->added_macs
);
411 shash_destroy(&iter
->physical_locators
);
414 hmap_destroy(&ls_map
);
416 /* Clean stale 'Mcast_Macs_Remote' */
417 struct mmr_hash_node_data
*mmr_ext
;
418 SHASH_FOR_EACH (node
, mcast_macs_rmts
) {
419 mmr_ext
= node
->data
;
420 vteprec_mcast_macs_remote_delete(mmr_ext
->mmr
);
424 /* Resets all logical switches' 'tunnel_key' to NULL */
426 vtep_lswitch_cleanup(struct ovsdb_idl
*vtep_idl
)
428 const struct vteprec_logical_switch
*vtep_ls
;
431 VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls
, vtep_idl
) {
432 if (vtep_ls
->n_tunnel_key
) {
433 vteprec_logical_switch_set_tunnel_key(vtep_ls
, NULL
, 0);
441 /* Removes all entries in the 'Ucast_Macs_Remote' table in the vtep database.
442 * Returns true when all done (i.e. no entry to remove). */
444 vtep_ucast_macs_cleanup(struct ovsdb_idl
*vtep_idl
)
446 const struct vteprec_ucast_macs_remote
*umr
;
448 VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr
, vtep_idl
) {
449 vteprec_ucast_macs_remote_delete(umr
);
456 /* Removes all entries in the 'Mcast_Macs_Remote' table in vtep database.
457 * Returns true when all done (i.e. no entry to remove). */
459 vtep_mcast_macs_cleanup(struct ovsdb_idl
*vtep_idl
)
461 const struct vteprec_mcast_macs_remote
*mmr
;
463 VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr
, vtep_idl
) {
464 vteprec_mcast_macs_remote_delete(mmr
);
471 /* Updates vtep logical switch tunnel keys. */
473 vtep_run(struct controller_vtep_ctx
*ctx
)
475 if (!ctx
->vtep_idl_txn
) {
479 struct sset vtep_pswitches
= SSET_INITIALIZER(&vtep_pswitches
);
480 struct shash vtep_lswitches
= SHASH_INITIALIZER(&vtep_lswitches
);
481 struct shash ucast_macs_rmts
= SHASH_INITIALIZER(&ucast_macs_rmts
);
482 struct shash mcast_macs_rmts
= SHASH_INITIALIZER(&mcast_macs_rmts
);
483 struct shash physical_locators
= SHASH_INITIALIZER(&physical_locators
);
484 struct shash vtep_pbs
= SHASH_INITIALIZER(&vtep_pbs
);
485 struct shash non_vtep_pbs
= SHASH_INITIALIZER(&non_vtep_pbs
);
486 const struct vteprec_physical_switch
*vtep_ps
;
487 const struct vteprec_logical_switch
*vtep_ls
;
488 const struct vteprec_ucast_macs_remote
*umr
;
489 const struct sbrec_port_binding
*port_binding_rec
;
490 const struct vteprec_mcast_macs_remote
*mmr
;
491 struct shash_node
*node
;
493 /* Collects 'Physical_Switch's. */
494 VTEPREC_PHYSICAL_SWITCH_FOR_EACH (vtep_ps
, ctx
->vtep_idl
) {
495 sset_add(&vtep_pswitches
, vtep_ps
->name
);
498 /* Collects 'Logical_Switch's. */
499 VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls
, ctx
->vtep_idl
) {
500 shash_add(&vtep_lswitches
, vtep_ls
->name
, vtep_ls
);
503 /* Collects 'Ucast_Macs_Remote's. */
504 VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr
, ctx
->vtep_idl
) {
505 char *mac_ip_tnlkey
=
506 xasprintf("%s_%s_%"PRId64
, umr
->MAC
,
507 umr
->locator
? umr
->locator
->dst_ip
: "",
508 umr
->logical_switch
&& umr
->logical_switch
->n_tunnel_key
509 ? umr
->logical_switch
->tunnel_key
[0] : INT64_MAX
);
511 shash_add(&ucast_macs_rmts
, mac_ip_tnlkey
, umr
);
515 /* Collects 'Mcast_Macs_Remote's. */
516 VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr
, ctx
->vtep_idl
) {
517 struct mmr_hash_node_data
*mmr_ext
= xmalloc(sizeof *mmr_ext
);;
519 xasprintf("%s_%"PRId64
, mmr
->MAC
,
520 mmr
->logical_switch
&& mmr
->logical_switch
->n_tunnel_key
521 ? mmr
->logical_switch
->tunnel_key
[0] : INT64_MAX
);
523 shash_add(&mcast_macs_rmts
, mac_tnlkey
, mmr_ext
);
526 shash_init(&mmr_ext
->physical_locators
);
527 for (size_t i
= 0; i
< mmr
->locator_set
->n_locators
; i
++) {
528 shash_add(&mmr_ext
->physical_locators
,
529 mmr
->locator_set
->locators
[i
]->dst_ip
,
530 mmr
->locator_set
->locators
[i
]);
536 /* Collects 'Physical_Locator's. */
537 const struct vteprec_physical_locator
*pl
;
538 VTEPREC_PHYSICAL_LOCATOR_FOR_EACH (pl
, ctx
->vtep_idl
) {
539 shash_add(&physical_locators
, pl
->dst_ip
, pl
);
542 /* Collects and classifies 'Port_Binding's. */
543 SBREC_PORT_BINDING_FOR_EACH(port_binding_rec
, ctx
->ovnsb_idl
) {
544 struct shash
*target
=
545 !strcmp(port_binding_rec
->type
, "vtep") ? &vtep_pbs
: &non_vtep_pbs
;
547 if (!port_binding_rec
->chassis
) {
550 shash_add(target
, port_binding_rec
->logical_port
, port_binding_rec
);
553 ovsdb_idl_txn_add_comment(ctx
->vtep_idl_txn
,
554 "ovn-controller-vtep: update logical switch "
555 "tunnel keys and 'ucast_macs_remote's");
557 vtep_lswitch_run(&vtep_pbs
, &vtep_pswitches
, &vtep_lswitches
);
558 vtep_macs_run(ctx
->vtep_idl_txn
, &ucast_macs_rmts
,
559 &mcast_macs_rmts
, &physical_locators
,
560 &vtep_lswitches
, &non_vtep_pbs
);
562 sset_destroy(&vtep_pswitches
);
563 shash_destroy(&vtep_lswitches
);
564 shash_destroy(&ucast_macs_rmts
);
565 SHASH_FOR_EACH (node
, &mcast_macs_rmts
) {
566 struct mmr_hash_node_data
*mmr_ext
= node
->data
;
567 shash_destroy(&mmr_ext
->physical_locators
);
570 shash_destroy(&mcast_macs_rmts
);
571 shash_destroy(&physical_locators
);
572 shash_destroy(&vtep_pbs
);
573 shash_destroy(&non_vtep_pbs
);
576 /* Cleans up all related entries in vtep. Returns true when done (i.e. there
577 * is no change made to 'ctx->vtep_idl'), otherwise returns false. */
579 vtep_cleanup(struct controller_vtep_ctx
*ctx
)
581 if (!ctx
->vtep_idl_txn
) {
587 ovsdb_idl_txn_add_comment(ctx
->vtep_idl_txn
,
588 "ovn-controller-vtep: cleaning up vtep "
590 all_done
= vtep_lswitch_cleanup(ctx
->vtep_idl
);
591 all_done
= vtep_ucast_macs_cleanup(ctx
->vtep_idl
) && all_done
;
592 all_done
= vtep_mcast_macs_cleanup(ctx
->vtep_idl
) && all_done
;