-/* Copyright (c) 2015 Nicira, Inc.
+/* Copyright (c) 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
#include "lflow.h"
-#include "dynamic-string.h"
+#include "lport.h"
#include "ofctrl.h"
-#include "ofp-actions.h"
-#include "ofpbuf.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
-#include "ovn/controller/ovn-controller.h"
+#include "ovn-controller.h"
#include "ovn/lib/actions.h"
#include "ovn/lib/expr.h"
+#include "ovn/lib/ovn-dhcp.h"
#include "ovn/lib/ovn-sb-idl.h"
+#include "packets.h"
#include "simap.h"
+#include "sset.h"
VLOG_DEFINE_THIS_MODULE(lflow);
\f
/* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
static struct shash symtab;
+/* Contains an internal expr datastructure that represents an address set. */
+static struct shash expr_address_sets;
+
static void
-symtab_init(void)
+add_logical_register(struct shash *symtab, enum mf_field_id id)
+{
+ char name[8];
+
+ snprintf(name, sizeof name, "reg%d", id - MFF_REG0);
+ expr_symtab_add_field(symtab, name, id, NULL, false);
+}
+
+void
+lflow_init(void)
{
shash_init(&symtab);
+ shash_init(&expr_address_sets);
/* Reserve a pair of registers for the logical inport and outport. A full
* 32-bit register each is bigger than we need, but the expression code
expr_symtab_add_string(&symtab, "inport", MFF_LOG_INPORT, NULL);
expr_symtab_add_string(&symtab, "outport", MFF_LOG_OUTPORT, NULL);
- /* Registers. We omit the registers that would otherwise overlap the
- * reserved fields. */
- for (enum mf_field_id id = MFF_REG0; id < MFF_REG0 + FLOW_N_REGS; id++) {
- if (id != MFF_LOG_INPORT && id != MFF_LOG_OUTPORT) {
- char name[8];
-
- snprintf(name, sizeof name, "reg%d", id - MFF_REG0);
- expr_symtab_add_field(&symtab, name, id, NULL, false);
- }
- }
+ /* Logical registers. */
+#define MFF_LOG_REG(ID) add_logical_register(&symtab, ID);
+ MFF_LOG_REGS;
+#undef MFF_LOG_REG
+
+ expr_symtab_add_field(&symtab, "xxreg0", MFF_XXREG0, NULL, false);
+ expr_symtab_add_field(&symtab, "xxreg1", MFF_XXREG1, NULL, false);
+
+ /* Connection tracking state. */
+ expr_symtab_add_field(&symtab, "ct_mark", MFF_CT_MARK, NULL, false);
+ expr_symtab_add_field(&symtab, "ct_label", MFF_CT_LABEL, NULL, false);
+ expr_symtab_add_field(&symtab, "ct_state", MFF_CT_STATE, NULL, false);
+ char ct_state_str[16];
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_TRACKED_BIT);
+ expr_symtab_add_predicate(&symtab, "ct.trk", ct_state_str);
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_NEW_BIT);
+ expr_symtab_add_subfield(&symtab, "ct.new", "ct.trk", ct_state_str);
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_ESTABLISHED_BIT);
+ expr_symtab_add_subfield(&symtab, "ct.est", "ct.trk", ct_state_str);
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_RELATED_BIT);
+ expr_symtab_add_subfield(&symtab, "ct.rel", "ct.trk", ct_state_str);
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_REPLY_DIR_BIT);
+ expr_symtab_add_subfield(&symtab, "ct.rpl", "ct.trk", ct_state_str);
+ snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_INVALID_BIT);
+ expr_symtab_add_subfield(&symtab, "ct.inv", "ct.trk", ct_state_str);
/* Data fields. */
expr_symtab_add_field(&symtab, "eth.src", MFF_ETH_SRC, NULL, false);
expr_symtab_add_field(&symtab, "eth.dst", MFF_ETH_DST, NULL, false);
expr_symtab_add_field(&symtab, "eth.type", MFF_ETH_TYPE, NULL, true);
+ expr_symtab_add_predicate(&symtab, "eth.bcast",
+ "eth.dst == ff:ff:ff:ff:ff:ff");
+ expr_symtab_add_subfield(&symtab, "eth.mcast", NULL, "eth.dst[40]");
expr_symtab_add_field(&symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false);
expr_symtab_add_predicate(&symtab, "vlan.present", "vlan.tci[12]");
expr_symtab_add_field(&symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
expr_symtab_add_field(&symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
+ expr_symtab_add_predicate(&symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");
expr_symtab_add_predicate(&symtab, "icmp4", "ip4 && ip.proto == 1");
expr_symtab_add_field(&symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4",
expr_symtab_add_field(&symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
expr_symtab_add_field(&symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
}
-\f
-/* Logical datapaths and logical port numbers. */
-/* A logical datapath.
- *
- * 'uuid' is the UUID that represents the logical datapath in the OVN_SB
- * database.
- *
- * 'integer' represents the logical datapath as an integer value that is unique
- * only within the local hypervisor. Because of its size, this value is more
- * practical for use in an OpenFlow flow table than a UUID.
- *
- * 'ports' maps 'logical_port' names to 'tunnel_key' values in the OVN_SB
- * Port_Binding table within the logical datapath. */
-struct logical_datapath {
- struct hmap_node hmap_node; /* Indexed on 'uuid'. */
- struct uuid uuid; /* The logical_datapath's UUID. */
- uint32_t integer; /* Locally unique among logical datapaths. */
- struct simap ports; /* Logical port name to port number. */
+/* Details of an address set currently in address_sets. We keep a cached
+ * copy of sets still in their string form here to make it easier to compare
+ * with the current values in the OVN_Southbound database. */
+struct address_set {
+ char **addresses;
+ size_t n_addresses;
};
-/* Contains "struct logical_datapath"s. */
-static struct hmap logical_datapaths = HMAP_INITIALIZER(&logical_datapaths);
+/* struct address_set instances for address sets currently in the symtab,
+ * hashed on the address set name. */
+static struct shash local_address_sets = SHASH_INITIALIZER(&local_address_sets);
-/* Finds and returns the logical_datapath with the given 'uuid', or NULL if
- * no such logical_datapath exists. */
-static struct logical_datapath *
-ldp_lookup(const struct uuid *uuid)
+static int
+addr_cmp(const void *p1, const void *p2)
{
- struct logical_datapath *ldp;
- HMAP_FOR_EACH_IN_BUCKET (ldp, hmap_node, uuid_hash(uuid),
- &logical_datapaths) {
- if (uuid_equals(&ldp->uuid, uuid)) {
- return ldp;
- }
- }
- return NULL;
+ const char *s1 = p1;
+ const char *s2 = p2;
+ return strcmp(s1, s2);
}
-/* Finds and returns the integer value corresponding to the given 'uuid', or 0
- * if no such logical datapath exists. */
-uint32_t
-ldp_to_integer(const struct uuid *logical_datapath)
+/* Return true if the address sets match, false otherwise. */
+static bool
+address_sets_match(const struct address_set *addr_set,
+ const struct sbrec_address_set *addr_set_rec)
{
- const struct logical_datapath *ldp = ldp_lookup(logical_datapath);
- return ldp ? ldp->integer : 0;
-}
+ char **addrs1;
+ char **addrs2;
-/* Creates a new logical_datapath with the given 'uuid'. */
-static struct logical_datapath *
-ldp_create(const struct uuid *uuid)
-{
- static uint32_t next_integer = 1;
- struct logical_datapath *ldp;
-
- /* We don't handle the case where the logical datapaths wrap around. */
- ovs_assert(next_integer);
-
- ldp = xmalloc(sizeof *ldp);
- hmap_insert(&logical_datapaths, &ldp->hmap_node, uuid_hash(uuid));
- ldp->uuid = *uuid;
- ldp->integer = next_integer++;
- simap_init(&ldp->ports);
- return ldp;
+ if (addr_set->n_addresses != addr_set_rec->n_addresses) {
+ return false;
+ }
+ size_t n_addresses = addr_set->n_addresses;
+
+ addrs1 = xmemdup(addr_set->addresses,
+ n_addresses * sizeof addr_set->addresses[0]);
+ addrs2 = xmemdup(addr_set_rec->addresses,
+ n_addresses * sizeof addr_set_rec->addresses[0]);
+
+ qsort(addrs1, n_addresses, sizeof *addrs1, addr_cmp);
+ qsort(addrs2, n_addresses, sizeof *addrs2, addr_cmp);
+
+ bool res = true;
+ size_t i;
+ for (i = 0; i < n_addresses; i++) {
+ if (strcmp(addrs1[i], addrs2[i])) {
+ res = false;
+ break;
+ }
+ }
+
+ free(addrs1);
+ free(addrs2);
+
+ return res;
}
static void
-ldp_free(struct logical_datapath *ldp)
+address_set_destroy(struct address_set *addr_set)
{
- simap_destroy(&ldp->ports);
- hmap_remove(&logical_datapaths, &ldp->hmap_node);
- free(ldp);
+ size_t i;
+ for (i = 0; i < addr_set->n_addresses; i++) {
+ free(addr_set->addresses[i]);
+ }
+ if (addr_set->n_addresses) {
+ free(addr_set->addresses);
+ }
+ free(addr_set);
}
-/* Iterates through all of the records in the Port_Binding table, updating the
- * table of logical_datapaths to match the values found in active Bindings. */
static void
-ldp_run(struct controller_ctx *ctx)
+update_address_sets(struct controller_ctx *ctx)
{
- struct logical_datapath *ldp;
- HMAP_FOR_EACH (ldp, hmap_node, &logical_datapaths) {
- simap_clear(&ldp->ports);
- }
+ /* Remember the names of all address sets currently in expr_address_sets
+ * so we can detect address sets that have been deleted. */
+ struct sset cur_addr_set_names = SSET_INITIALIZER(&cur_addr_set_names);
- const struct sbrec_port_binding *binding;
- SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
- struct logical_datapath *ldp;
+ struct shash_node *node;
+ SHASH_FOR_EACH (node, &local_address_sets) {
+ sset_add(&cur_addr_set_names, node->name);
+ }
- ldp = ldp_lookup(&binding->logical_datapath);
- if (!ldp) {
- ldp = ldp_create(&binding->logical_datapath);
+ /* Iterate address sets in the southbound database. Create and update the
+ * corresponding symtab entries as necessary. */
+ const struct sbrec_address_set *addr_set_rec;
+ SBREC_ADDRESS_SET_FOR_EACH (addr_set_rec, ctx->ovnsb_idl) {
+ struct address_set *addr_set =
+ shash_find_data(&local_address_sets, addr_set_rec->name);
+
+ bool create_set = false;
+ if (addr_set) {
+ /* This address set has already been added. We must determine
+ * if the symtab entry needs to be updated due to a change. */
+ sset_find_and_delete(&cur_addr_set_names, addr_set_rec->name);
+ if (!address_sets_match(addr_set, addr_set_rec)) {
+ shash_find_and_delete(&local_address_sets, addr_set_rec->name);
+ expr_macros_remove(&expr_address_sets, addr_set_rec->name);
+ address_set_destroy(addr_set);
+ addr_set = NULL;
+ create_set = true;
+ }
+ } else {
+ /* This address set is not yet in the symtab, so add it. */
+ create_set = true;
}
- simap_put(&ldp->ports, binding->logical_port, binding->tunnel_key);
- }
+ if (create_set) {
+ /* The address set is either new or has changed. Create a symbol
+ * that resolves to the full set of addresses. Store it in
+ * address_sets to remember that we created this symbol. */
+ addr_set = xzalloc(sizeof *addr_set);
+ addr_set->n_addresses = addr_set_rec->n_addresses;
+ if (addr_set_rec->n_addresses) {
+ addr_set->addresses = xmalloc(addr_set_rec->n_addresses
+ * sizeof addr_set->addresses[0]);
+ size_t i;
+ for (i = 0; i < addr_set_rec->n_addresses; i++) {
+ addr_set->addresses[i] = xstrdup(addr_set_rec->addresses[i]);
+ }
+ }
+ shash_add(&local_address_sets, addr_set_rec->name, addr_set);
- struct logical_datapath *next_ldp;
- HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) {
- if (simap_is_empty(&ldp->ports)) {
- ldp_free(ldp);
+ expr_macros_add(&expr_address_sets, addr_set_rec->name,
+ (const char * const *) addr_set->addresses,
+ addr_set->n_addresses);
}
}
+
+ /* Anything remaining in cur_addr_set_names refers to an address set that
+ * has been deleted from the southbound database. We should delete
+ * the corresponding symtab entry. */
+ const char *cur_node, *next_node;
+ SSET_FOR_EACH_SAFE (cur_node, next_node, &cur_addr_set_names) {
+ expr_macros_remove(&expr_address_sets, cur_node);
+
+ struct address_set *addr_set
+ = shash_find_and_delete(&local_address_sets, cur_node);
+ address_set_destroy(addr_set);
+
+ struct sset_node *sset_node = SSET_NODE_FROM_NAME(cur_node);
+ sset_delete(&cur_addr_set_names, sset_node);
+ }
+
+ sset_destroy(&cur_addr_set_names);
}
+\f
+struct lookup_port_aux {
+ const struct lport_index *lports;
+ const struct mcgroup_index *mcgroups;
+ const struct sbrec_datapath_binding *dp;
+};
-static void
-ldp_destroy(void)
+static void consider_logical_flow(const struct lport_index *lports,
+ const struct mcgroup_index *mcgroups,
+ const struct sbrec_logical_flow *lflow,
+ const struct hmap *local_datapaths,
+ const struct hmap *patched_datapaths,
+ struct group_table *group_table,
+ const struct simap *ct_zones,
+ struct hmap *dhcp_opts_p,
+ uint32_t *conj_id_ofs_p);
+
+static bool
+lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
{
- struct logical_datapath *ldp, *next_ldp;
- HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) {
- ldp_free(ldp);
+ const struct lookup_port_aux *aux = aux_;
+
+ const struct sbrec_port_binding *pb
+ = lport_lookup_by_name(aux->lports, port_name);
+ if (pb && pb->datapath == aux->dp) {
+ *portp = pb->tunnel_key;
+ return true;
+ }
+
+ const struct sbrec_multicast_group *mg
+ = mcgroup_lookup_by_dp_name(aux->mcgroups, aux->dp, port_name);
+ if (mg) {
+ *portp = mg->tunnel_key;
+ return true;
}
+
+ return false;
}
-\f
-void
-lflow_init(void)
+
+static bool
+is_switch(const struct sbrec_datapath_binding *ldp)
{
- symtab_init();
+ return smap_get(&ldp->external_ids, "logical-switch") != NULL;
+
}
-/* Translates logical flows in the Logical_Flow table in the OVN_SB database
- * into OpenFlow flows, adding the OpenFlow flows to 'flow_table'.
- *
- * We put the logical flows into OpenFlow tables 16 through 47 (inclusive). */
-void
-lflow_run(struct controller_ctx *ctx, struct hmap *flow_table)
+/* Adds the logical flows from the Logical_Flow table to flow tables. */
+static void
+add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
+ const struct mcgroup_index *mcgroups,
+ const struct hmap *local_datapaths,
+ const struct hmap *patched_datapaths,
+ struct group_table *group_table,
+ const struct simap *ct_zones)
{
- struct hmap flows = HMAP_INITIALIZER(&flows);
uint32_t conj_id_ofs = 1;
- ldp_run(ctx);
+ struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
+ const struct sbrec_dhcp_options *dhcp_opt_row;
+ SBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opt_row, ctx->ovnsb_idl) {
+ dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
+ dhcp_opt_row->type);
+ }
const struct sbrec_logical_flow *lflow;
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
- /* Find the "struct logical_datapath" asssociated with this
- * Logical_Flow row. If there's no such struct, that must be because
- * no logical ports are bound to that logical datapath, so there's no
- * point in maintaining any flows for it anyway, so skip it. */
- const struct logical_datapath *ldp;
- ldp = ldp_lookup(&lflow->logical_datapath);
- if (!ldp) {
- continue;
- }
+ consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
+ patched_datapaths, group_table, ct_zones,
+ &dhcp_opts, &conj_id_ofs);
+ }
- /* Translate OVN actions into OpenFlow actions. */
- uint64_t ofpacts_stub[64 / 8];
- struct ofpbuf ofpacts;
- struct expr *prereqs;
- uint8_t next_table_id;
- char *error;
-
- ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
- next_table_id = lflow->table_id < 31 ? lflow->table_id + 17 : 0;
- error = actions_parse_string(lflow->actions, &symtab, &ldp->ports,
- next_table_id, &ofpacts, &prereqs);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
- lflow->actions, error);
- free(error);
- continue;
- }
+ dhcp_opts_destroy(&dhcp_opts);
+}
- /* Translate OVN match into table of OpenFlow matches. */
- struct hmap matches;
- struct expr *expr;
+static void
+consider_logical_flow(const struct lport_index *lports,
+ const struct mcgroup_index *mcgroups,
+ const struct sbrec_logical_flow *lflow,
+ const struct hmap *local_datapaths,
+ const struct hmap *patched_datapaths,
+ struct group_table *group_table,
+ const struct simap *ct_zones,
+ struct hmap *dhcp_opts_p,
+ uint32_t *conj_id_ofs_p)
+{
+ /* Determine translation of logical table IDs to physical table IDs. */
+ bool ingress = !strcmp(lflow->pipeline, "ingress");
- expr = expr_parse_string(lflow->match, &symtab, &error);
- if (!error) {
- if (prereqs) {
- expr = expr_combine(EXPR_T_AND, expr, prereqs);
- prereqs = NULL;
+ const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
+ if (!ldp) {
+ return;
+ }
+ if (is_switch(ldp)) {
+ /* For a logical switch datapath, local_datapaths tells us if there
+ * are any local ports for this datapath. If not, we can skip
+ * processing logical flows if that logical switch datapath is not
+ * patched to any logical router.
+ *
+ * Otherwise, we still need both ingress and egress pipeline
+ * because even if there are no local ports, we still may need to
+ * execute the ingress pipeline after a packet leaves a logical
+ * router and we need to do egress pipeline for a switch that
+ * is connected to only routers. Further optimization is possible,
+ * but not based on what we know with local_datapaths right now.
+ *
+ * A better approach would be a kind of "flood fill" algorithm:
+ *
+ * 1. Initialize set S to the logical datapaths that have a port
+ * located on the hypervisor.
+ *
+ * 2. For each patch port P in a logical datapath in S, add the
+ * logical datapath of the remote end of P to S. Iterate
+ * until S reaches a fixed point.
+ *
+ * This can be implemented in northd, which can generate the sets and
+ * save it on each port-binding record in SB, and ovn-controller can
+ * use the information directly. However, there can be update storms
+ * when a pair of patch ports are added/removed to connect/disconnect
+ * large lrouters and lswitches. This need to be studied further.
+ */
+
+ if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
+ if (!get_patched_datapath(patched_datapaths,
+ ldp->tunnel_key)) {
+ return;
}
- expr = expr_annotate(expr, &symtab, &error);
}
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
- lflow->match, error);
- expr_destroy(prereqs);
- ofpbuf_uninit(&ofpacts);
- free(error);
- continue;
+ }
+
+ /* Determine translation of logical table IDs to physical table IDs. */
+ uint8_t first_ptable = (ingress
+ ? OFTABLE_LOG_INGRESS_PIPELINE
+ : OFTABLE_LOG_EGRESS_PIPELINE);
+ uint8_t ptable = first_ptable + lflow->table_id;
+ uint8_t output_ptable = (ingress
+ ? OFTABLE_REMOTE_OUTPUT
+ : OFTABLE_LOG_TO_PHY);
+
+ /* Translate OVN actions into OpenFlow actions.
+ *
+ * XXX Deny changes to 'outport' in egress pipeline. */
+ uint64_t ofpacts_stub[64 / 8];
+ struct ofpbuf ofpacts;
+ struct expr *prereqs;
+ char *error;
+
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ struct lookup_port_aux aux = {
+ .lports = lports,
+ .mcgroups = mcgroups,
+ .dp = lflow->logical_datapath
+ };
+ struct action_params ap = {
+ .symtab = &symtab,
+ .dhcp_opts = dhcp_opts_p,
+ .lookup_port = lookup_port_cb,
+ .aux = &aux,
+ .ct_zones = ct_zones,
+ .group_table = group_table,
+
+ .n_tables = LOG_PIPELINE_LEN,
+ .first_ptable = first_ptable,
+ .cur_ltable = lflow->table_id,
+ .output_ptable = output_ptable,
+ .arp_ptable = OFTABLE_MAC_BINDING,
+ };
+ error = actions_parse_string(lflow->actions, &ap, &ofpacts, &prereqs);
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
+ lflow->actions, error);
+ free(error);
+ return;
+ }
+
+ /* Translate OVN match into table of OpenFlow matches. */
+ struct hmap matches;
+ struct expr *expr;
+
+ expr = expr_parse_string(lflow->match, &symtab,
+ &expr_address_sets, &error);
+ if (!error) {
+ if (prereqs) {
+ expr = expr_combine(EXPR_T_AND, expr, prereqs);
+ prereqs = NULL;
}
+ expr = expr_annotate(expr, &symtab, &error);
+ }
+ if (error) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
+ lflow->match, error);
+ expr_destroy(prereqs);
+ ofpbuf_uninit(&ofpacts);
+ free(error);
+ return;
+ }
- expr = expr_simplify(expr);
- expr = expr_normalize(expr);
- uint32_t n_conjs = expr_to_matches(expr, &ldp->ports, &matches);
- expr_destroy(expr);
-
- /* Prepare the OpenFlow matches for adding to the flow table. */
- struct expr_match *m;
- HMAP_FOR_EACH (m, hmap_node, &matches) {
- match_set_metadata(&m->match, htonll(ldp->integer));
- if (m->match.wc.masks.conj_id) {
- m->match.flow.conj_id += conj_id_ofs;
+ expr = expr_simplify(expr);
+ expr = expr_normalize(expr);
+ uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
+ &matches);
+ expr_destroy(expr);
+
+ /* Prepare the OpenFlow matches for adding to the flow table. */
+ struct expr_match *m;
+ HMAP_FOR_EACH (m, hmap_node, &matches) {
+ match_set_metadata(&m->match,
+ htonll(lflow->logical_datapath->tunnel_key));
+ if (m->match.wc.masks.conj_id) {
+ m->match.flow.conj_id += *conj_id_ofs_p;
+ }
+ if (!m->n) {
+ ofctrl_add_flow(ptable, lflow->priority, &m->match, &ofpacts,
+ &lflow->header_.uuid);
+ } else {
+ uint64_t conj_stubs[64 / 8];
+ struct ofpbuf conj;
+
+ ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
+ for (int i = 0; i < m->n; i++) {
+ const struct cls_conjunction *src = &m->conjunctions[i];
+ struct ofpact_conjunction *dst;
+
+ dst = ofpact_put_CONJUNCTION(&conj);
+ dst->id = src->id + *conj_id_ofs_p;
+ dst->clause = src->clause;
+ dst->n_clauses = src->n_clauses;
}
- if (!m->n) {
- ofctrl_add_flow(flow_table, lflow->table_id + 16,
- lflow->priority, &m->match, &ofpacts);
- } else {
- uint64_t conj_stubs[64 / 8];
- struct ofpbuf conj;
-
- ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
- for (int i = 0; i < m->n; i++) {
- const struct cls_conjunction *src = &m->conjunctions[i];
- struct ofpact_conjunction *dst;
-
- dst = ofpact_put_CONJUNCTION(&conj);
- dst->id = src->id + conj_id_ofs;
- dst->clause = src->clause;
- dst->n_clauses = src->n_clauses;
- }
- ofctrl_add_flow(flow_table, lflow->table_id + 16,
- lflow->priority, &m->match, &conj);
+ ofctrl_add_flow(ptable, lflow->priority, &m->match, &conj,
+ &lflow->header_.uuid);
ofpbuf_uninit(&conj);
- }
+ ofpbuf_uninit(&conj);
}
+ }
- /* Clean up. */
- expr_matches_destroy(&matches);
- ofpbuf_uninit(&ofpacts);
- conj_id_ofs += n_conjs;
+ /* Clean up. */
+ expr_matches_destroy(&matches);
+ ofpbuf_uninit(&ofpacts);
+ *conj_id_ofs_p += n_conjs;
+}
+
+static void
+put_load(const uint8_t *data, size_t len,
+ enum mf_field_id dst, int ofs, int n_bits,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
+ sf->field = mf_from_id(dst);
+ sf->flow_has_vlan = false;
+
+ bitwise_copy(data, len, 0, &sf->value, sf->field->n_bytes, ofs, n_bits);
+ bitwise_one(&sf->mask, sf->field->n_bytes, ofs, n_bits);
+}
+
+static void
+consider_neighbor_flow(const struct lport_index *lports,
+ const struct sbrec_mac_binding *b,
+ struct ofpbuf *ofpacts_p,
+ struct match *match_p)
+{
+ const struct sbrec_port_binding *pb
+ = lport_lookup_by_name(lports, b->logical_port);
+ if (!pb) {
+ return;
+ }
+
+ struct eth_addr mac;
+ if (!eth_addr_from_string(b->mac, &mac)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
+ return;
+ }
+
+ ovs_be32 ip;
+ if (!ip_parse(b->ip, &ip)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
+ return;
+ }
+
+ match_set_metadata(match_p, htonll(pb->datapath->tunnel_key));
+ match_set_reg(match_p, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
+ match_set_reg(match_p, 0, ntohl(ip));
+
+ ofpbuf_clear(ofpacts_p);
+ put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, ofpacts_p);
+
+ ofctrl_add_flow(OFTABLE_MAC_BINDING, 100, match_p, ofpacts_p,
+ &b->header_.uuid);
+}
+
+/* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
+ * southbound database, using 'lports' to resolve logical port names to
+ * numbers. */
+static void
+add_neighbor_flows(struct controller_ctx *ctx,
+ const struct lport_index *lports)
+{
+ struct ofpbuf ofpacts;
+ struct match match;
+ match_init_catchall(&match);
+ ofpbuf_init(&ofpacts, 0);
+
+ const struct sbrec_mac_binding *b;
+ SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
+ consider_neighbor_flow(lports, b, &ofpacts, &match);
}
+ ofpbuf_uninit(&ofpacts);
+}
+\f
+/* Translates logical flows in the Logical_Flow table in the OVN_SB database
+ * into OpenFlow flows. See ovn-architecture(7) for more information. */
+void
+lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
+ const struct mcgroup_index *mcgroups,
+ const struct hmap *local_datapaths,
+ const struct hmap *patched_datapaths,
+ struct group_table *group_table,
+ const struct simap *ct_zones)
+{
+ update_address_sets(ctx);
+ add_logical_flows(ctx, lports, mcgroups, local_datapaths,
+ patched_datapaths, group_table, ct_zones);
+ add_neighbor_flows(ctx, lports);
}
void
lflow_destroy(void)
{
expr_symtab_destroy(&symtab);
- ldp_destroy();
+ shash_destroy(&symtab);
+ expr_macros_destroy(&expr_address_sets);
+ shash_destroy(&expr_address_sets);
}