]> git.proxmox.com Git - mirror_ovs.git/blame - ovn/northd/ovn-northd.c
ovn-northd: Fix treatment of ports with unknown MACs.
[mirror_ovs.git] / ovn / northd / ovn-northd.c
CommitLineData
ac0630a2
RB
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at:
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15#include <config.h>
16
17#include <getopt.h>
18#include <stdlib.h>
19#include <stdio.h>
20
21#include "command-line.h"
67d9b930 22#include "daemon.h"
ac0630a2 23#include "dirs.h"
bd39395f 24#include "dynamic-string.h"
ac0630a2 25#include "fatal-signal.h"
4edcdcf4
RB
26#include "hash.h"
27#include "hmap.h"
bd39395f
BP
28#include "json.h"
29#include "ovn/lib/lex.h"
e3df8838
BP
30#include "ovn/lib/ovn-nb-idl.h"
31#include "ovn/lib/ovn-sb-idl.h"
ac0630a2
RB
32#include "poll-loop.h"
33#include "stream.h"
34#include "stream-ssl.h"
35#include "util.h"
4edcdcf4 36#include "uuid.h"
ac0630a2
RB
37#include "openvswitch/vlog.h"
38
2e2762d4 39VLOG_DEFINE_THIS_MODULE(ovn_northd);
ac0630a2 40
2e2762d4 41struct northd_context {
f93818dd 42 struct ovsdb_idl *ovnnb_idl;
ec78987f 43 struct ovsdb_idl *ovnsb_idl;
f93818dd 44 struct ovsdb_idl_txn *ovnnb_txn;
3c78b3ca 45 struct ovsdb_idl_txn *ovnsb_txn;
f93818dd
RB
46};
47
ac0630a2 48static const char *ovnnb_db;
ec78987f 49static const char *ovnsb_db;
ac0630a2
RB
50
51static const char *default_db(void);
52
53static void
54usage(void)
55{
56 printf("\
57%s: OVN northbound management daemon\n\
58usage: %s [OPTIONS]\n\
59\n\
60Options:\n\
61 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
62 (default: %s)\n\
ec78987f 63 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
ac0630a2
RB
64 (default: %s)\n\
65 -h, --help display this help message\n\
66 -o, --options list available options\n\
67 -V, --version display version information\n\
68", program_name, program_name, default_db(), default_db());
67d9b930 69 daemon_usage();
ac0630a2
RB
70 vlog_usage();
71 stream_usage("database", true, true, false);
72}
73\f
4edcdcf4
RB
74static int
75compare_strings(const void *a_, const void *b_)
76{
77 char *const *a = a_;
78 char *const *b = b_;
79 return strcmp(*a, *b);
80}
81
82/*
83 * Determine whether 2 arrays of MAC addresses are the same. It's possible that
84 * the lists could be *very* long and this check is being done a lot (every
85 * time the OVN_Northbound database changes).
86 */
87static bool
88macs_equal(char **binding_macs_, size_t b_n_macs,
89 char **lport_macs_, size_t l_n_macs)
90{
91 char **binding_macs, **lport_macs;
92 size_t bytes, i;
93
94 if (b_n_macs != l_n_macs) {
95 return false;
96 }
97
98 bytes = b_n_macs * sizeof binding_macs_[0];
99 binding_macs = xmalloc(bytes);
100 lport_macs = xmalloc(bytes);
101
102 memcpy(binding_macs, binding_macs_, bytes);
103 memcpy(lport_macs, lport_macs_, bytes);
104
105 qsort(binding_macs, b_n_macs, sizeof binding_macs[0], compare_strings);
106 qsort(lport_macs, l_n_macs, sizeof lport_macs[0], compare_strings);
107
108 for (i = 0; i < b_n_macs; i++) {
109 if (strcmp(binding_macs[i], lport_macs[i])) {
110 break;
111 }
112 }
113
114 free(binding_macs);
115 free(lport_macs);
116
117 return (i == b_n_macs) ? true : false;
118}
bd39395f
BP
119\f
120/* Pipeline generation.
121 *
122 * This code generates the Pipeline table in the southbound database, as a
123 * function of most of the northbound database.
124 */
125
126/* Enough context to add a Pipeline row, using pipeline_add(). */
127struct pipeline_ctx {
128 /* From northd_context. */
129 struct ovsdb_idl *ovnsb_idl;
130 struct ovsdb_idl_txn *ovnsb_txn;
131
132 /* Contains "struct pipeline_hash_node"s. Used to figure out what existing
133 * Pipeline rows should be deleted: we index all of the Pipeline rows into
134 * this data structure, then as existing rows are generated we remove them.
135 * After generating all the rows, any remaining in 'pipeline_hmap' must be
136 * deleted from the database. */
137 struct hmap pipeline_hmap;
138};
139
140/* A row in the Pipeline table, indexed by its full contents, */
141struct pipeline_hash_node {
142 struct hmap_node node;
143 const struct sbrec_pipeline *pipeline;
144};
145
146static size_t
147pipeline_hash(const struct uuid *logical_datapath, uint8_t table_id,
148 uint16_t priority, const char *match, const char *actions)
149{
150 size_t hash = uuid_hash(logical_datapath);
151 hash = hash_2words((table_id << 16) | priority, hash);
152 hash = hash_string(match, hash);
153 return hash_string(actions, hash);
154}
155
156static size_t
157pipeline_hash_rec(const struct sbrec_pipeline *pipeline)
158{
159 return pipeline_hash(&pipeline->logical_datapath, pipeline->table_id,
160 pipeline->priority, pipeline->match,
161 pipeline->actions);
162}
163
164/* Adds a row with the specified contents to the Pipeline table. */
165static void
166pipeline_add(struct pipeline_ctx *ctx,
167 const struct nbrec_logical_switch *logical_datapath,
168 uint8_t table_id,
169 uint16_t priority,
170 const char *match,
171 const char *actions)
172{
173 struct pipeline_hash_node *hash_node;
174
175 /* Check whether such a row already exists in the Pipeline table. If so,
176 * remove it from 'ctx->pipeline_hmap' and we're done. */
177 HMAP_FOR_EACH_WITH_HASH (hash_node, node,
178 pipeline_hash(&logical_datapath->header_.uuid,
179 table_id, priority, match, actions),
180 &ctx->pipeline_hmap) {
181 const struct sbrec_pipeline *pipeline = hash_node->pipeline;
182 if (uuid_equals(&pipeline->logical_datapath,
183 &logical_datapath->header_.uuid)
184 && pipeline->table_id == table_id
185 && pipeline->priority == priority
186 && !strcmp(pipeline->match, match)
187 && !strcmp(pipeline->actions, actions)) {
188 hmap_remove(&ctx->pipeline_hmap, &hash_node->node);
189 free(hash_node);
190 return;
191 }
192 }
193
194 /* No such Pipeline row. Add one. */
195 const struct sbrec_pipeline *pipeline;
196 pipeline = sbrec_pipeline_insert(ctx->ovnsb_txn);
197 sbrec_pipeline_set_logical_datapath(pipeline,
198 logical_datapath->header_.uuid);
199 sbrec_pipeline_set_table_id(pipeline, table_id);
200 sbrec_pipeline_set_priority(pipeline, priority);
201 sbrec_pipeline_set_match(pipeline, match);
202 sbrec_pipeline_set_actions(pipeline, actions);
203}
204
205/* A single port security constraint. This is a parsed version of a single
206 * member of the port_security column in the OVN_NB Logical_Port table.
207 *
208 * Each token has type LEX_T_END if that field is missing, otherwise
209 * LEX_T_INTEGER or LEX_T_MASKED_INTEGER. */
210struct ps_constraint {
211 struct lex_token eth;
212 struct lex_token ip4;
213 struct lex_token ip6;
214};
215
216/* Parses a member of the port_security column 'ps' into 'c'. Returns true if
217 * successful, false on syntax error. */
218static bool
219parse_port_security(const char *ps, struct ps_constraint *c)
220{
221 c->eth.type = LEX_T_END;
222 c->ip4.type = LEX_T_END;
223 c->ip6.type = LEX_T_END;
224
225 struct lexer lexer;
226 lexer_init(&lexer, ps);
227 do {
228 if (lexer.token.type == LEX_T_INTEGER ||
229 lexer.token.type == LEX_T_MASKED_INTEGER) {
230 struct lex_token *t;
231
232 t = (lexer.token.format == LEX_F_IPV4 ? &c->ip4
233 : lexer.token.format == LEX_F_IPV6 ? &c->ip6
234 : lexer.token.format == LEX_F_ETHERNET ? &c->eth
235 : NULL);
236 if (t) {
237 if (t->type == LEX_T_END) {
238 *t = lexer.token;
239 } else {
240 VLOG_INFO("%s: port_security has duplicate %s address",
241 ps, lex_format_to_string(lexer.token.format));
242 }
243 lexer_get(&lexer);
244 lexer_match(&lexer, LEX_T_COMMA);
245 continue;
246 }
247 }
248
249 VLOG_INFO("%s: syntax error in port_security", ps);
250 lexer_destroy(&lexer);
251 return false;
252 } while (lexer.token.type != LEX_T_END);
253 lexer_destroy(&lexer);
254
255 return true;
256}
257
258/* Appends port security constraints on L2 address field 'eth_addr_field'
259 * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with
260 * 'n_port_security' elements, is the collection of port_security constraints
261 * from an OVN_NB Logical_Port row.
262 *
263 * (This is naive; it's not yet possible to express complete L2 and L3 port
264 * security constraints as a single Boolean expression.) */
265static void
266build_port_security(const char *eth_addr_field,
267 char **port_security, size_t n_port_security,
268 struct ds *match)
269{
270 size_t base_len = match->length;
271 ds_put_format(match, " && %s == {", eth_addr_field);
272
273 size_t n = 0;
274 for (size_t i = 0; i < n_port_security; i++) {
275 struct ps_constraint c;
276 if (parse_port_security(port_security[i], &c)
277 && c.eth.type != LEX_T_END) {
278 lex_token_format(&c.eth, match);
279 ds_put_char(match, ' ');
280 n++;
281 }
282 }
283 ds_put_cstr(match, "}");
4edcdcf4 284
bd39395f
BP
285 if (!n) {
286 match->length = base_len;
287 }
288}
289
290/* Updates the Pipeline table in the OVN_SB database, constructing its contents
291 * based on the OVN_NB database. */
292static void
293build_pipeline(struct northd_context *ctx)
294{
295 struct pipeline_ctx pc = {
296 .ovnsb_idl = ctx->ovnsb_idl,
297 .ovnsb_txn = ctx->ovnsb_txn,
298 .pipeline_hmap = HMAP_INITIALIZER(&pc.pipeline_hmap)
299 };
300
301 /* Add all the Pipeline entries currently in the southbound database to
302 * 'pc.pipeline_hmap'. We remove entries that we generate from the hmap,
303 * thus by the time we're done only entries that need to be removed
304 * remain. */
305 const struct sbrec_pipeline *pipeline;
306 SBREC_PIPELINE_FOR_EACH (pipeline, ctx->ovnsb_idl) {
307 struct pipeline_hash_node *hash_node = xzalloc(sizeof *hash_node);
308 hash_node->pipeline = pipeline;
309 hmap_insert(&pc.pipeline_hmap, &hash_node->node,
310 pipeline_hash_rec(pipeline));
311 }
312
313 /* Table 0: Admission control framework. */
314 const struct nbrec_logical_switch *lswitch;
315 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
316 /* Logical VLANs not supported. */
5e3b3449 317 pipeline_add(&pc, lswitch, 0, 100, "vlan.present", "drop;");
bd39395f
BP
318
319 /* Broadcast/multicast source address is invalid. */
5e3b3449 320 pipeline_add(&pc, lswitch, 0, 100, "eth.src[40]", "drop;");
bd39395f
BP
321
322 /* Port security flows have priority 50 (see below) and will resubmit
323 * if packet source is acceptable. */
324
325 /* Otherwise drop the packet. */
5e3b3449 326 pipeline_add(&pc, lswitch, 0, 0, "1", "drop;");
bd39395f
BP
327 }
328
329 /* Table 0: Ingress port security. */
330 const struct nbrec_logical_port *lport;
331 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
332 struct ds match = DS_EMPTY_INITIALIZER;
333 ds_put_cstr(&match, "inport == ");
334 json_string_escape(lport->name, &match);
335 build_port_security("eth.src",
336 lport->port_security, lport->n_port_security,
337 &match);
5e3b3449 338 pipeline_add(&pc, lport->lswitch, 0, 50, ds_cstr(&match), "resubmit;");
bd39395f
BP
339 ds_destroy(&match);
340 }
341
342 /* Table 1: Destination lookup, broadcast and multicast handling (priority
343 * 100). */
344 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
345 struct ds actions;
346
347 ds_init(&actions);
348 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
349 if (lport->lswitch == lswitch) {
350 ds_put_cstr(&actions, "outport = ");
351 json_string_escape(lport->name, &actions);
352 ds_put_cstr(&actions, "; resubmit; ");
353 }
354 }
355 ds_chomp(&actions, ' ');
356
357 pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&actions));
358 ds_destroy(&actions);
359 }
360
361 /* Table 1: Destination lookup, unicast handling (priority 50), */
2fd81197
BP
362 struct unknown_actions {
363 struct hmap_node hmap_node;
364 const struct nbrec_logical_switch *ls;
365 struct ds actions;
366 };
367 struct hmap unknown_actions = HMAP_INITIALIZER(&unknown_actions);
bd39395f 368 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
2fd81197 369 lswitch = lport->lswitch;
bd39395f
BP
370 for (size_t i = 0; i < lport->n_macs; i++) {
371 uint8_t mac[ETH_ADDR_LEN];
372
373 if (eth_addr_from_string(lport->macs[i], mac)) {
374 struct ds match, actions;
375
376 ds_init(&match);
377 ds_put_format(&match, "eth.dst == %s", lport->macs[i]);
378
379 ds_init(&actions);
380 ds_put_cstr(&actions, "outport = ");
381 json_string_escape(lport->name, &actions);
382 ds_put_cstr(&actions, "; resubmit;");
2fd81197 383 pipeline_add(&pc, lswitch, 1, 50,
bd39395f
BP
384 ds_cstr(&match), ds_cstr(&actions));
385 ds_destroy(&actions);
386 ds_destroy(&match);
387 } else if (!strcmp(lport->macs[i], "unknown")) {
2fd81197
BP
388 const struct uuid *uuid = &lswitch->header_.uuid;
389 struct unknown_actions *ua = NULL;
390 struct unknown_actions *iter;
391 HMAP_FOR_EACH_WITH_HASH (iter, hmap_node, uuid_hash(uuid),
392 &unknown_actions) {
393 if (uuid_equals(&iter->ls->header_.uuid, uuid)) {
394 ua = iter;
395 break;
396 }
397 }
398 if (!ua) {
399 ua = xmalloc(sizeof *ua);
400 hmap_insert(&unknown_actions, &ua->hmap_node,
401 uuid_hash(uuid));
402 ua->ls = lswitch;
403 ds_init(&ua->actions);
404 } else {
405 ds_put_char(&ua->actions, ' ');
406 }
407
408 ds_put_cstr(&ua->actions, "outport = ");
409 json_string_escape(lport->name, &ua->actions);
410 ds_put_cstr(&ua->actions, "; resubmit;");
bd39395f
BP
411 } else {
412 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
413
414 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
415 lport->name, lport->macs[i]);
416 }
417 }
418 }
419
420 /* Table 1: Destination lookup for unknown MACs (priority 0). */
2fd81197
BP
421 struct unknown_actions *ua, *next_ua;
422 HMAP_FOR_EACH_SAFE (ua, next_ua, hmap_node, &unknown_actions) {
423 pipeline_add(&pc, ua->ls, 1, 0, "1", ds_cstr(&ua->actions));
424 hmap_remove(&unknown_actions, &ua->hmap_node);
425 ds_destroy(&ua->actions);
426 free(ua);
bd39395f 427 }
2fd81197 428 hmap_destroy(&unknown_actions);
bd39395f
BP
429
430 /* Table 2: ACLs. */
431 const struct nbrec_acl *acl;
432 NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
433 const char *action;
434
435 action = (!strcmp(acl->action, "allow") ||
5e3b3449
JP
436 !strcmp(acl->action, "allow-related"))
437 ? "resubmit;" : "drop;";
bd39395f
BP
438 pipeline_add(&pc, acl->lswitch, 2, acl->priority, acl->match, action);
439 }
440 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
5e3b3449 441 pipeline_add(&pc, lswitch, 2, 0, "1", "resubmit;");
bd39395f
BP
442 }
443
444 /* Table 3: Egress port security. */
445 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
446 struct ds match, actions;
447
448 ds_init(&match);
449 ds_put_cstr(&match, "outport == ");
450 json_string_escape(lport->name, &match);
451 build_port_security("eth.dst",
452 lport->port_security, lport->n_port_security,
453 &match);
454
455 ds_init(&actions);
456 ds_put_cstr(&actions, "output(");
457 json_string_escape(lport->name, &actions);
5e3b3449 458 ds_put_cstr(&actions, ");");
bd39395f
BP
459
460 pipeline_add(&pc, lport->lswitch, 3, 50,
461 ds_cstr(&match), ds_cstr(&actions));
462
463 ds_destroy(&actions);
464 ds_destroy(&match);
465 }
466
467 /* Delete any existing Pipeline rows that were not re-generated. */
468 struct pipeline_hash_node *hash_node, *next_hash_node;
469 HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &pc.pipeline_hmap) {
470 hmap_remove(&pc.pipeline_hmap, &hash_node->node);
471 sbrec_pipeline_delete(hash_node->pipeline);
472 free(hash_node);
473 }
474 hmap_destroy(&pc.pipeline_hmap);
475}
476\f
48f42f3a
RB
477static bool
478parents_equal(const struct sbrec_bindings *binding,
479 const struct nbrec_logical_port *lport)
480{
481 if (!!binding->parent_port != !!lport->parent_name) {
482 /* One is set and the other is not. */
483 return false;
484 }
485
486 if (binding->parent_port) {
487 /* Both are set. */
488 return strcmp(binding->parent_port, lport->parent_name) ? false : true;
489 }
490
491 /* Both are NULL. */
492 return true;
493}
494
495static bool
496tags_equal(const struct sbrec_bindings *binding,
497 const struct nbrec_logical_port *lport)
498{
499 if (binding->n_tag != lport->n_tag) {
500 return false;
501 }
502
503 return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
504}
505
4edcdcf4
RB
506/*
507 * When a change has occurred in the OVN_Northbound database, we go through and
ec78987f
JP
508 * make sure that the contents of the Bindings table in the OVN_Southbound
509 * database are up to date with the logical ports defined in the
510 * OVN_Northbound database.
4edcdcf4 511 */
ac0630a2 512static void
2e2762d4 513set_bindings(struct northd_context *ctx)
ac0630a2 514{
4edcdcf4 515 struct hmap bindings_hmap;
ec78987f 516 const struct sbrec_bindings *binding;
4edcdcf4
RB
517 const struct nbrec_logical_port *lport;
518
519 struct binding_hash_node {
520 struct hmap_node node;
ec78987f 521 const struct sbrec_bindings *binding;
4edcdcf4
RB
522 } *hash_node, *hash_node_next;
523
524 /*
525 * We will need to look up a binding for every logical port. We don't want
526 * to have to do an O(n) search for every binding, so start out by hashing
527 * them on the logical port.
528 *
529 * As we go through every logical port, we will update the binding if it
530 * exists or create one otherwise. When the update is done, we'll remove it
531 * from the hashmap. At the end, any bindings left in the hashmap are for
532 * logical ports that have been deleted.
533 */
534 hmap_init(&bindings_hmap);
535
ec78987f 536 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
cf1486e0 537 hash_node = xzalloc(sizeof *hash_node);
4edcdcf4
RB
538 hash_node->binding = binding;
539 hmap_insert(&bindings_hmap, &hash_node->node,
540 hash_string(binding->logical_port, 0));
541 }
542
543 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
1d4e6b55 544 binding = NULL;
4edcdcf4
RB
545 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
546 hash_string(lport->name, 0), &bindings_hmap) {
547 if (!strcmp(lport->name, hash_node->binding->logical_port)) {
1d4e6b55 548 binding = hash_node->binding;
4edcdcf4
RB
549 break;
550 }
551 }
552
1d4e6b55 553 if (binding) {
4edcdcf4 554 /* We found an existing binding for this logical port. Update its
48f42f3a 555 * contents. */
4edcdcf4 556
4edcdcf4
RB
557 hmap_remove(&bindings_hmap, &hash_node->node);
558 free(hash_node);
559 hash_node = NULL;
560
561 if (!macs_equal(binding->mac, binding->n_mac,
562 lport->macs, lport->n_macs)) {
ec78987f 563 sbrec_bindings_set_mac(binding,
4edcdcf4
RB
564 (const char **) lport->macs, lport->n_macs);
565 }
48f42f3a
RB
566 if (!parents_equal(binding, lport)) {
567 sbrec_bindings_set_parent_port(binding, lport->parent_name);
568 }
569 if (!tags_equal(binding, lport)) {
570 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
571 }
4edcdcf4
RB
572 } else {
573 /* There is no binding for this logical port, so create one. */
574
3c78b3ca 575 binding = sbrec_bindings_insert(ctx->ovnsb_txn);
ec78987f
JP
576 sbrec_bindings_set_logical_port(binding, lport->name);
577 sbrec_bindings_set_mac(binding,
4edcdcf4 578 (const char **) lport->macs, lport->n_macs);
48f42f3a
RB
579 if (lport->parent_name && lport->n_tag > 0) {
580 sbrec_bindings_set_parent_port(binding, lport->parent_name);
581 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
582 }
4edcdcf4
RB
583 }
584 }
585
586 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
587 hmap_remove(&bindings_hmap, &hash_node->node);
ec78987f 588 sbrec_bindings_delete(hash_node->binding);
4edcdcf4
RB
589 free(hash_node);
590 }
591 hmap_destroy(&bindings_hmap);
592}
593
594static void
2e2762d4 595ovnnb_db_changed(struct northd_context *ctx)
4edcdcf4 596{
c29734fc 597 VLOG_DBG("ovn-nb db contents have changed.");
4edcdcf4
RB
598
599 set_bindings(ctx);
bd39395f 600 build_pipeline(ctx);
ac0630a2
RB
601}
602
f93818dd
RB
603/*
604 * The only change we get notified about is if the 'chassis' column of the
605 * 'Bindings' table changes. When this column is not empty, it means we need to
606 * set the corresponding logical port as 'up' in the northbound DB.
607 */
ac0630a2 608static void
2e2762d4 609ovnsb_db_changed(struct northd_context *ctx)
ac0630a2 610{
fc3113bc
RB
611 struct hmap lports_hmap;
612 const struct sbrec_bindings *binding;
613 const struct nbrec_logical_port *lport;
614
615 struct lport_hash_node {
616 struct hmap_node node;
617 const struct nbrec_logical_port *lport;
618 } *hash_node, *hash_node_next;
f93818dd
RB
619
620 VLOG_DBG("Recalculating port up states for ovn-nb db.");
621
fc3113bc 622 hmap_init(&lports_hmap);
f93818dd 623
fc3113bc
RB
624 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
625 hash_node = xzalloc(sizeof *hash_node);
626 hash_node->lport = lport;
627 hmap_insert(&lports_hmap, &hash_node->node,
628 hash_string(lport->name, 0));
629 }
630
631 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
632 lport = NULL;
633 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
634 hash_string(binding->logical_port, 0), &lports_hmap) {
635 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
636 lport = hash_node->lport;
637 break;
638 }
f93818dd
RB
639 }
640
f93818dd 641 if (!lport) {
2e2762d4
BP
642 /* The logical port doesn't exist for this binding. This can
643 * happen under normal circumstances when ovn-northd hasn't gotten
644 * around to pruning the Binding yet. */
f93818dd
RB
645 continue;
646 }
647
fc3113bc 648 if (*binding->chassis && (!lport->up || !*lport->up)) {
f93818dd
RB
649 bool up = true;
650 nbrec_logical_port_set_up(lport, &up, 1);
fc3113bc 651 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
f93818dd
RB
652 bool up = false;
653 nbrec_logical_port_set_up(lport, &up, 1);
654 }
655 }
fc3113bc
RB
656
657 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
658 hmap_remove(&lports_hmap, &hash_node->node);
659 free(hash_node);
660 }
661 hmap_destroy(&lports_hmap);
ac0630a2
RB
662}
663\f
664static const char *
665default_db(void)
666{
667 static char *def;
668 if (!def) {
669 def = xasprintf("unix:%s/db.sock", ovs_rundir());
670 }
671 return def;
672}
673
674static void
675parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
676{
677 enum {
67d9b930 678 DAEMON_OPTION_ENUMS,
ac0630a2
RB
679 VLOG_OPTION_ENUMS,
680 };
681 static const struct option long_options[] = {
ec78987f 682 {"ovnsb-db", required_argument, NULL, 'd'},
ac0630a2
RB
683 {"ovnnb-db", required_argument, NULL, 'D'},
684 {"help", no_argument, NULL, 'h'},
685 {"options", no_argument, NULL, 'o'},
686 {"version", no_argument, NULL, 'V'},
67d9b930 687 DAEMON_LONG_OPTIONS,
ac0630a2
RB
688 VLOG_LONG_OPTIONS,
689 STREAM_SSL_LONG_OPTIONS,
690 {NULL, 0, NULL, 0},
691 };
692 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
693
694 for (;;) {
695 int c;
696
697 c = getopt_long(argc, argv, short_options, long_options, NULL);
698 if (c == -1) {
699 break;
700 }
701
702 switch (c) {
67d9b930 703 DAEMON_OPTION_HANDLERS;
ac0630a2
RB
704 VLOG_OPTION_HANDLERS;
705 STREAM_SSL_OPTION_HANDLERS;
706
707 case 'd':
ec78987f 708 ovnsb_db = optarg;
ac0630a2
RB
709 break;
710
711 case 'D':
712 ovnnb_db = optarg;
713 break;
714
715 case 'h':
716 usage();
717 exit(EXIT_SUCCESS);
718
719 case 'o':
720 ovs_cmdl_print_options(long_options);
721 exit(EXIT_SUCCESS);
722
723 case 'V':
724 ovs_print_version(0, 0);
725 exit(EXIT_SUCCESS);
726
727 default:
728 break;
729 }
730 }
731
ec78987f
JP
732 if (!ovnsb_db) {
733 ovnsb_db = default_db();
ac0630a2
RB
734 }
735
736 if (!ovnnb_db) {
737 ovnnb_db = default_db();
738 }
739
740 free(short_options);
741}
742
743int
744main(int argc, char *argv[])
745{
746 extern struct vlog_module VLM_reconnect;
ec78987f 747 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
ac0630a2
RB
748 unsigned int ovnnb_seqno, ovn_seqno;
749 int res = EXIT_SUCCESS;
2e2762d4 750 struct northd_context ctx = {
3c78b3ca 751 .ovnsb_txn = NULL,
f93818dd
RB
752 };
753 bool ovnnb_changes_pending = false;
754 bool ovn_changes_pending = false;
ac0630a2
RB
755
756 fatal_ignore_sigpipe();
757 set_program_name(argv[0]);
758 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
759 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
760 parse_options(argc, argv);
67d9b930
RB
761
762 daemonize();
763
ac0630a2 764 nbrec_init();
ec78987f 765 sbrec_init();
ac0630a2
RB
766
767 /* We want to detect all changes to the ovn-nb db. */
f93818dd
RB
768 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
769 &nbrec_idl_class, true, true);
ac0630a2 770
91ae2065 771 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
a0149f47 772 * has to care about, so we'll enable monitoring those directly. */
ec78987f
JP
773 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
774 &sbrec_idl_class, false, true);
775 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
776 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
777 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
778 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
48f42f3a
RB
779 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
780 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
bd39395f
BP
781 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
782 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
783 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
784 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_table_id);
785 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_priority);
786 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_priority);
787 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_match);
788 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_match);
789 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_actions);
790 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_actions);
ac0630a2
RB
791
792 /*
793 * The loop here just runs the IDL in a loop waiting for the seqno to
794 * change, which indicates that the contents of the db have changed.
795 *
a0149f47
JP
796 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
797 * db must be recalculated.
ac0630a2 798 *
a0149f47 799 * If the contents of the ovn-sb db change, it means the 'up' state of
91ae2065 800 * a port may have changed, as that's the only type of change ovn-northd is
a0149f47 801 * watching for.
ac0630a2
RB
802 */
803
804 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
ec78987f 805 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
ac0630a2
RB
806 for (;;) {
807 ovsdb_idl_run(ovnnb_idl);
ec78987f 808 ovsdb_idl_run(ovnsb_idl);
ac0630a2
RB
809
810 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
811 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
812 VLOG_ERR("%s: database connection failed (%s)",
813 ovnnb_db, ovs_retval_to_string(retval));
814 res = EXIT_FAILURE;
815 break;
816 }
817
ec78987f
JP
818 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
819 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
ac0630a2 820 VLOG_ERR("%s: database connection failed (%s)",
ec78987f 821 ovnsb_db, ovs_retval_to_string(retval));
ac0630a2
RB
822 res = EXIT_FAILURE;
823 break;
824 }
825
826 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
827 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
f93818dd 828 ovnnb_changes_pending = true;
ac0630a2
RB
829 }
830
ec78987f
JP
831 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
832 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
f93818dd
RB
833 ovn_changes_pending = true;
834 }
835
836 /*
837 * If there are any pending changes, we delay recalculating the
838 * necessary updates until after an existing transaction finishes.
91ae2065
RB
839 * This avoids the possibility of rapid updates causing ovn-northd to
840 * never be able to successfully make the corresponding updates to the
841 * other db. Instead, pending changes are batched up until the next
842 * time we get a chance to calculate the new state and apply it.
f93818dd
RB
843 */
844
3c78b3ca 845 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
f93818dd
RB
846 /*
847 * The OVN-nb db contents have changed, so create a transaction for
a0149f47 848 * updating the OVN-sb DB.
f93818dd 849 */
3c78b3ca 850 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
5da82071 851 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
91ae2065 852 "ovn-northd: northbound db changed");
f93818dd
RB
853 ovnnb_db_changed(&ctx);
854 ovnnb_changes_pending = false;
855 }
856
857 if (ovn_changes_pending && !ctx.ovnnb_txn) {
858 /*
a0149f47 859 * The OVN-sb db contents have changed, so create a transaction for
f93818dd
RB
860 * updating the northbound DB.
861 */
862 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
5da82071 863 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
91ae2065 864 "ovn-northd: southbound db changed");
ec78987f 865 ovnsb_db_changed(&ctx);
f93818dd
RB
866 ovn_changes_pending = false;
867 }
868
869 if (ctx.ovnnb_txn) {
870 enum ovsdb_idl_txn_status txn_status;
871 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
872 switch (txn_status) {
873 case TXN_UNCOMMITTED:
874 case TXN_INCOMPLETE:
875 /* Come back around and try to commit this transaction again */
876 break;
877 case TXN_ABORTED:
878 case TXN_TRY_AGAIN:
879 case TXN_NOT_LOCKED:
880 case TXN_ERROR:
881 /* Something went wrong, so try creating a new transaction. */
882 ovn_changes_pending = true;
883 case TXN_UNCHANGED:
884 case TXN_SUCCESS:
885 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
886 ctx.ovnnb_txn = NULL;
887 }
888 }
889
3c78b3ca 890 if (ctx.ovnsb_txn) {
f93818dd 891 enum ovsdb_idl_txn_status txn_status;
3c78b3ca 892 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
f93818dd
RB
893 switch (txn_status) {
894 case TXN_UNCOMMITTED:
895 case TXN_INCOMPLETE:
896 /* Come back around and try to commit this transaction again */
897 break;
898 case TXN_ABORTED:
899 case TXN_TRY_AGAIN:
900 case TXN_NOT_LOCKED:
901 case TXN_ERROR:
902 /* Something went wrong, so try creating a new transaction. */
903 ovnnb_changes_pending = true;
904 case TXN_UNCHANGED:
905 case TXN_SUCCESS:
3c78b3ca
JP
906 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
907 ctx.ovnsb_txn = NULL;
f93818dd 908 }
ac0630a2
RB
909 }
910
911 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
ec78987f 912 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
ac0630a2 913 ovsdb_idl_wait(ovnnb_idl);
ec78987f 914 ovsdb_idl_wait(ovnsb_idl);
f93818dd
RB
915 if (ctx.ovnnb_txn) {
916 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
917 }
3c78b3ca
JP
918 if (ctx.ovnsb_txn) {
919 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
f93818dd 920 }
ac0630a2
RB
921 poll_block();
922 }
923 }
924
ec78987f 925 ovsdb_idl_destroy(ovnsb_idl);
ac0630a2
RB
926 ovsdb_idl_destroy(ovnnb_idl);
927
928 exit(res);
929}