]> git.proxmox.com Git - ovs.git/blame - ovn/northd/ovn-northd.c
Merge "master" into "ovn".
[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
bd39395f
BP
205/* Appends port security constraints on L2 address field 'eth_addr_field'
206 * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with
207 * 'n_port_security' elements, is the collection of port_security constraints
f7cb14cd 208 * from an OVN_NB Logical_Port row. */
bd39395f
BP
209static void
210build_port_security(const char *eth_addr_field,
211 char **port_security, size_t n_port_security,
212 struct ds *match)
213{
214 size_t base_len = match->length;
215 ds_put_format(match, " && %s == {", eth_addr_field);
216
217 size_t n = 0;
218 for (size_t i = 0; i < n_port_security; i++) {
f7cb14cd
BP
219 uint8_t ea[ETH_ADDR_LEN];
220
221 if (eth_addr_from_string(port_security[i], ea)) {
222 ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(ea));
bd39395f
BP
223 ds_put_char(match, ' ');
224 n++;
225 }
226 }
f7cb14cd 227 ds_chomp(match, ' ');
bd39395f 228 ds_put_cstr(match, "}");
4edcdcf4 229
bd39395f
BP
230 if (!n) {
231 match->length = base_len;
232 }
233}
234
235/* Updates the Pipeline table in the OVN_SB database, constructing its contents
236 * based on the OVN_NB database. */
237static void
238build_pipeline(struct northd_context *ctx)
239{
240 struct pipeline_ctx pc = {
241 .ovnsb_idl = ctx->ovnsb_idl,
242 .ovnsb_txn = ctx->ovnsb_txn,
243 .pipeline_hmap = HMAP_INITIALIZER(&pc.pipeline_hmap)
244 };
245
246 /* Add all the Pipeline entries currently in the southbound database to
247 * 'pc.pipeline_hmap'. We remove entries that we generate from the hmap,
248 * thus by the time we're done only entries that need to be removed
249 * remain. */
250 const struct sbrec_pipeline *pipeline;
251 SBREC_PIPELINE_FOR_EACH (pipeline, ctx->ovnsb_idl) {
252 struct pipeline_hash_node *hash_node = xzalloc(sizeof *hash_node);
253 hash_node->pipeline = pipeline;
254 hmap_insert(&pc.pipeline_hmap, &hash_node->node,
255 pipeline_hash_rec(pipeline));
256 }
257
258 /* Table 0: Admission control framework. */
259 const struct nbrec_logical_switch *lswitch;
260 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
261 /* Logical VLANs not supported. */
5e3b3449 262 pipeline_add(&pc, lswitch, 0, 100, "vlan.present", "drop;");
bd39395f
BP
263
264 /* Broadcast/multicast source address is invalid. */
5e3b3449 265 pipeline_add(&pc, lswitch, 0, 100, "eth.src[40]", "drop;");
bd39395f 266
35060cdc
BP
267 /* Port security flows have priority 50 (see below) and will continue
268 * to the next table if packet source is acceptable. */
bd39395f
BP
269
270 /* Otherwise drop the packet. */
5e3b3449 271 pipeline_add(&pc, lswitch, 0, 0, "1", "drop;");
bd39395f
BP
272 }
273
274 /* Table 0: Ingress port security. */
275 const struct nbrec_logical_port *lport;
276 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
277 struct ds match = DS_EMPTY_INITIALIZER;
278 ds_put_cstr(&match, "inport == ");
279 json_string_escape(lport->name, &match);
280 build_port_security("eth.src",
281 lport->port_security, lport->n_port_security,
282 &match);
35060cdc 283 pipeline_add(&pc, lport->lswitch, 0, 50, ds_cstr(&match), "next;");
bd39395f
BP
284 ds_destroy(&match);
285 }
286
287 /* Table 1: Destination lookup, broadcast and multicast handling (priority
288 * 100). */
289 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
290 struct ds actions;
291
292 ds_init(&actions);
293 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
294 if (lport->lswitch == lswitch) {
295 ds_put_cstr(&actions, "outport = ");
296 json_string_escape(lport->name, &actions);
35060cdc 297 ds_put_cstr(&actions, "; next; ");
bd39395f
BP
298 }
299 }
300 ds_chomp(&actions, ' ');
301
302 pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&actions));
303 ds_destroy(&actions);
304 }
305
306 /* Table 1: Destination lookup, unicast handling (priority 50), */
2fd81197
BP
307 struct unknown_actions {
308 struct hmap_node hmap_node;
309 const struct nbrec_logical_switch *ls;
310 struct ds actions;
311 };
312 struct hmap unknown_actions = HMAP_INITIALIZER(&unknown_actions);
bd39395f 313 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
2fd81197 314 lswitch = lport->lswitch;
bd39395f
BP
315 for (size_t i = 0; i < lport->n_macs; i++) {
316 uint8_t mac[ETH_ADDR_LEN];
317
318 if (eth_addr_from_string(lport->macs[i], mac)) {
319 struct ds match, actions;
320
321 ds_init(&match);
322 ds_put_format(&match, "eth.dst == %s", lport->macs[i]);
323
324 ds_init(&actions);
325 ds_put_cstr(&actions, "outport = ");
326 json_string_escape(lport->name, &actions);
35060cdc 327 ds_put_cstr(&actions, "; next;");
2fd81197 328 pipeline_add(&pc, lswitch, 1, 50,
bd39395f
BP
329 ds_cstr(&match), ds_cstr(&actions));
330 ds_destroy(&actions);
331 ds_destroy(&match);
332 } else if (!strcmp(lport->macs[i], "unknown")) {
2fd81197
BP
333 const struct uuid *uuid = &lswitch->header_.uuid;
334 struct unknown_actions *ua = NULL;
335 struct unknown_actions *iter;
336 HMAP_FOR_EACH_WITH_HASH (iter, hmap_node, uuid_hash(uuid),
337 &unknown_actions) {
338 if (uuid_equals(&iter->ls->header_.uuid, uuid)) {
339 ua = iter;
340 break;
341 }
342 }
343 if (!ua) {
344 ua = xmalloc(sizeof *ua);
345 hmap_insert(&unknown_actions, &ua->hmap_node,
346 uuid_hash(uuid));
347 ua->ls = lswitch;
348 ds_init(&ua->actions);
349 } else {
350 ds_put_char(&ua->actions, ' ');
351 }
352
353 ds_put_cstr(&ua->actions, "outport = ");
354 json_string_escape(lport->name, &ua->actions);
35060cdc 355 ds_put_cstr(&ua->actions, "; next;");
bd39395f
BP
356 } else {
357 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
358
359 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
360 lport->name, lport->macs[i]);
361 }
362 }
363 }
364
365 /* Table 1: Destination lookup for unknown MACs (priority 0). */
2fd81197
BP
366 struct unknown_actions *ua, *next_ua;
367 HMAP_FOR_EACH_SAFE (ua, next_ua, hmap_node, &unknown_actions) {
368 pipeline_add(&pc, ua->ls, 1, 0, "1", ds_cstr(&ua->actions));
369 hmap_remove(&unknown_actions, &ua->hmap_node);
370 ds_destroy(&ua->actions);
371 free(ua);
bd39395f 372 }
2fd81197 373 hmap_destroy(&unknown_actions);
bd39395f
BP
374
375 /* Table 2: ACLs. */
376 const struct nbrec_acl *acl;
377 NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
378 const char *action;
379
380 action = (!strcmp(acl->action, "allow") ||
5e3b3449 381 !strcmp(acl->action, "allow-related"))
35060cdc 382 ? "next;" : "drop;";
bd39395f
BP
383 pipeline_add(&pc, acl->lswitch, 2, acl->priority, acl->match, action);
384 }
385 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
35060cdc 386 pipeline_add(&pc, lswitch, 2, 0, "1", "next;");
bd39395f
BP
387 }
388
389 /* Table 3: Egress port security. */
2f60b7b7
BP
390 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
391 pipeline_add(&pc, lswitch, 3, 100, "eth.dst[40]", "output;");
392 }
bd39395f 393 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
35060cdc 394 struct ds match;
bd39395f
BP
395
396 ds_init(&match);
397 ds_put_cstr(&match, "outport == ");
398 json_string_escape(lport->name, &match);
399 build_port_security("eth.dst",
400 lport->port_security, lport->n_port_security,
401 &match);
402
35060cdc 403 pipeline_add(&pc, lport->lswitch, 3, 50, ds_cstr(&match), "output;");
bd39395f 404
bd39395f
BP
405 ds_destroy(&match);
406 }
407
408 /* Delete any existing Pipeline rows that were not re-generated. */
409 struct pipeline_hash_node *hash_node, *next_hash_node;
410 HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &pc.pipeline_hmap) {
411 hmap_remove(&pc.pipeline_hmap, &hash_node->node);
412 sbrec_pipeline_delete(hash_node->pipeline);
413 free(hash_node);
414 }
415 hmap_destroy(&pc.pipeline_hmap);
416}
417\f
48f42f3a 418static bool
e387e3e8 419parents_equal(const struct sbrec_binding *binding,
48f42f3a
RB
420 const struct nbrec_logical_port *lport)
421{
422 if (!!binding->parent_port != !!lport->parent_name) {
423 /* One is set and the other is not. */
424 return false;
425 }
426
427 if (binding->parent_port) {
428 /* Both are set. */
429 return strcmp(binding->parent_port, lport->parent_name) ? false : true;
430 }
431
432 /* Both are NULL. */
433 return true;
434}
435
436static bool
e387e3e8 437tags_equal(const struct sbrec_binding *binding,
48f42f3a
RB
438 const struct nbrec_logical_port *lport)
439{
440 if (binding->n_tag != lport->n_tag) {
441 return false;
442 }
443
444 return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
445}
446
eb00399e
BP
447struct binding_hash_node {
448 struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */
449 struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */
e387e3e8 450 const struct sbrec_binding *binding;
eb00399e
BP
451};
452
453static bool
454tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
455{
456 const struct binding_hash_node *hash_node;
457
458 HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0),
459 tk_hmap) {
460 if (hash_node->binding->tunnel_key == tunnel_key) {
461 return true;
462 }
463 }
464 return false;
465}
466
467/* Chooses and returns a positive tunnel key that is not already in use in
468 * 'tk_hmap'. Returns 0 if all tunnel keys are in use. */
469static uint16_t
470choose_tunnel_key(const struct hmap *tk_hmap)
471{
472 static uint16_t prev;
473
474 for (uint16_t key = prev + 1; key != prev; key++) {
475 if (!tunnel_key_in_use(tk_hmap, key)) {
476 prev = key;
477 return key;
478 }
479 }
480
481 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
482 VLOG_WARN_RL(&rl, "all tunnel keys exhausted");
483 return 0;
484}
485
4edcdcf4
RB
486/*
487 * When a change has occurred in the OVN_Northbound database, we go through and
e387e3e8 488 * make sure that the contents of the Binding table in the OVN_Southbound
ec78987f
JP
489 * database are up to date with the logical ports defined in the
490 * OVN_Northbound database.
4edcdcf4 491 */
ac0630a2 492static void
2e2762d4 493set_bindings(struct northd_context *ctx)
ac0630a2 494{
e387e3e8 495 const struct sbrec_binding *binding;
4edcdcf4
RB
496 const struct nbrec_logical_port *lport;
497
4edcdcf4
RB
498 /*
499 * We will need to look up a binding for every logical port. We don't want
500 * to have to do an O(n) search for every binding, so start out by hashing
501 * them on the logical port.
502 *
503 * As we go through every logical port, we will update the binding if it
eb00399e
BP
504 * exists or create one otherwise. When the update is done, we'll remove
505 * it from the hashmap. At the end, any bindings left in the hashmap are
506 * for logical ports that have been deleted.
507 *
508 * We index the logical_port column because that's the shared key between
509 * the OVN_NB and OVN_SB databases. We index the tunnel_key column to
510 * allow us to choose a unique tunnel key for any Binding rows we have to
511 * add.
4edcdcf4 512 */
eb00399e
BP
513 struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap);
514 struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap);
4edcdcf4 515
e387e3e8 516 SBREC_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
eb00399e 517 struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
4edcdcf4 518 hash_node->binding = binding;
eb00399e
BP
519 hmap_insert(&lp_hmap, &hash_node->lp_node,
520 hash_string(binding->logical_port, 0));
521 hmap_insert(&tk_hmap, &hash_node->tk_node,
522 hash_int(binding->tunnel_key, 0));
4edcdcf4
RB
523 }
524
525 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
eb00399e 526 struct binding_hash_node *hash_node;
1d4e6b55 527 binding = NULL;
eb00399e
BP
528 HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
529 hash_string(lport->name, 0), &lp_hmap) {
4edcdcf4 530 if (!strcmp(lport->name, hash_node->binding->logical_port)) {
1d4e6b55 531 binding = hash_node->binding;
4edcdcf4
RB
532 break;
533 }
534 }
535
03f455bd
BP
536 struct uuid logical_datapath;
537 if (lport->lswitch) {
538 logical_datapath = lport->lswitch->header_.uuid;
539 } else {
540 uuid_zero(&logical_datapath);
541 }
542
1d4e6b55 543 if (binding) {
4edcdcf4 544 /* We found an existing binding for this logical port. Update its
48f42f3a 545 * contents. */
4edcdcf4 546
eb00399e 547 hmap_remove(&lp_hmap, &hash_node->lp_node);
4edcdcf4
RB
548
549 if (!macs_equal(binding->mac, binding->n_mac,
550 lport->macs, lport->n_macs)) {
e387e3e8 551 sbrec_binding_set_mac(binding,
4edcdcf4
RB
552 (const char **) lport->macs, lport->n_macs);
553 }
48f42f3a 554 if (!parents_equal(binding, lport)) {
e387e3e8 555 sbrec_binding_set_parent_port(binding, lport->parent_name);
48f42f3a
RB
556 }
557 if (!tags_equal(binding, lport)) {
e387e3e8 558 sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
48f42f3a 559 }
03f455bd 560 if (!uuid_equals(&binding->logical_datapath, &logical_datapath)) {
e387e3e8 561 sbrec_binding_set_logical_datapath(binding,
03f455bd
BP
562 logical_datapath);
563 }
4edcdcf4
RB
564 } else {
565 /* There is no binding for this logical port, so create one. */
566
eb00399e
BP
567 uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
568 if (!tunnel_key) {
569 continue;
570 }
571
e387e3e8
BP
572 binding = sbrec_binding_insert(ctx->ovnsb_txn);
573 sbrec_binding_set_logical_port(binding, lport->name);
574 sbrec_binding_set_mac(binding,
4edcdcf4 575 (const char **) lport->macs, lport->n_macs);
48f42f3a 576 if (lport->parent_name && lport->n_tag > 0) {
e387e3e8
BP
577 sbrec_binding_set_parent_port(binding, lport->parent_name);
578 sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
48f42f3a 579 }
03f455bd 580
e387e3e8
BP
581 sbrec_binding_set_tunnel_key(binding, tunnel_key);
582 sbrec_binding_set_logical_datapath(binding, logical_datapath);
eb00399e
BP
583
584 /* Add the tunnel key to the tk_hmap so that we don't try to use it
585 * for another port. (We don't want it in the lp_hmap because that
e387e3e8 586 * would just get the Binding record deleted later.) */
eb00399e
BP
587 struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
588 hash_node->binding = binding;
589 hmap_insert(&tk_hmap, &hash_node->tk_node,
590 hash_int(binding->tunnel_key, 0));
4edcdcf4
RB
591 }
592 }
593
eb00399e
BP
594 struct binding_hash_node *hash_node;
595 HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
596 hmap_remove(&lp_hmap, &hash_node->lp_node);
e387e3e8 597 sbrec_binding_delete(hash_node->binding);
eb00399e
BP
598 }
599 hmap_destroy(&lp_hmap);
600
601 struct binding_hash_node *hash_node_next;
602 HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) {
603 hmap_remove(&tk_hmap, &hash_node->tk_node);
4edcdcf4
RB
604 free(hash_node);
605 }
eb00399e 606 hmap_destroy(&tk_hmap);
4edcdcf4
RB
607}
608
609static void
2e2762d4 610ovnnb_db_changed(struct northd_context *ctx)
4edcdcf4 611{
c29734fc 612 VLOG_DBG("ovn-nb db contents have changed.");
4edcdcf4
RB
613
614 set_bindings(ctx);
bd39395f 615 build_pipeline(ctx);
ac0630a2
RB
616}
617
f93818dd
RB
618/*
619 * The only change we get notified about is if the 'chassis' column of the
e387e3e8 620 * 'Binding' table changes. When this column is not empty, it means we need to
f93818dd
RB
621 * set the corresponding logical port as 'up' in the northbound DB.
622 */
ac0630a2 623static void
2e2762d4 624ovnsb_db_changed(struct northd_context *ctx)
ac0630a2 625{
fc3113bc 626 struct hmap lports_hmap;
e387e3e8 627 const struct sbrec_binding *binding;
fc3113bc
RB
628 const struct nbrec_logical_port *lport;
629
630 struct lport_hash_node {
631 struct hmap_node node;
632 const struct nbrec_logical_port *lport;
633 } *hash_node, *hash_node_next;
f93818dd
RB
634
635 VLOG_DBG("Recalculating port up states for ovn-nb db.");
636
fc3113bc 637 hmap_init(&lports_hmap);
f93818dd 638
fc3113bc
RB
639 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
640 hash_node = xzalloc(sizeof *hash_node);
641 hash_node->lport = lport;
642 hmap_insert(&lports_hmap, &hash_node->node,
643 hash_string(lport->name, 0));
644 }
645
e387e3e8 646 SBREC_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
fc3113bc
RB
647 lport = NULL;
648 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
649 hash_string(binding->logical_port, 0), &lports_hmap) {
650 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
651 lport = hash_node->lport;
652 break;
653 }
f93818dd
RB
654 }
655
f93818dd 656 if (!lport) {
2e2762d4
BP
657 /* The logical port doesn't exist for this binding. This can
658 * happen under normal circumstances when ovn-northd hasn't gotten
659 * around to pruning the Binding yet. */
f93818dd
RB
660 continue;
661 }
662
fc3113bc 663 if (*binding->chassis && (!lport->up || !*lport->up)) {
f93818dd
RB
664 bool up = true;
665 nbrec_logical_port_set_up(lport, &up, 1);
fc3113bc 666 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
f93818dd
RB
667 bool up = false;
668 nbrec_logical_port_set_up(lport, &up, 1);
669 }
670 }
fc3113bc
RB
671
672 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
673 hmap_remove(&lports_hmap, &hash_node->node);
674 free(hash_node);
675 }
676 hmap_destroy(&lports_hmap);
ac0630a2
RB
677}
678\f
679static const char *
680default_db(void)
681{
682 static char *def;
683 if (!def) {
684 def = xasprintf("unix:%s/db.sock", ovs_rundir());
685 }
686 return def;
687}
688
689static void
690parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
691{
692 enum {
67d9b930 693 DAEMON_OPTION_ENUMS,
ac0630a2
RB
694 VLOG_OPTION_ENUMS,
695 };
696 static const struct option long_options[] = {
ec78987f 697 {"ovnsb-db", required_argument, NULL, 'd'},
ac0630a2
RB
698 {"ovnnb-db", required_argument, NULL, 'D'},
699 {"help", no_argument, NULL, 'h'},
700 {"options", no_argument, NULL, 'o'},
701 {"version", no_argument, NULL, 'V'},
67d9b930 702 DAEMON_LONG_OPTIONS,
ac0630a2
RB
703 VLOG_LONG_OPTIONS,
704 STREAM_SSL_LONG_OPTIONS,
705 {NULL, 0, NULL, 0},
706 };
707 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
708
709 for (;;) {
710 int c;
711
712 c = getopt_long(argc, argv, short_options, long_options, NULL);
713 if (c == -1) {
714 break;
715 }
716
717 switch (c) {
67d9b930 718 DAEMON_OPTION_HANDLERS;
ac0630a2
RB
719 VLOG_OPTION_HANDLERS;
720 STREAM_SSL_OPTION_HANDLERS;
721
722 case 'd':
ec78987f 723 ovnsb_db = optarg;
ac0630a2
RB
724 break;
725
726 case 'D':
727 ovnnb_db = optarg;
728 break;
729
730 case 'h':
731 usage();
732 exit(EXIT_SUCCESS);
733
734 case 'o':
735 ovs_cmdl_print_options(long_options);
736 exit(EXIT_SUCCESS);
737
738 case 'V':
739 ovs_print_version(0, 0);
740 exit(EXIT_SUCCESS);
741
742 default:
743 break;
744 }
745 }
746
ec78987f
JP
747 if (!ovnsb_db) {
748 ovnsb_db = default_db();
ac0630a2
RB
749 }
750
751 if (!ovnnb_db) {
752 ovnnb_db = default_db();
753 }
754
755 free(short_options);
756}
757
758int
759main(int argc, char *argv[])
760{
761 extern struct vlog_module VLM_reconnect;
ec78987f 762 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
ac0630a2
RB
763 unsigned int ovnnb_seqno, ovn_seqno;
764 int res = EXIT_SUCCESS;
2e2762d4 765 struct northd_context ctx = {
3c78b3ca 766 .ovnsb_txn = NULL,
f93818dd
RB
767 };
768 bool ovnnb_changes_pending = false;
769 bool ovn_changes_pending = false;
ac0630a2
RB
770
771 fatal_ignore_sigpipe();
772 set_program_name(argv[0]);
773 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
774 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
775 parse_options(argc, argv);
67d9b930
RB
776
777 daemonize();
778
ac0630a2 779 nbrec_init();
ec78987f 780 sbrec_init();
ac0630a2
RB
781
782 /* We want to detect all changes to the ovn-nb db. */
f93818dd
RB
783 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
784 &nbrec_idl_class, true, true);
ac0630a2 785
91ae2065 786 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
a0149f47 787 * has to care about, so we'll enable monitoring those directly. */
ec78987f
JP
788 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
789 &sbrec_idl_class, false, true);
e387e3e8
BP
790 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_binding);
791 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_logical_port);
792 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_chassis);
793 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_mac);
794 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_tag);
795 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_parent_port);
796 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_logical_datapath);
797 ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_tunnel_key);
bd39395f
BP
798 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
799 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
800 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
801 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_table_id);
802 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_priority);
803 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_priority);
804 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_match);
805 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_match);
806 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_actions);
807 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_actions);
ac0630a2
RB
808
809 /*
810 * The loop here just runs the IDL in a loop waiting for the seqno to
811 * change, which indicates that the contents of the db have changed.
812 *
a0149f47
JP
813 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
814 * db must be recalculated.
ac0630a2 815 *
a0149f47 816 * If the contents of the ovn-sb db change, it means the 'up' state of
91ae2065 817 * a port may have changed, as that's the only type of change ovn-northd is
a0149f47 818 * watching for.
ac0630a2
RB
819 */
820
821 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
ec78987f 822 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
ac0630a2
RB
823 for (;;) {
824 ovsdb_idl_run(ovnnb_idl);
ec78987f 825 ovsdb_idl_run(ovnsb_idl);
ac0630a2
RB
826
827 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
828 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
829 VLOG_ERR("%s: database connection failed (%s)",
830 ovnnb_db, ovs_retval_to_string(retval));
831 res = EXIT_FAILURE;
832 break;
833 }
834
ec78987f
JP
835 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
836 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
ac0630a2 837 VLOG_ERR("%s: database connection failed (%s)",
ec78987f 838 ovnsb_db, ovs_retval_to_string(retval));
ac0630a2
RB
839 res = EXIT_FAILURE;
840 break;
841 }
842
843 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
844 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
f93818dd 845 ovnnb_changes_pending = true;
ac0630a2
RB
846 }
847
ec78987f
JP
848 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
849 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
f93818dd
RB
850 ovn_changes_pending = true;
851 }
852
853 /*
854 * If there are any pending changes, we delay recalculating the
855 * necessary updates until after an existing transaction finishes.
91ae2065
RB
856 * This avoids the possibility of rapid updates causing ovn-northd to
857 * never be able to successfully make the corresponding updates to the
858 * other db. Instead, pending changes are batched up until the next
859 * time we get a chance to calculate the new state and apply it.
f93818dd
RB
860 */
861
3c78b3ca 862 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
f93818dd
RB
863 /*
864 * The OVN-nb db contents have changed, so create a transaction for
a0149f47 865 * updating the OVN-sb DB.
f93818dd 866 */
3c78b3ca 867 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
5da82071 868 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
91ae2065 869 "ovn-northd: northbound db changed");
f93818dd
RB
870 ovnnb_db_changed(&ctx);
871 ovnnb_changes_pending = false;
872 }
873
874 if (ovn_changes_pending && !ctx.ovnnb_txn) {
875 /*
a0149f47 876 * The OVN-sb db contents have changed, so create a transaction for
f93818dd
RB
877 * updating the northbound DB.
878 */
879 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
5da82071 880 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
91ae2065 881 "ovn-northd: southbound db changed");
ec78987f 882 ovnsb_db_changed(&ctx);
f93818dd
RB
883 ovn_changes_pending = false;
884 }
885
886 if (ctx.ovnnb_txn) {
887 enum ovsdb_idl_txn_status txn_status;
888 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
889 switch (txn_status) {
890 case TXN_UNCOMMITTED:
891 case TXN_INCOMPLETE:
892 /* Come back around and try to commit this transaction again */
893 break;
894 case TXN_ABORTED:
895 case TXN_TRY_AGAIN:
896 case TXN_NOT_LOCKED:
897 case TXN_ERROR:
898 /* Something went wrong, so try creating a new transaction. */
899 ovn_changes_pending = true;
900 case TXN_UNCHANGED:
901 case TXN_SUCCESS:
902 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
903 ctx.ovnnb_txn = NULL;
904 }
905 }
906
3c78b3ca 907 if (ctx.ovnsb_txn) {
f93818dd 908 enum ovsdb_idl_txn_status txn_status;
3c78b3ca 909 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
f93818dd
RB
910 switch (txn_status) {
911 case TXN_UNCOMMITTED:
912 case TXN_INCOMPLETE:
913 /* Come back around and try to commit this transaction again */
914 break;
915 case TXN_ABORTED:
916 case TXN_TRY_AGAIN:
917 case TXN_NOT_LOCKED:
918 case TXN_ERROR:
919 /* Something went wrong, so try creating a new transaction. */
920 ovnnb_changes_pending = true;
921 case TXN_UNCHANGED:
922 case TXN_SUCCESS:
3c78b3ca
JP
923 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
924 ctx.ovnsb_txn = NULL;
f93818dd 925 }
ac0630a2
RB
926 }
927
928 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
ec78987f 929 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
ac0630a2 930 ovsdb_idl_wait(ovnnb_idl);
ec78987f 931 ovsdb_idl_wait(ovnsb_idl);
f93818dd
RB
932 if (ctx.ovnnb_txn) {
933 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
934 }
3c78b3ca
JP
935 if (ctx.ovnsb_txn) {
936 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
f93818dd 937 }
ac0630a2
RB
938 poll_block();
939 }
940 }
941
ec78987f 942 ovsdb_idl_destroy(ovnsb_idl);
ac0630a2
RB
943 ovsdb_idl_destroy(ovnnb_idl);
944
945 exit(res);
946}