ovn-northd: Pass logical port type and options to ovn-sb database.
[cascardo/ovs.git] / ovn / northd / ovn-northd.c
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"
22 #include "daemon.h"
23 #include "dirs.h"
24 #include "dynamic-string.h"
25 #include "fatal-signal.h"
26 #include "hash.h"
27 #include "hmap.h"
28 #include "json.h"
29 #include "ovn/lib/lex.h"
30 #include "ovn/lib/ovn-nb-idl.h"
31 #include "ovn/lib/ovn-sb-idl.h"
32 #include "poll-loop.h"
33 #include "smap.h"
34 #include "stream.h"
35 #include "stream-ssl.h"
36 #include "unixctl.h"
37 #include "util.h"
38 #include "uuid.h"
39 #include "openvswitch/vlog.h"
40
41 VLOG_DEFINE_THIS_MODULE(ovn_northd);
42
43 static unixctl_cb_func ovn_northd_exit;
44
45 struct northd_context {
46     struct ovsdb_idl *ovnnb_idl;
47     struct ovsdb_idl *ovnsb_idl;
48     struct ovsdb_idl_txn *ovnnb_txn;
49     struct ovsdb_idl_txn *ovnsb_txn;
50 };
51
52 static const char *ovnnb_db;
53 static const char *ovnsb_db;
54
55 static const char *default_db(void);
56
57 static void
58 usage(void)
59 {
60     printf("\
61 %s: OVN northbound management daemon\n\
62 usage: %s [OPTIONS]\n\
63 \n\
64 Options:\n\
65   --ovnnb-db=DATABASE       connect to ovn-nb database at DATABASE\n\
66                             (default: %s)\n\
67   --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
68                             (default: %s)\n\
69   -h, --help                display this help message\n\
70   -o, --options             list available options\n\
71   -V, --version             display version information\n\
72 ", program_name, program_name, default_db(), default_db());
73     daemon_usage();
74     vlog_usage();
75     stream_usage("database", true, true, false);
76 }
77 \f
78 struct tnlid_node {
79     struct hmap_node hmap_node;
80     uint32_t tnlid;
81 };
82
83 static void
84 destroy_tnlids(struct hmap *tnlids)
85 {
86     struct tnlid_node *node, *next;
87     HMAP_FOR_EACH_SAFE (node, next, hmap_node, tnlids) {
88         hmap_remove(tnlids, &node->hmap_node);
89         free(node);
90     }
91     hmap_destroy(tnlids);
92 }
93
94 static void
95 add_tnlid(struct hmap *set, uint32_t tnlid)
96 {
97     struct tnlid_node *node = xmalloc(sizeof *node);
98     hmap_insert(set, &node->hmap_node, hash_int(tnlid, 0));
99     node->tnlid = tnlid;
100 }
101
102 static bool
103 tnlid_in_use(const struct hmap *set, uint32_t tnlid)
104 {
105     const struct tnlid_node *node;
106     HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_int(tnlid, 0), set) {
107         if (node->tnlid == tnlid) {
108             return true;
109         }
110     }
111     return false;
112 }
113
114 static uint32_t
115 allocate_tnlid(struct hmap *set, const char *name, uint32_t max,
116                uint32_t *hint)
117 {
118     for (uint32_t tnlid = *hint + 1; tnlid != *hint;
119          tnlid = tnlid + 1 <= max ? tnlid + 1 : 1) {
120         if (!tnlid_in_use(set, tnlid)) {
121             add_tnlid(set, tnlid);
122             *hint = tnlid;
123             return tnlid;
124         }
125     }
126
127     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
128     VLOG_WARN_RL(&rl, "all %s tunnel ids exhausted", name);
129     return 0;
130 }
131 \f
132 /* The 'key' comes from nb->header_.uuid or sb->external_ids:logical-switch. */
133 struct ovn_datapath {
134     struct hmap_node key_node;  /* Index on 'key'. */
135     struct uuid key;            /* nb->header_.uuid. */
136
137     const struct nbrec_logical_switch *nb;   /* May be NULL. */
138     const struct sbrec_datapath_binding *sb; /* May be NULL. */
139
140     struct ovs_list list;       /* In list of similar records. */
141
142     struct hmap port_tnlids;
143     uint32_t port_key_hint;
144
145     bool has_unknown;
146 };
147
148 static struct ovn_datapath *
149 ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
150                     const struct nbrec_logical_switch *nb,
151                     const struct sbrec_datapath_binding *sb)
152 {
153     struct ovn_datapath *od = xzalloc(sizeof *od);
154     od->key = *key;
155     od->sb = sb;
156     od->nb = nb;
157     hmap_init(&od->port_tnlids);
158     od->port_key_hint = 0;
159     hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
160     return od;
161 }
162
163 static void
164 ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
165 {
166     if (od) {
167         /* Don't remove od->list.  It is used within build_datapaths() as a
168          * private list and once we've exited that function it is not safe to
169          * use it. */
170         hmap_remove(datapaths, &od->key_node);
171         destroy_tnlids(&od->port_tnlids);
172         free(od);
173     }
174 }
175
176 static struct ovn_datapath *
177 ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid)
178 {
179     struct ovn_datapath *od;
180
181     HMAP_FOR_EACH_WITH_HASH (od, key_node, uuid_hash(uuid), datapaths) {
182         if (uuid_equals(uuid, &od->key)) {
183             return od;
184         }
185     }
186     return NULL;
187 }
188
189 static struct ovn_datapath *
190 ovn_datapath_from_sbrec(struct hmap *datapaths,
191                         const struct sbrec_datapath_binding *sb)
192 {
193     struct uuid key;
194
195     if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key)) {
196         return NULL;
197     }
198     return ovn_datapath_find(datapaths, &key);
199 }
200
201 static void
202 join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
203                struct ovs_list *sb_only, struct ovs_list *nb_only,
204                struct ovs_list *both)
205 {
206     hmap_init(datapaths);
207     list_init(sb_only);
208     list_init(nb_only);
209     list_init(both);
210
211     const struct sbrec_datapath_binding *sb, *sb_next;
212     SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
213         struct uuid key;
214         if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key)) {
215             ovsdb_idl_txn_add_comment(ctx->ovnsb_txn,
216                                       "deleting Datapath_Binding "UUID_FMT" that "
217                                       "lacks external-ids:logical-switch",
218                          UUID_ARGS(&sb->header_.uuid));
219             sbrec_datapath_binding_delete(sb);
220             continue;
221         }
222
223         if (ovn_datapath_find(datapaths, &key)) {
224             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
225             VLOG_INFO_RL(&rl, "deleting Datapath_Binding "UUID_FMT" with "
226                          "duplicate external-ids:logical-switch "UUID_FMT,
227                          UUID_ARGS(&sb->header_.uuid), UUID_ARGS(&key));
228             sbrec_datapath_binding_delete(sb);
229             continue;
230         }
231
232         struct ovn_datapath *od = ovn_datapath_create(datapaths, &key,
233                                                       NULL, sb);
234         list_push_back(sb_only, &od->list);
235     }
236
237     const struct nbrec_logical_switch *nb;
238     NBREC_LOGICAL_SWITCH_FOR_EACH (nb, ctx->ovnnb_idl) {
239         struct ovn_datapath *od = ovn_datapath_find(datapaths,
240                                                     &nb->header_.uuid);
241         if (od) {
242             od->nb = nb;
243             list_remove(&od->list);
244             list_push_back(both, &od->list);
245         } else {
246             od = ovn_datapath_create(datapaths, &nb->header_.uuid, nb, NULL);
247             list_push_back(nb_only, &od->list);
248         }
249     }
250 }
251
252 static uint32_t
253 ovn_datapath_allocate_key(struct hmap *dp_tnlids)
254 {
255     static uint32_t hint;
256     return allocate_tnlid(dp_tnlids, "datapath", (1u << 24) - 1, &hint);
257 }
258
259 static void
260 build_datapaths(struct northd_context *ctx, struct hmap *datapaths)
261 {
262     struct ovs_list sb_only, nb_only, both;
263
264     join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both);
265
266     if (!list_is_empty(&nb_only)) {
267         /* First index the in-use datapath tunnel IDs. */
268         struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
269         struct ovn_datapath *od;
270         LIST_FOR_EACH (od, list, &both) {
271             add_tnlid(&dp_tnlids, od->sb->tunnel_key);
272         }
273
274         /* Add southbound record for each unmatched northbound record. */
275         LIST_FOR_EACH (od, list, &nb_only) {
276             uint16_t tunnel_key = ovn_datapath_allocate_key(&dp_tnlids);
277             if (!tunnel_key) {
278                 break;
279             }
280
281             od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
282
283             struct smap external_ids = SMAP_INITIALIZER(&external_ids);
284             char uuid_s[UUID_LEN + 1];
285             sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->nb->header_.uuid));
286             smap_add(&external_ids, "logical-switch", uuid_s);
287             sbrec_datapath_binding_set_external_ids(od->sb, &external_ids);
288             smap_destroy(&external_ids);
289
290             sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key);
291         }
292         destroy_tnlids(&dp_tnlids);
293     }
294
295     /* Delete southbound records without northbound matches. */
296     struct ovn_datapath *od, *next;
297     LIST_FOR_EACH_SAFE (od, next, list, &sb_only) {
298         list_remove(&od->list);
299         sbrec_datapath_binding_delete(od->sb);
300         ovn_datapath_destroy(datapaths, od);
301     }
302 }
303 \f
304 struct ovn_port {
305     struct hmap_node key_node;  /* Index on 'key'. */
306     const char *key;            /* nb->name and sb->logical_port */
307
308     const struct nbrec_logical_port *nb; /* May be NULL. */
309     const struct sbrec_port_binding *sb; /* May be NULL. */
310
311     struct ovn_datapath *od;
312
313     struct ovs_list list;       /* In list of similar records. */
314 };
315
316 static struct ovn_port *
317 ovn_port_create(struct hmap *ports, const char *key,
318                 const struct nbrec_logical_port *nb,
319                 const struct sbrec_port_binding *sb)
320 {
321     struct ovn_port *op = xzalloc(sizeof *op);
322     op->key = key;
323     op->sb = sb;
324     op->nb = nb;
325     hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
326     return op;
327 }
328
329 static void
330 ovn_port_destroy(struct hmap *ports, struct ovn_port *port)
331 {
332     if (port) {
333         /* Don't remove port->list.  It is used within build_ports() as a
334          * private list and once we've exited that function it is not safe to
335          * use it. */
336         hmap_remove(ports, &port->key_node);
337         free(port);
338     }
339 }
340
341 static struct ovn_port *
342 ovn_port_find(struct hmap *ports, const char *name)
343 {
344     struct ovn_port *op;
345
346     HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports) {
347         if (!strcmp(op->key, name)) {
348             return op;
349         }
350     }
351     return NULL;
352 }
353
354 static uint32_t
355 ovn_port_allocate_key(struct ovn_datapath *od)
356 {
357     return allocate_tnlid(&od->port_tnlids, "port",
358                           (1u << 15) - 1, &od->port_key_hint);
359 }
360
361 static void
362 join_logical_ports(struct northd_context *ctx,
363                    struct hmap *datapaths, struct hmap *ports,
364                    struct ovs_list *sb_only, struct ovs_list *nb_only,
365                    struct ovs_list *both)
366 {
367     hmap_init(ports);
368     list_init(sb_only);
369     list_init(nb_only);
370     list_init(both);
371
372     const struct sbrec_port_binding *sb;
373     SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
374         struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
375                                               NULL, sb);
376         list_push_back(sb_only, &op->list);
377     }
378
379     struct ovn_datapath *od;
380     HMAP_FOR_EACH (od, key_node, datapaths) {
381         for (size_t i = 0; i < od->nb->n_ports; i++) {
382             const struct nbrec_logical_port *nb = od->nb->ports[i];
383             struct ovn_port *op = ovn_port_find(ports, nb->name);
384             if (op) {
385                 op->nb = nb;
386                 list_remove(&op->list);
387                 list_push_back(both, &op->list);
388             } else {
389                 op = ovn_port_create(ports, nb->name, nb, NULL);
390                 list_push_back(nb_only, &op->list);
391             }
392             op->od = od;
393         }
394     }
395 }
396
397 static void
398 ovn_port_update_sbrec(const struct ovn_port *op)
399 {
400     sbrec_port_binding_set_type(op->sb, op->nb->type);
401     sbrec_port_binding_set_options(op->sb, &op->nb->options);
402     sbrec_port_binding_set_datapath(op->sb, op->od->sb);
403     sbrec_port_binding_set_parent_port(op->sb, op->nb->parent_name);
404     sbrec_port_binding_set_tag(op->sb, op->nb->tag, op->nb->n_tag);
405     sbrec_port_binding_set_mac(op->sb, (const char **) op->nb->macs,
406                                op->nb->n_macs);
407 }
408
409 static void
410 build_ports(struct northd_context *ctx, struct hmap *datapaths,
411             struct hmap *ports)
412 {
413     struct ovs_list sb_only, nb_only, both;
414
415     join_logical_ports(ctx, datapaths, ports, &sb_only, &nb_only, &both);
416
417     /* For logical ports that are in both databases, update the southbound
418      * record based on northbound data.  Also index the in-use tunnel_keys. */
419     struct ovn_port *op, *next;
420     LIST_FOR_EACH_SAFE (op, next, list, &both) {
421         ovn_port_update_sbrec(op);
422
423         add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
424         if (op->sb->tunnel_key > op->od->port_key_hint) {
425             op->od->port_key_hint = op->sb->tunnel_key;
426         }
427     }
428
429     /* Add southbound record for each unmatched northbound record. */
430     LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
431         uint16_t tunnel_key = ovn_port_allocate_key(op->od);
432         if (!tunnel_key) {
433             continue;
434         }
435
436         op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
437         ovn_port_update_sbrec(op);
438
439         sbrec_port_binding_set_logical_port(op->sb, op->key);
440         sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);
441     }
442
443     /* Delete southbound records without northbound matches. */
444     LIST_FOR_EACH_SAFE(op, next, list, &sb_only) {
445         list_remove(&op->list);
446         sbrec_port_binding_delete(op->sb);
447         ovn_port_destroy(ports, op);
448     }
449 }
450 \f
451 #define OVN_MIN_MULTICAST 32768
452 #define OVN_MAX_MULTICAST 65535
453
454 struct multicast_group {
455     const char *name;
456     uint16_t key;               /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */
457 };
458
459 #define MC_FLOOD "_MC_flood"
460 static const struct multicast_group mc_flood = { MC_FLOOD, 65535 };
461
462 #define MC_UNKNOWN "_MC_unknown"
463 static const struct multicast_group mc_unknown = { MC_UNKNOWN, 65534 };
464
465 static bool
466 multicast_group_equal(const struct multicast_group *a,
467                       const struct multicast_group *b)
468 {
469     return !strcmp(a->name, b->name) && a->key == b->key;
470 }
471
472 /* Multicast group entry. */
473 struct ovn_multicast {
474     struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */
475     struct ovn_datapath *datapath;
476     const struct multicast_group *group;
477
478     struct ovn_port **ports;
479     size_t n_ports, allocated_ports;
480 };
481
482 static uint32_t
483 ovn_multicast_hash(const struct ovn_datapath *datapath,
484                    const struct multicast_group *group)
485 {
486     return hash_pointer(datapath, group->key);
487 }
488
489 static struct ovn_multicast *
490 ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath,
491                    const struct multicast_group *group)
492 {
493     struct ovn_multicast *mc;
494
495     HMAP_FOR_EACH_WITH_HASH (mc, hmap_node,
496                              ovn_multicast_hash(datapath, group), mcgroups) {
497         if (mc->datapath == datapath
498             && multicast_group_equal(mc->group, group)) {
499             return mc;
500         }
501     }
502     return NULL;
503 }
504
505 static void
506 ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group,
507                   struct ovn_port *port)
508 {
509     struct ovn_datapath *od = port->od;
510     struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group);
511     if (!mc) {
512         mc = xmalloc(sizeof *mc);
513         hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group));
514         mc->datapath = od;
515         mc->group = group;
516         mc->n_ports = 0;
517         mc->allocated_ports = 4;
518         mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports);
519     }
520     if (mc->n_ports >= mc->allocated_ports) {
521         mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports,
522                                sizeof *mc->ports);
523     }
524     mc->ports[mc->n_ports++] = port;
525 }
526
527 static void
528 ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc)
529 {
530     if (mc) {
531         hmap_remove(mcgroups, &mc->hmap_node);
532         free(mc->ports);
533         free(mc);
534     }
535 }
536
537 static void
538 ovn_multicast_update_sbrec(const struct ovn_multicast *mc,
539                            const struct sbrec_multicast_group *sb)
540 {
541     struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports);
542     for (size_t i = 0; i < mc->n_ports; i++) {
543         ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb);
544     }
545     sbrec_multicast_group_set_ports(sb, ports, mc->n_ports);
546     free(ports);
547 }
548 \f
549 /* Logical flow generation.
550  *
551  * This code generates the Logical_Flow table in the southbound database, as a
552  * function of most of the northbound database.
553  */
554
555 struct ovn_lflow {
556     struct hmap_node hmap_node;
557
558     struct ovn_datapath *od;
559     enum ovn_pipeline { P_IN, P_OUT } pipeline;
560     uint8_t table_id;
561     uint16_t priority;
562     char *match;
563     char *actions;
564 };
565
566 static size_t
567 ovn_lflow_hash(const struct ovn_lflow *lflow)
568 {
569     size_t hash = uuid_hash(&lflow->od->key);
570     hash = hash_2words((lflow->table_id << 16) | lflow->priority, hash);
571     hash = hash_string(lflow->match, hash);
572     return hash_string(lflow->actions, hash);
573 }
574
575 static bool
576 ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
577 {
578     return (a->od == b->od
579             && a->pipeline == b->pipeline
580             && a->table_id == b->table_id
581             && a->priority == b->priority
582             && !strcmp(a->match, b->match)
583             && !strcmp(a->actions, b->actions));
584 }
585
586 static void
587 ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
588               enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
589               char *match, char *actions)
590 {
591     lflow->od = od;
592     lflow->pipeline = pipeline;
593     lflow->table_id = table_id;
594     lflow->priority = priority;
595     lflow->match = match;
596     lflow->actions = actions;
597 }
598
599 /* Adds a row with the specified contents to the Logical_Flow table. */
600 static void
601 ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od,
602               enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
603               const char *match, const char *actions)
604 {
605     struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
606     ovn_lflow_init(lflow, od, pipeline, table_id, priority,
607                    xstrdup(match), xstrdup(actions));
608     hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
609 }
610
611 static struct ovn_lflow *
612 ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
613                enum ovn_pipeline pipeline, uint8_t table_id, uint16_t priority,
614                const char *match, const char *actions)
615 {
616     struct ovn_lflow target;
617     ovn_lflow_init(&target, od, pipeline, table_id, priority,
618                    CONST_CAST(char *, match), CONST_CAST(char *, actions));
619
620     struct ovn_lflow *lflow;
621     HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target),
622                              lflows) {
623         if (ovn_lflow_equal(lflow, &target)) {
624             return lflow;
625         }
626     }
627     return NULL;
628 }
629
630 static void
631 ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
632 {
633     if (lflow) {
634         hmap_remove(lflows, &lflow->hmap_node);
635         free(lflow->match);
636         free(lflow->actions);
637         free(lflow);
638     }
639 }
640
641 /* Appends port security constraints on L2 address field 'eth_addr_field'
642  * (e.g. "eth.src" or "eth.dst") to 'match'.  'port_security', with
643  * 'n_port_security' elements, is the collection of port_security constraints
644  * from an OVN_NB Logical_Port row. */
645 static void
646 build_port_security(const char *eth_addr_field,
647                     char **port_security, size_t n_port_security,
648                     struct ds *match)
649 {
650     size_t base_len = match->length;
651     ds_put_format(match, " && %s == {", eth_addr_field);
652
653     size_t n = 0;
654     for (size_t i = 0; i < n_port_security; i++) {
655         uint8_t ea[ETH_ADDR_LEN];
656
657         if (eth_addr_from_string(port_security[i], ea)) {
658             ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(ea));
659             ds_put_char(match, ' ');
660             n++;
661         }
662     }
663     ds_chomp(match, ' ');
664     ds_put_cstr(match, "}");
665
666     if (!n) {
667         match->length = base_len;
668     }
669 }
670
671 static bool
672 lport_is_enabled(const struct nbrec_logical_port *lport)
673 {
674     return !lport->enabled || *lport->enabled;
675 }
676
677 /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
678  * constructing their contents based on the OVN_NB database. */
679 static void
680 build_lflows(struct northd_context *ctx, struct hmap *datapaths,
681              struct hmap *ports)
682 {
683     struct hmap lflows = HMAP_INITIALIZER(&lflows);
684     struct hmap mcgroups = HMAP_INITIALIZER(&mcgroups);
685
686     /* Ingress table 0: Admission control framework (priorities 0 and 100). */
687     struct ovn_datapath *od;
688     HMAP_FOR_EACH (od, key_node, datapaths) {
689         /* Logical VLANs not supported. */
690         ovn_lflow_add(&lflows, od, P_IN, 0, 100, "vlan.present", "drop;");
691
692         /* Broadcast/multicast source address is invalid. */
693         ovn_lflow_add(&lflows, od, P_IN, 0, 100, "eth.src[40]", "drop;");
694
695         /* Port security flows have priority 50 (see below) and will continue
696          * to the next table if packet source is acceptable. */
697
698         /* Otherwise drop the packet. */
699         ovn_lflow_add(&lflows, od, P_IN, 0, 0, "1", "drop;");
700     }
701
702     /* Ingress table 0: Ingress port security (priority 50). */
703     struct ovn_port *op;
704     HMAP_FOR_EACH (op, key_node, ports) {
705         struct ds match = DS_EMPTY_INITIALIZER;
706         ds_put_cstr(&match, "inport == ");
707         json_string_escape(op->key, &match);
708         build_port_security("eth.src",
709                             op->nb->port_security, op->nb->n_port_security,
710                             &match);
711         ovn_lflow_add(&lflows, op->od, P_IN, 0, 50, ds_cstr(&match),
712                       lport_is_enabled(op->nb) ? "next;" : "drop;");
713         ds_destroy(&match);
714     }
715
716     /* Ingress table 1: Destination lookup, broadcast and multicast handling
717      * (priority 100). */
718     HMAP_FOR_EACH (op, key_node, ports) {
719         if (lport_is_enabled(op->nb)) {
720             ovn_multicast_add(&mcgroups, &mc_flood, op);
721         }
722     }
723     HMAP_FOR_EACH (od, key_node, datapaths) {
724         ovn_lflow_add(&lflows, od, P_IN, 1, 100, "eth.dst[40]",
725                       "outport = \""MC_FLOOD"\"; output;");
726     }
727
728     /* Ingress table 1: Destination lookup, unicast handling (priority 50), */
729     HMAP_FOR_EACH (op, key_node, ports) {
730         for (size_t i = 0; i < op->nb->n_macs; i++) {
731             uint8_t mac[ETH_ADDR_LEN];
732
733             if (eth_addr_from_string(op->nb->macs[i], mac)) {
734                 struct ds match, actions;
735
736                 ds_init(&match);
737                 ds_put_format(&match, "eth.dst == %s", op->nb->macs[i]);
738
739                 ds_init(&actions);
740                 ds_put_cstr(&actions, "outport = ");
741                 json_string_escape(op->nb->name, &actions);
742                 ds_put_cstr(&actions, "; output;");
743                 ovn_lflow_add(&lflows, op->od, P_IN, 1, 50,
744                               ds_cstr(&match), ds_cstr(&actions));
745                 ds_destroy(&actions);
746                 ds_destroy(&match);
747             } else if (!strcmp(op->nb->macs[i], "unknown")) {
748                 ovn_multicast_add(&mcgroups, &mc_unknown, op);
749                 op->od->has_unknown = true;
750             } else {
751                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
752
753                 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
754                              op->nb->name, op->nb->macs[i]);
755             }
756         }
757     }
758
759     /* Ingress table 1: Destination lookup for unknown MACs (priority 0). */
760     HMAP_FOR_EACH (od, key_node, datapaths) {
761         if (od->has_unknown) {
762             ovn_lflow_add(&lflows, od, P_IN, 1, 0, "1",
763                           "outport = \""MC_UNKNOWN"\"; output;");
764         }
765     }
766
767     /* Egress table 0: ACLs (any priority). */
768     HMAP_FOR_EACH (od, key_node, datapaths) {
769         for (size_t i = 0; i < od->nb->n_acls; i++) {
770             const struct nbrec_acl *acl = od->nb->acls[i];
771             const char *action;
772
773             action = (!strcmp(acl->action, "allow") ||
774                       !strcmp(acl->action, "allow-related"))
775                 ? "next;" : "drop;";
776             ovn_lflow_add(&lflows, od, P_OUT, 0, acl->priority, acl->match,
777                           action);
778         }
779     }
780     HMAP_FOR_EACH (od, key_node, datapaths) {
781         ovn_lflow_add(&lflows, od, P_OUT, 0, 0, "1", "next;");
782     }
783
784     /* Egress table 1: Egress port security multicast/broadcast (priority
785      * 100). */
786     HMAP_FOR_EACH (od, key_node, datapaths) {
787         ovn_lflow_add(&lflows, od, P_OUT, 1, 100, "eth.dst[40]", "output;");
788     }
789
790     /* Egress table 1: Egress port security (priority 50). */
791     HMAP_FOR_EACH (op, key_node, ports) {
792         struct ds match;
793
794         ds_init(&match);
795         ds_put_cstr(&match, "outport == ");
796         json_string_escape(op->key, &match);
797         build_port_security("eth.dst",
798                             op->nb->port_security, op->nb->n_port_security,
799                             &match);
800
801         ovn_lflow_add(&lflows, op->od, P_OUT, 1, 50, ds_cstr(&match),
802                       lport_is_enabled(op->nb) ? "output;" : "drop;");
803
804         ds_destroy(&match);
805     }
806
807     /* Push changes to the Logical_Flow table to database. */
808     const struct sbrec_logical_flow *sbflow, *next_sbflow;
809     SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) {
810         struct ovn_datapath *od
811             = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath);
812         if (!od) {
813             sbrec_logical_flow_delete(sbflow);
814             continue;
815         }
816
817         struct ovn_lflow *lflow = ovn_lflow_find(
818             &lflows, od, (!strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT),
819             sbflow->table_id, sbflow->priority,
820             sbflow->match, sbflow->actions);
821         if (lflow) {
822             ovn_lflow_destroy(&lflows, lflow);
823         } else {
824             sbrec_logical_flow_delete(sbflow);
825         }
826     }
827     struct ovn_lflow *lflow, *next_lflow;
828     HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
829         sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
830         sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
831         sbrec_logical_flow_set_pipeline(
832             sbflow, lflow->pipeline == P_IN ? "ingress" : "egress");
833         sbrec_logical_flow_set_table_id(sbflow, lflow->table_id);
834         sbrec_logical_flow_set_priority(sbflow, lflow->priority);
835         sbrec_logical_flow_set_match(sbflow, lflow->match);
836         sbrec_logical_flow_set_actions(sbflow, lflow->actions);
837         ovn_lflow_destroy(&lflows, lflow);
838     }
839     hmap_destroy(&lflows);
840
841     /* Push changes to the Multicast_Group table to database. */
842     const struct sbrec_multicast_group *sbmc, *next_sbmc;
843     SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
844         struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
845                                                           sbmc->datapath);
846         if (!od) {
847             sbrec_multicast_group_delete(sbmc);
848             continue;
849         }
850
851         struct multicast_group group = { .name = sbmc->name,
852                                          .key = sbmc->tunnel_key };
853         struct ovn_multicast *mc = ovn_multicast_find(&mcgroups, od, &group);
854         if (mc) {
855             ovn_multicast_update_sbrec(mc, sbmc);
856             ovn_multicast_destroy(&mcgroups, mc);
857         } else {
858             sbrec_multicast_group_delete(sbmc);
859         }
860     }
861     struct ovn_multicast *mc, *next_mc;
862     HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, &mcgroups) {
863         sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn);
864         sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb);
865         sbrec_multicast_group_set_name(sbmc, mc->group->name);
866         sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key);
867         ovn_multicast_update_sbrec(mc, sbmc);
868         ovn_multicast_destroy(&mcgroups, mc);
869     }
870     hmap_destroy(&mcgroups);
871 }
872 \f
873 static void
874 ovnnb_db_changed(struct northd_context *ctx)
875 {
876     VLOG_DBG("ovn-nb db contents have changed.");
877
878     struct hmap datapaths, ports;
879     build_datapaths(ctx, &datapaths);
880     build_ports(ctx, &datapaths, &ports);
881     build_lflows(ctx, &datapaths, &ports);
882
883     struct ovn_datapath *dp, *next_dp;
884     HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, &datapaths) {
885         ovn_datapath_destroy(&datapaths, dp);
886     }
887     hmap_destroy(&datapaths);
888
889     struct ovn_port *port, *next_port;
890     HMAP_FOR_EACH_SAFE (port, next_port, key_node, &ports) {
891         ovn_port_destroy(&ports, port);
892     }
893     hmap_destroy(&ports);
894 }
895
896 /*
897  * The only change we get notified about is if the 'chassis' column of the
898  * 'Port_Binding' table changes.  When this column is not empty, it means we
899  * need to set the corresponding logical port as 'up' in the northbound DB.
900  */
901 static void
902 ovnsb_db_changed(struct northd_context *ctx)
903 {
904     struct hmap lports_hmap;
905     const struct sbrec_port_binding *sb;
906     const struct nbrec_logical_port *nb;
907
908     struct lport_hash_node {
909         struct hmap_node node;
910         const struct nbrec_logical_port *nb;
911     } *hash_node, *hash_node_next;
912
913     VLOG_DBG("Recalculating port up states for ovn-nb db.");
914
915     hmap_init(&lports_hmap);
916
917     NBREC_LOGICAL_PORT_FOR_EACH(nb, ctx->ovnnb_idl) {
918         hash_node = xzalloc(sizeof *hash_node);
919         hash_node->nb = nb;
920         hmap_insert(&lports_hmap, &hash_node->node, hash_string(nb->name, 0));
921     }
922
923     SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
924         nb = NULL;
925         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
926                                 hash_string(sb->logical_port, 0),
927                                 &lports_hmap) {
928             if (!strcmp(sb->logical_port, hash_node->nb->name)) {
929                 nb = hash_node->nb;
930                 break;
931             }
932         }
933
934         if (!nb) {
935             /* The logical port doesn't exist for this port binding.  This can
936              * happen under normal circumstances when ovn-northd hasn't gotten
937              * around to pruning the Port_Binding yet. */
938             continue;
939         }
940
941         if (sb->chassis && (!nb->up || !*nb->up)) {
942             bool up = true;
943             nbrec_logical_port_set_up(nb, &up, 1);
944         } else if (!sb->chassis && (!nb->up || *nb->up)) {
945             bool up = false;
946             nbrec_logical_port_set_up(nb, &up, 1);
947         }
948     }
949
950     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
951         hmap_remove(&lports_hmap, &hash_node->node);
952         free(hash_node);
953     }
954     hmap_destroy(&lports_hmap);
955 }
956 \f
957 static const char *
958 default_db(void)
959 {
960     static char *def;
961     if (!def) {
962         def = xasprintf("unix:%s/db.sock", ovs_rundir());
963     }
964     return def;
965 }
966
967 static void
968 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
969 {
970     enum {
971         DAEMON_OPTION_ENUMS,
972         VLOG_OPTION_ENUMS,
973     };
974     static const struct option long_options[] = {
975         {"ovnsb-db", required_argument, NULL, 'd'},
976         {"ovnnb-db", required_argument, NULL, 'D'},
977         {"help", no_argument, NULL, 'h'},
978         {"options", no_argument, NULL, 'o'},
979         {"version", no_argument, NULL, 'V'},
980         DAEMON_LONG_OPTIONS,
981         VLOG_LONG_OPTIONS,
982         STREAM_SSL_LONG_OPTIONS,
983         {NULL, 0, NULL, 0},
984     };
985     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
986
987     for (;;) {
988         int c;
989
990         c = getopt_long(argc, argv, short_options, long_options, NULL);
991         if (c == -1) {
992             break;
993         }
994
995         switch (c) {
996         DAEMON_OPTION_HANDLERS;
997         VLOG_OPTION_HANDLERS;
998         STREAM_SSL_OPTION_HANDLERS;
999
1000         case 'd':
1001             ovnsb_db = optarg;
1002             break;
1003
1004         case 'D':
1005             ovnnb_db = optarg;
1006             break;
1007
1008         case 'h':
1009             usage();
1010             exit(EXIT_SUCCESS);
1011
1012         case 'o':
1013             ovs_cmdl_print_options(long_options);
1014             exit(EXIT_SUCCESS);
1015
1016         case 'V':
1017             ovs_print_version(0, 0);
1018             exit(EXIT_SUCCESS);
1019
1020         default:
1021             break;
1022         }
1023     }
1024
1025     if (!ovnsb_db) {
1026         ovnsb_db = default_db();
1027     }
1028
1029     if (!ovnnb_db) {
1030         ovnnb_db = default_db();
1031     }
1032
1033     free(short_options);
1034 }
1035
1036 static void
1037 add_column_noalert(struct ovsdb_idl *idl,
1038                    const struct ovsdb_idl_column *column)
1039 {
1040     ovsdb_idl_add_column(idl, column);
1041     ovsdb_idl_omit_alert(idl, column);
1042 }
1043
1044 int
1045 main(int argc, char *argv[])
1046 {
1047     extern struct vlog_module VLM_reconnect;
1048     struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
1049     unsigned int ovnnb_seqno, ovn_seqno;
1050     int res = EXIT_SUCCESS;
1051     struct northd_context ctx = {
1052         .ovnsb_txn = NULL,
1053     };
1054     bool ovnnb_changes_pending = false;
1055     bool ovn_changes_pending = false;
1056     struct unixctl_server *unixctl;
1057     int retval;
1058     bool exiting;
1059
1060     fatal_ignore_sigpipe();
1061     set_program_name(argv[0]);
1062     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
1063     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
1064     parse_options(argc, argv);
1065
1066     daemonize_start();
1067
1068     retval = unixctl_server_create(NULL, &unixctl);
1069     if (retval) {
1070         exit(EXIT_FAILURE);
1071     }
1072     unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
1073
1074     daemonize_complete();
1075
1076     nbrec_init();
1077     sbrec_init();
1078
1079     /* We want to detect all changes to the ovn-nb db. */
1080     ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
1081             &nbrec_idl_class, true, true);
1082
1083     ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
1084             &sbrec_idl_class, false, true);
1085
1086     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_logical_flow);
1087     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_logical_datapath);
1088     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_pipeline);
1089     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_table_id);
1090     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_priority);
1091     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_match);
1092     add_column_noalert(ovnsb_idl, &sbrec_logical_flow_col_actions);
1093
1094     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_multicast_group);
1095     add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_datapath);
1096     add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_tunnel_key);
1097     add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_name);
1098     add_column_noalert(ovnsb_idl, &sbrec_multicast_group_col_ports);
1099
1100     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_datapath_binding);
1101     add_column_noalert(ovnsb_idl, &sbrec_datapath_binding_col_tunnel_key);
1102     add_column_noalert(ovnsb_idl, &sbrec_datapath_binding_col_external_ids);
1103
1104     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_port_binding);
1105     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_datapath);
1106     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_logical_port);
1107     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_tunnel_key);
1108     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_parent_port);
1109     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_tag);
1110     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_type);
1111     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_options);
1112     add_column_noalert(ovnsb_idl, &sbrec_port_binding_col_mac);
1113     ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_chassis);
1114
1115     /*
1116      * The loop here just runs the IDL in a loop waiting for the seqno to
1117      * change, which indicates that the contents of the db have changed.
1118      *
1119      * If the contents of the ovn-nb db change, the mappings to the ovn-sb
1120      * db must be recalculated.
1121      *
1122      * If the contents of the ovn-sb db change, it means the 'up' state of
1123      * a port may have changed, as that's the only type of change ovn-northd is
1124      * watching for.
1125      */
1126
1127     ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
1128     ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
1129     exiting = false;
1130     while (!exiting) {
1131         ovsdb_idl_run(ovnnb_idl);
1132         ovsdb_idl_run(ovnsb_idl);
1133         unixctl_server_run(unixctl);
1134
1135         if (!ovsdb_idl_is_alive(ovnnb_idl)) {
1136             int retval = ovsdb_idl_get_last_error(ovnnb_idl);
1137             VLOG_ERR("%s: database connection failed (%s)",
1138                     ovnnb_db, ovs_retval_to_string(retval));
1139             res = EXIT_FAILURE;
1140             break;
1141         }
1142
1143         if (!ovsdb_idl_is_alive(ovnsb_idl)) {
1144             int retval = ovsdb_idl_get_last_error(ovnsb_idl);
1145             VLOG_ERR("%s: database connection failed (%s)",
1146                     ovnsb_db, ovs_retval_to_string(retval));
1147             res = EXIT_FAILURE;
1148             break;
1149         }
1150
1151         if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
1152             ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
1153             ovnnb_changes_pending = true;
1154         }
1155
1156         if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
1157             ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
1158             ovn_changes_pending = true;
1159         }
1160
1161         /*
1162          * If there are any pending changes, we delay recalculating the
1163          * necessary updates until after an existing transaction finishes.
1164          * This avoids the possibility of rapid updates causing ovn-northd to
1165          * never be able to successfully make the corresponding updates to the
1166          * other db.  Instead, pending changes are batched up until the next
1167          * time we get a chance to calculate the new state and apply it.
1168          */
1169
1170         if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
1171             /*
1172              * The OVN-nb db contents have changed, so create a transaction for
1173              * updating the OVN-sb DB.
1174              */
1175             ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
1176             ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
1177                                       "ovn-northd: northbound db changed");
1178             ovnnb_db_changed(&ctx);
1179             ovnnb_changes_pending = false;
1180         }
1181
1182         if (ovn_changes_pending && !ctx.ovnnb_txn) {
1183             /*
1184              * The OVN-sb db contents have changed, so create a transaction for
1185              * updating the northbound DB.
1186              */
1187             ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
1188             ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
1189                                       "ovn-northd: southbound db changed");
1190             ovnsb_db_changed(&ctx);
1191             ovn_changes_pending = false;
1192         }
1193
1194         if (ctx.ovnnb_txn) {
1195             enum ovsdb_idl_txn_status txn_status;
1196             txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
1197             switch (txn_status) {
1198             case TXN_UNCOMMITTED:
1199             case TXN_INCOMPLETE:
1200                 /* Come back around and try to commit this transaction again */
1201                 break;
1202             case TXN_ABORTED:
1203             case TXN_TRY_AGAIN:
1204             case TXN_NOT_LOCKED:
1205             case TXN_ERROR:
1206                 /* Something went wrong, so try creating a new transaction. */
1207                 ovn_changes_pending = true;
1208             case TXN_UNCHANGED:
1209             case TXN_SUCCESS:
1210                 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
1211                 ctx.ovnnb_txn = NULL;
1212             }
1213         }
1214
1215         if (ctx.ovnsb_txn) {
1216             enum ovsdb_idl_txn_status txn_status;
1217             txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
1218             switch (txn_status) {
1219             case TXN_UNCOMMITTED:
1220             case TXN_INCOMPLETE:
1221                 /* Come back around and try to commit this transaction again */
1222                 break;
1223             case TXN_ABORTED:
1224             case TXN_TRY_AGAIN:
1225             case TXN_NOT_LOCKED:
1226             case TXN_ERROR:
1227                 /* Something went wrong, so try creating a new transaction. */
1228                 ovnnb_changes_pending = true;
1229             case TXN_UNCHANGED:
1230             case TXN_SUCCESS:
1231                 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
1232                 ctx.ovnsb_txn = NULL;
1233             }
1234         }
1235
1236         if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
1237                 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
1238             ovsdb_idl_wait(ovnnb_idl);
1239             ovsdb_idl_wait(ovnsb_idl);
1240             if (ctx.ovnnb_txn) {
1241                 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
1242             }
1243             if (ctx.ovnsb_txn) {
1244                 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
1245             }
1246             unixctl_server_wait(unixctl);
1247             if (exiting) {
1248                 poll_immediate_wake();
1249             }
1250             poll_block();
1251         }
1252     }
1253
1254     unixctl_server_destroy(unixctl);
1255     ovsdb_idl_destroy(ovnsb_idl);
1256     ovsdb_idl_destroy(ovnnb_idl);
1257
1258     exit(res);
1259 }
1260
1261 static void
1262 ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
1263                 const char *argv[] OVS_UNUSED, void *exiting_)
1264 {
1265     bool *exiting = exiting_;
1266     *exiting = true;
1267
1268     unixctl_command_reply(conn, NULL);
1269 }