1 /* Copyright (c) 2015, 2016 Nicira, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
20 #include "openvswitch/dynamic-string.h"
21 #include "openvswitch/ofp-actions.h"
22 #include "openvswitch/ofpbuf.h"
23 #include "openvswitch/vlog.h"
24 #include "ovn-controller.h"
25 #include "ovn/lib/actions.h"
26 #include "ovn/lib/expr.h"
27 #include "ovn/lib/ovn-dhcp.h"
28 #include "ovn/lib/ovn-sb-idl.h"
33 VLOG_DEFINE_THIS_MODULE(lflow);
37 /* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
38 static struct shash symtab;
40 /* Contains an internal expr datastructure that represents an address set. */
41 static struct shash expr_address_sets;
44 add_logical_register(struct shash *symtab, enum mf_field_id id)
48 snprintf(name, sizeof name, "reg%d", id - MFF_REG0);
49 expr_symtab_add_field(symtab, name, id, NULL, false);
56 shash_init(&expr_address_sets);
58 /* Reserve a pair of registers for the logical inport and outport. A full
59 * 32-bit register each is bigger than we need, but the expression code
60 * doesn't yet support string fields that occupy less than a full OXM. */
61 expr_symtab_add_string(&symtab, "inport", MFF_LOG_INPORT, NULL);
62 expr_symtab_add_string(&symtab, "outport", MFF_LOG_OUTPORT, NULL);
64 /* Logical registers. */
65 #define MFF_LOG_REG(ID) add_logical_register(&symtab, ID);
69 expr_symtab_add_field(&symtab, "xxreg0", MFF_XXREG0, NULL, false);
70 expr_symtab_add_field(&symtab, "xxreg1", MFF_XXREG1, NULL, false);
72 /* Connection tracking state. */
73 expr_symtab_add_field(&symtab, "ct_mark", MFF_CT_MARK, NULL, false);
74 expr_symtab_add_field(&symtab, "ct_label", MFF_CT_LABEL, NULL, false);
75 expr_symtab_add_field(&symtab, "ct_state", MFF_CT_STATE, NULL, false);
76 char ct_state_str[16];
77 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_TRACKED_BIT);
78 expr_symtab_add_predicate(&symtab, "ct.trk", ct_state_str);
79 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_NEW_BIT);
80 expr_symtab_add_subfield(&symtab, "ct.new", "ct.trk", ct_state_str);
81 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_ESTABLISHED_BIT);
82 expr_symtab_add_subfield(&symtab, "ct.est", "ct.trk", ct_state_str);
83 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_RELATED_BIT);
84 expr_symtab_add_subfield(&symtab, "ct.rel", "ct.trk", ct_state_str);
85 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_REPLY_DIR_BIT);
86 expr_symtab_add_subfield(&symtab, "ct.rpl", "ct.trk", ct_state_str);
87 snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_INVALID_BIT);
88 expr_symtab_add_subfield(&symtab, "ct.inv", "ct.trk", ct_state_str);
91 expr_symtab_add_field(&symtab, "eth.src", MFF_ETH_SRC, NULL, false);
92 expr_symtab_add_field(&symtab, "eth.dst", MFF_ETH_DST, NULL, false);
93 expr_symtab_add_field(&symtab, "eth.type", MFF_ETH_TYPE, NULL, true);
94 expr_symtab_add_predicate(&symtab, "eth.bcast",
95 "eth.dst == ff:ff:ff:ff:ff:ff");
96 expr_symtab_add_subfield(&symtab, "eth.mcast", NULL, "eth.dst[40]");
98 expr_symtab_add_field(&symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false);
99 expr_symtab_add_predicate(&symtab, "vlan.present", "vlan.tci[12]");
100 expr_symtab_add_subfield(&symtab, "vlan.pcp", "vlan.present",
102 expr_symtab_add_subfield(&symtab, "vlan.vid", "vlan.present",
105 expr_symtab_add_predicate(&symtab, "ip4", "eth.type == 0x800");
106 expr_symtab_add_predicate(&symtab, "ip6", "eth.type == 0x86dd");
107 expr_symtab_add_predicate(&symtab, "ip", "ip4 || ip6");
108 expr_symtab_add_field(&symtab, "ip.proto", MFF_IP_PROTO, "ip", true);
109 expr_symtab_add_field(&symtab, "ip.dscp", MFF_IP_DSCP, "ip", false);
110 expr_symtab_add_field(&symtab, "ip.ecn", MFF_IP_ECN, "ip", false);
111 expr_symtab_add_field(&symtab, "ip.ttl", MFF_IP_TTL, "ip", false);
113 expr_symtab_add_field(&symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
114 expr_symtab_add_field(&symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
115 expr_symtab_add_predicate(&symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");
117 expr_symtab_add_predicate(&symtab, "icmp4", "ip4 && ip.proto == 1");
118 expr_symtab_add_field(&symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4",
120 expr_symtab_add_field(&symtab, "icmp4.code", MFF_ICMPV4_CODE, "icmp4",
123 expr_symtab_add_field(&symtab, "ip6.src", MFF_IPV6_SRC, "ip6", false);
124 expr_symtab_add_field(&symtab, "ip6.dst", MFF_IPV6_DST, "ip6", false);
125 expr_symtab_add_field(&symtab, "ip6.label", MFF_IPV6_LABEL, "ip6", false);
127 expr_symtab_add_predicate(&symtab, "icmp6", "ip6 && ip.proto == 58");
128 expr_symtab_add_field(&symtab, "icmp6.type", MFF_ICMPV6_TYPE, "icmp6",
130 expr_symtab_add_field(&symtab, "icmp6.code", MFF_ICMPV6_CODE, "icmp6",
133 expr_symtab_add_predicate(&symtab, "icmp", "icmp4 || icmp6");
135 expr_symtab_add_field(&symtab, "ip.frag", MFF_IP_FRAG, "ip", false);
136 expr_symtab_add_predicate(&symtab, "ip.is_frag", "ip.frag[0]");
137 expr_symtab_add_predicate(&symtab, "ip.later_frag", "ip.frag[1]");
138 expr_symtab_add_predicate(&symtab, "ip.first_frag",
139 "ip.is_frag && !ip.later_frag");
141 expr_symtab_add_predicate(&symtab, "arp", "eth.type == 0x806");
142 expr_symtab_add_field(&symtab, "arp.op", MFF_ARP_OP, "arp", false);
143 expr_symtab_add_field(&symtab, "arp.spa", MFF_ARP_SPA, "arp", false);
144 expr_symtab_add_field(&symtab, "arp.sha", MFF_ARP_SHA, "arp", false);
145 expr_symtab_add_field(&symtab, "arp.tpa", MFF_ARP_TPA, "arp", false);
146 expr_symtab_add_field(&symtab, "arp.tha", MFF_ARP_THA, "arp", false);
148 expr_symtab_add_predicate(&symtab, "nd",
149 "icmp6.type == {135, 136} && icmp6.code == 0");
150 expr_symtab_add_field(&symtab, "nd.target", MFF_ND_TARGET, "nd", false);
151 expr_symtab_add_field(&symtab, "nd.sll", MFF_ND_SLL,
152 "nd && icmp6.type == 135", false);
153 expr_symtab_add_field(&symtab, "nd.tll", MFF_ND_TLL,
154 "nd && icmp6.type == 136", false);
156 expr_symtab_add_predicate(&symtab, "tcp", "ip.proto == 6");
157 expr_symtab_add_field(&symtab, "tcp.src", MFF_TCP_SRC, "tcp", false);
158 expr_symtab_add_field(&symtab, "tcp.dst", MFF_TCP_DST, "tcp", false);
159 expr_symtab_add_field(&symtab, "tcp.flags", MFF_TCP_FLAGS, "tcp", false);
161 expr_symtab_add_predicate(&symtab, "udp", "ip.proto == 17");
162 expr_symtab_add_field(&symtab, "udp.src", MFF_UDP_SRC, "udp", false);
163 expr_symtab_add_field(&symtab, "udp.dst", MFF_UDP_DST, "udp", false);
165 expr_symtab_add_predicate(&symtab, "sctp", "ip.proto == 132");
166 expr_symtab_add_field(&symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
167 expr_symtab_add_field(&symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
170 /* Details of an address set currently in address_sets. We keep a cached
171 * copy of sets still in their string form here to make it easier to compare
172 * with the current values in the OVN_Southbound database. */
178 /* struct address_set instances for address sets currently in the symtab,
179 * hashed on the address set name. */
180 static struct shash local_address_sets = SHASH_INITIALIZER(&local_address_sets);
183 addr_cmp(const void *p1, const void *p2)
187 return strcmp(s1, s2);
190 /* Return true if the address sets match, false otherwise. */
192 address_sets_match(const struct address_set *addr_set,
193 const struct sbrec_address_set *addr_set_rec)
198 if (addr_set->n_addresses != addr_set_rec->n_addresses) {
201 size_t n_addresses = addr_set->n_addresses;
203 addrs1 = xmemdup(addr_set->addresses,
204 n_addresses * sizeof addr_set->addresses[0]);
205 addrs2 = xmemdup(addr_set_rec->addresses,
206 n_addresses * sizeof addr_set_rec->addresses[0]);
208 qsort(addrs1, n_addresses, sizeof *addrs1, addr_cmp);
209 qsort(addrs2, n_addresses, sizeof *addrs2, addr_cmp);
213 for (i = 0; i < n_addresses; i++) {
214 if (strcmp(addrs1[i], addrs2[i])) {
227 address_set_destroy(struct address_set *addr_set)
230 for (i = 0; i < addr_set->n_addresses; i++) {
231 free(addr_set->addresses[i]);
233 if (addr_set->n_addresses) {
234 free(addr_set->addresses);
240 update_address_sets(struct controller_ctx *ctx)
242 /* Remember the names of all address sets currently in expr_address_sets
243 * so we can detect address sets that have been deleted. */
244 struct sset cur_addr_set_names = SSET_INITIALIZER(&cur_addr_set_names);
246 struct shash_node *node;
247 SHASH_FOR_EACH (node, &local_address_sets) {
248 sset_add(&cur_addr_set_names, node->name);
251 /* Iterate address sets in the southbound database. Create and update the
252 * corresponding symtab entries as necessary. */
253 const struct sbrec_address_set *addr_set_rec;
254 SBREC_ADDRESS_SET_FOR_EACH (addr_set_rec, ctx->ovnsb_idl) {
255 struct address_set *addr_set =
256 shash_find_data(&local_address_sets, addr_set_rec->name);
258 bool create_set = false;
260 /* This address set has already been added. We must determine
261 * if the symtab entry needs to be updated due to a change. */
262 sset_find_and_delete(&cur_addr_set_names, addr_set_rec->name);
263 if (!address_sets_match(addr_set, addr_set_rec)) {
264 shash_find_and_delete(&local_address_sets, addr_set_rec->name);
265 expr_macros_remove(&expr_address_sets, addr_set_rec->name);
266 address_set_destroy(addr_set);
271 /* This address set is not yet in the symtab, so add it. */
276 /* The address set is either new or has changed. Create a symbol
277 * that resolves to the full set of addresses. Store it in
278 * address_sets to remember that we created this symbol. */
279 addr_set = xzalloc(sizeof *addr_set);
280 addr_set->n_addresses = addr_set_rec->n_addresses;
281 if (addr_set_rec->n_addresses) {
282 addr_set->addresses = xmalloc(addr_set_rec->n_addresses
283 * sizeof addr_set->addresses[0]);
285 for (i = 0; i < addr_set_rec->n_addresses; i++) {
286 addr_set->addresses[i] = xstrdup(addr_set_rec->addresses[i]);
289 shash_add(&local_address_sets, addr_set_rec->name, addr_set);
291 expr_macros_add(&expr_address_sets, addr_set_rec->name,
292 (const char * const *) addr_set->addresses,
293 addr_set->n_addresses);
297 /* Anything remaining in cur_addr_set_names refers to an address set that
298 * has been deleted from the southbound database. We should delete
299 * the corresponding symtab entry. */
300 const char *cur_node, *next_node;
301 SSET_FOR_EACH_SAFE (cur_node, next_node, &cur_addr_set_names) {
302 expr_macros_remove(&expr_address_sets, cur_node);
304 struct address_set *addr_set
305 = shash_find_and_delete(&local_address_sets, cur_node);
306 address_set_destroy(addr_set);
308 struct sset_node *sset_node = SSET_NODE_FROM_NAME(cur_node);
309 sset_delete(&cur_addr_set_names, sset_node);
312 sset_destroy(&cur_addr_set_names);
315 struct lookup_port_aux {
316 const struct lport_index *lports;
317 const struct mcgroup_index *mcgroups;
318 const struct sbrec_datapath_binding *dp;
321 static void consider_logical_flow(const struct lport_index *lports,
322 const struct mcgroup_index *mcgroups,
323 const struct sbrec_logical_flow *lflow,
324 const struct hmap *local_datapaths,
325 const struct hmap *patched_datapaths,
326 struct group_table *group_table,
327 const struct simap *ct_zones,
328 struct hmap *dhcp_opts_p,
329 uint32_t *conj_id_ofs_p);
332 lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
334 const struct lookup_port_aux *aux = aux_;
336 const struct sbrec_port_binding *pb
337 = lport_lookup_by_name(aux->lports, port_name);
338 if (pb && pb->datapath == aux->dp) {
339 *portp = pb->tunnel_key;
343 const struct sbrec_multicast_group *mg
344 = mcgroup_lookup_by_dp_name(aux->mcgroups, aux->dp, port_name);
346 *portp = mg->tunnel_key;
354 is_switch(const struct sbrec_datapath_binding *ldp)
356 return smap_get(&ldp->external_ids, "logical-switch") != NULL;
360 /* Adds the logical flows from the Logical_Flow table to flow tables. */
362 add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
363 const struct mcgroup_index *mcgroups,
364 const struct hmap *local_datapaths,
365 const struct hmap *patched_datapaths,
366 struct group_table *group_table,
367 const struct simap *ct_zones)
369 uint32_t conj_id_ofs = 1;
371 struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
372 const struct sbrec_dhcp_options *dhcp_opt_row;
373 SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) {
374 dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
378 const struct sbrec_logical_flow *lflow;
379 SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
380 consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
381 patched_datapaths, group_table, ct_zones,
382 &dhcp_opts, &conj_id_ofs);
385 dhcp_opts_destroy(&dhcp_opts);
389 consider_logical_flow(const struct lport_index *lports,
390 const struct mcgroup_index *mcgroups,
391 const struct sbrec_logical_flow *lflow,
392 const struct hmap *local_datapaths,
393 const struct hmap *patched_datapaths,
394 struct group_table *group_table,
395 const struct simap *ct_zones,
396 struct hmap *dhcp_opts_p,
397 uint32_t *conj_id_ofs_p)
399 /* Determine translation of logical table IDs to physical table IDs. */
400 bool ingress = !strcmp(lflow->pipeline, "ingress");
402 const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
406 if (is_switch(ldp)) {
407 /* For a logical switch datapath, local_datapaths tells us if there
408 * are any local ports for this datapath. If not, we can skip
409 * processing logical flows if that logical switch datapath is not
410 * patched to any logical router.
412 * Otherwise, we still need both ingress and egress pipeline
413 * because even if there are no local ports, we still may need to
414 * execute the ingress pipeline after a packet leaves a logical
415 * router and we need to do egress pipeline for a switch that
416 * is connected to only routers. Further optimization is possible,
417 * but not based on what we know with local_datapaths right now.
419 * A better approach would be a kind of "flood fill" algorithm:
421 * 1. Initialize set S to the logical datapaths that have a port
422 * located on the hypervisor.
424 * 2. For each patch port P in a logical datapath in S, add the
425 * logical datapath of the remote end of P to S. Iterate
426 * until S reaches a fixed point.
428 * This can be implemented in northd, which can generate the sets and
429 * save it on each port-binding record in SB, and ovn-controller can
430 * use the information directly. However, there can be update storms
431 * when a pair of patch ports are added/removed to connect/disconnect
432 * large lrouters and lswitches. This need to be studied further.
435 if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
436 if (!get_patched_datapath(patched_datapaths,
443 /* Determine translation of logical table IDs to physical table IDs. */
444 uint8_t first_ptable = (ingress
445 ? OFTABLE_LOG_INGRESS_PIPELINE
446 : OFTABLE_LOG_EGRESS_PIPELINE);
447 uint8_t ptable = first_ptable + lflow->table_id;
448 uint8_t output_ptable = (ingress
449 ? OFTABLE_REMOTE_OUTPUT
450 : OFTABLE_LOG_TO_PHY);
452 /* Translate OVN actions into OpenFlow actions.
454 * XXX Deny changes to 'outport' in egress pipeline. */
455 uint64_t ofpacts_stub[64 / 8];
456 struct ofpbuf ofpacts;
457 struct expr *prereqs;
460 ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
461 struct lookup_port_aux aux = {
463 .mcgroups = mcgroups,
464 .dp = lflow->logical_datapath
466 struct action_params ap = {
468 .dhcp_opts = dhcp_opts_p,
469 .lookup_port = lookup_port_cb,
471 .ct_zones = ct_zones,
472 .group_table = group_table,
474 .n_tables = LOG_PIPELINE_LEN,
475 .first_ptable = first_ptable,
476 .cur_ltable = lflow->table_id,
477 .output_ptable = output_ptable,
478 .arp_ptable = OFTABLE_MAC_BINDING,
480 error = actions_parse_string(lflow->actions, &ap, &ofpacts, &prereqs);
482 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
483 VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
484 lflow->actions, error);
489 /* Translate OVN match into table of OpenFlow matches. */
493 expr = expr_parse_string(lflow->match, &symtab,
494 &expr_address_sets, &error);
497 expr = expr_combine(EXPR_T_AND, expr, prereqs);
500 expr = expr_annotate(expr, &symtab, &error);
503 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
504 VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
505 lflow->match, error);
506 expr_destroy(prereqs);
507 ofpbuf_uninit(&ofpacts);
512 expr = expr_simplify(expr);
513 expr = expr_normalize(expr);
514 uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
518 /* Prepare the OpenFlow matches for adding to the flow table. */
519 struct expr_match *m;
520 HMAP_FOR_EACH (m, hmap_node, &matches) {
521 match_set_metadata(&m->match,
522 htonll(lflow->logical_datapath->tunnel_key));
523 if (m->match.wc.masks.conj_id) {
524 m->match.flow.conj_id += *conj_id_ofs_p;
527 ofctrl_add_flow(ptable, lflow->priority, &m->match, &ofpacts,
528 &lflow->header_.uuid);
530 uint64_t conj_stubs[64 / 8];
533 ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
534 for (int i = 0; i < m->n; i++) {
535 const struct cls_conjunction *src = &m->conjunctions[i];
536 struct ofpact_conjunction *dst;
538 dst = ofpact_put_CONJUNCTION(&conj);
539 dst->id = src->id + *conj_id_ofs_p;
540 dst->clause = src->clause;
541 dst->n_clauses = src->n_clauses;
543 ofctrl_add_flow(ptable, lflow->priority, &m->match, &conj,
544 &lflow->header_.uuid);
545 ofpbuf_uninit(&conj);
546 ofpbuf_uninit(&conj);
551 expr_matches_destroy(&matches);
552 ofpbuf_uninit(&ofpacts);
553 *conj_id_ofs_p += n_conjs;
557 put_load(const uint8_t *data, size_t len,
558 enum mf_field_id dst, int ofs, int n_bits,
559 struct ofpbuf *ofpacts)
561 struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
562 sf->field = mf_from_id(dst);
563 sf->flow_has_vlan = false;
565 bitwise_copy(data, len, 0, &sf->value, sf->field->n_bytes, ofs, n_bits);
566 bitwise_one(&sf->mask, sf->field->n_bytes, ofs, n_bits);
570 consider_neighbor_flow(const struct lport_index *lports,
571 const struct sbrec_mac_binding *b,
572 struct ofpbuf *ofpacts_p,
573 struct match *match_p)
575 const struct sbrec_port_binding *pb
576 = lport_lookup_by_name(lports, b->logical_port);
582 if (!eth_addr_from_string(b->mac, &mac)) {
583 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
584 VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
589 if (!ip_parse(b->ip, &ip)) {
590 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
591 VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
595 match_set_metadata(match_p, htonll(pb->datapath->tunnel_key));
596 match_set_reg(match_p, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
597 match_set_reg(match_p, 0, ntohl(ip));
599 ofpbuf_clear(ofpacts_p);
600 put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, ofpacts_p);
602 ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, match_p, ofpacts_p,
606 /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
607 * southbound database, using 'lports' to resolve logical port names to
610 add_neighbor_flows(struct controller_ctx *ctx,
611 const struct lport_index *lports)
613 struct ofpbuf ofpacts;
615 match_init_catchall(&match);
616 ofpbuf_init(&ofpacts, 0);
618 const struct sbrec_mac_binding *b;
619 SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
620 consider_neighbor_flow(lports, b, &ofpacts, &match);
622 ofpbuf_uninit(&ofpacts);
625 /* Translates logical flows in the Logical_Flow table in the OVN_SB database
626 * into OpenFlow flows. See ovn-architecture(7) for more information. */
628 lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
629 const struct mcgroup_index *mcgroups,
630 const struct hmap *local_datapaths,
631 const struct hmap *patched_datapaths,
632 struct group_table *group_table,
633 const struct simap *ct_zones)
635 update_address_sets(ctx);
636 add_logical_flows(ctx, lports, mcgroups, local_datapaths,
637 patched_datapaths, group_table, ct_zones);
638 add_neighbor_flows(ctx, lports);
644 expr_symtab_destroy(&symtab);
645 shash_destroy(&symtab);
646 expr_macros_destroy(&expr_address_sets);
647 shash_destroy(&expr_address_sets);