#include "patch.h"
#include "hash.h"
+#include "lib/hmap.h"
#include "lib/vswitch-idl.h"
#include "openvswitch/vlog.h"
#include "ovn-controller.h"
free(ports);
}
-/* Creates a pair of patch ports that connect bridges 'b1' and 'b2', using a
- * port named 'name1' and 'name2' in each respective bridge.
- * external-ids:'key' in each port is initialized to 'value'.
- *
- * If one or both of the ports already exists, leaves it there and removes it
- * from 'existing_ports'. */
-static void
-create_patch_ports(struct controller_ctx *ctx,
- const char *key, const char *value,
- const struct ovsrec_bridge *b1,
- const struct ovsrec_bridge *b2,
- struct shash *existing_ports)
-{
- char *name1 = patch_port_name(b1->name, b2->name);
- char *name2 = patch_port_name(b2->name, b1->name);
- create_patch_port(ctx, key, value, b1, name1, b2, name2, existing_ports);
- create_patch_port(ctx, key, value, b2, name2, b1, name1, existing_ports);
- free(name2);
- free(name1);
-}
-
static void
remove_port(struct controller_ctx *ctx,
const struct ovsrec_port *port)
static void
add_bridge_mappings(struct controller_ctx *ctx,
const struct ovsrec_bridge *br_int,
- struct shash *existing_ports)
+ struct shash *existing_ports,
+ struct hmap *local_datapaths)
{
/* Get ovn-bridge-mappings. */
const char *mappings_cfg = "";
}
}
- /* Create patch ports. */
+ /* Parse bridge mappings. */
+ struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
char *cur, *next, *start;
next = start = xstrdup(mappings_cfg);
while ((cur = strsep(&next, ",")) && *cur) {
continue;
}
- create_patch_ports(ctx, "ovn-localnet-port", network,
- br_int, ovs_bridge, existing_ports);
+ shash_add(&bridge_mappings, network, ovs_bridge);
}
free(start);
+
+ const struct sbrec_port_binding *binding;
+ SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
+ if (strcmp(binding->type, "localnet")) {
+ /* Not a binding for a localnet port. */
+ continue;
+ }
+
+ struct hmap_node *ld;
+ ld = hmap_first_with_hash(local_datapaths,
+ binding->datapath->tunnel_key);
+ if (!ld) {
+ /* This localnet port is on a datapath with no
+ * logical ports bound to this chassis, so there's no need
+ * to create patch ports for it. */
+ continue;
+ }
+
+ const char *network = smap_get(&binding->options, "network_name");
+ if (!network) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_ERR_RL(&rl, "localnet port '%s' has no network name.",
+ binding->logical_port);
+ continue;
+ }
+ struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings, network);
+ if (!br_ln) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_ERR_RL(&rl, "bridge not found for localnet port '%s' "
+ "with network name '%s'", binding->logical_port, network);
+ continue;
+ }
+
+ char *name1 = patch_port_name(br_int->name, binding->logical_port);
+ char *name2 = patch_port_name(binding->logical_port, br_int->name);
+ create_patch_port(ctx, "ovn-localnet-port", binding->logical_port,
+ br_int, name1, br_ln, name2, existing_ports);
+ create_patch_port(ctx, "ovn-localnet-port", binding->logical_port,
+ br_ln, name2, br_int, name1, existing_ports);
+ free(name1);
+ free(name2);
+ }
+
+ shash_destroy(&bridge_mappings);
}
/* Add one OVS patch port for each OVN logical patch port.
}
void
-patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
+patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
+ struct hmap *local_datapaths)
{
if (!ctx->ovs_idl_txn) {
return;
/* Create in the database any patch ports that should exist. Remove from
* 'existing_ports' any patch ports that do exist in the database and
* should be there. */
- add_bridge_mappings(ctx, br_int, &existing_ports);
+ add_bridge_mappings(ctx, br_int, &existing_ports, local_datapaths);
add_logical_patch_ports(ctx, br_int, &existing_ports);
/* Now 'existing_ports' only still contains patch ports that exist in the
void
physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
const struct ovsrec_bridge *br_int, const char *this_chassis_id,
- const struct simap *ct_zones, struct hmap *flow_table,
- struct hmap *local_datapaths)
+ const struct simap *ct_zones, struct hmap *flow_table)
{
struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport);
struct hmap tunnels = HMAP_INITIALIZER(&tunnels);
- struct simap localnet_to_ofport = SIMAP_INITIALIZER(&localnet_to_ofport);
for (int i = 0; i < br_int->n_ports; i++) {
const struct ovsrec_port *port_rec = br_int->ports[i];
* local logical port. */
bool is_patch = !strcmp(iface_rec->type, "patch");
if (is_patch && localnet) {
- simap_put(&localnet_to_ofport, localnet, ofport);
+ /* localnet patch ports can be handled just like VIFs. */
+ simap_put(&localvif_to_ofport, localnet, ofport);
break;
} else if (is_patch && logpatch) {
/* Logical patch ports can be handled just like VIFs. */
struct ofpbuf ofpacts;
ofpbuf_init(&ofpacts, 0);
- struct binding_elem {
- struct ovs_list list_elem;
- const struct sbrec_port_binding *binding;
- };
- /* The bindings for a given VLAN on a localnet port. */
- struct localnet_vlan {
- struct hmap_node node;
- int tag;
- struct ovs_list bindings;
- };
- /* A hash of localnet_vlans, hashed on VLAN ID, for a localnet port */
- struct localnet_bindings {
- ofp_port_t ofport;
- struct hmap vlans;
- };
- /* Maps from network name to "struct localnet_bindings". */
- struct shash localnet_inputs = SHASH_INITIALIZER(&localnet_inputs);
-
/* Set up flows in table 0 for physical-to-logical translation and in table
* 64 for logical-to-physical translation. */
const struct sbrec_port_binding *binding;
* - If the port is a VIF on the chassis we're managing, the
* OpenFlow port for the VIF. 'tun' will be NULL.
*
- * In this or the next case, for a container nested inside a VM
- * and accessible via a VLAN, 'tag' is the VLAN ID; otherwise
- * 'tag' is 0.
+ * The same logic handles logical patch ports, as well as
+ * localnet patch ports.
*
- * The same logic handles logical patch ports.
+ * For a container nested inside a VM and accessible via a VLAN,
+ * 'tag' is the VLAN ID; otherwise 'tag' is 0.
+ *
+ * For a localnet patch port, if a VLAN ID was configured, 'tag'
+ * is set to that VLAN ID; otherwise 'tag' is 0.
*
* - If the port is on a remote chassis, the OpenFlow port for a
* tunnel to the VIF's remote chassis. 'tun' identifies that
* tunnel.
- *
- * - If the port is a "localnet" port for a network that is
- * attached to the chassis we're managing, the OpenFlow port for
- * the localnet port (a patch port).
- *
- * The "localnet" port may be configured with a VLAN ID. If so,
- * 'tag' will be set to that VLAN ID; otherwise 'tag' is 0.
*/
int tag = 0;
ofp_port_t ofport;
- if (!strcmp(binding->type, "localnet")) {
- const char *network = smap_get(&binding->options, "network_name");
- if (!network) {
- continue;
- }
- ofport = u16_to_ofp(simap_get(&localnet_to_ofport, network));
- if (ofport && binding->tag) {
- tag = *binding->tag;
- }
- } else if (binding->parent_port && *binding->parent_port) {
+ if (binding->parent_port && *binding->parent_port) {
if (!binding->tag) {
continue;
}
} else {
ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
binding->logical_port));
+ if (!strcmp(binding->type, "localnet") && ofport && binding->tag) {
+ tag = *binding->tag;
+ }
}
const struct chassis_tunnel *tun = NULL;
* input port, MFF_LOG_DATAPATH to the logical datapath, and
* resubmit into the logical ingress pipeline starting at table
* 16. */
- if (!strcmp(binding->type, "localnet")) {
- /* The same OpenFlow port may correspond to localnet ports
- * attached to more than one logical datapath, so keep track of
- * all associated bindings and add a flow at the end. */
-
- const char *network
- = smap_get(&binding->options, "network_name");
- struct localnet_bindings *ln_bindings;
- struct hmap_node *node;
- struct localnet_vlan *ln_vlan;
-
- ln_bindings = shash_find_data(&localnet_inputs, network);
- if (!ln_bindings) {
- ln_bindings = xmalloc(sizeof *ln_bindings);
- ln_bindings->ofport = ofport;
- hmap_init(&ln_bindings->vlans);
- shash_add(&localnet_inputs, network, ln_bindings);
- }
- node = hmap_first_with_hash(&ln_bindings->vlans, tag);
- if (node) {
- ASSIGN_CONTAINER(ln_vlan, node, node);
- } else {
- ln_vlan = xmalloc(sizeof *ln_vlan);
- ln_vlan->tag = tag;
- list_init(&ln_vlan->bindings);
- hmap_insert(&ln_bindings->vlans, &ln_vlan->node, tag);
- }
+ ofpbuf_clear(&ofpacts);
+ match_init_catchall(&match);
+ match_set_in_port(&match, ofport);
- struct binding_elem *b = xmalloc(sizeof *b);
- b->binding = binding;
- list_insert(&ln_vlan->bindings, &b->list_elem);
- } else {
- ofpbuf_clear(&ofpacts);
- match_init_catchall(&match);
- match_set_in_port(&match, ofport);
- if (tag) {
- match_set_dl_vlan(&match, htons(tag));
- }
+ /* Match a VLAN tag and strip it, including stripping priority tags
+ * (e.g. VLAN ID 0). In the latter case we'll add a second flow
+ * for frames that lack any 802.1Q header later. */
+ if (tag || !strcmp(binding->type, "localnet")) {
+ match_set_dl_vlan(&match, htons(tag));
+ ofpact_put_STRIP_VLAN(&ofpacts);
+ }
- if (zone_id) {
- put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts);
- }
+ /* Remember the size with just strip vlan added so far,
+ * as we're going to remove this with ofpbuf_pull() later. */
+ uint32_t ofpacts_orig_size = ofpacts.size;
- /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
- put_load(binding->datapath->tunnel_key, MFF_LOG_DATAPATH, 0, 64,
- &ofpacts);
- put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 32,
- &ofpacts);
+ if (zone_id) {
+ put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts);
+ }
- /* Strip vlans. */
- if (tag) {
- ofpact_put_STRIP_VLAN(&ofpacts);
- }
+ /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
+ put_load(binding->datapath->tunnel_key, MFF_LOG_DATAPATH, 0, 64,
+ &ofpacts);
+ put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 32,
+ &ofpacts);
- /* Resubmit to first logical ingress pipeline table. */
- put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
- tag ? 150 : 100, &match, &ofpacts);
+ /* Resubmit to first logical ingress pipeline table. */
+ put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
+ tag ? 150 : 100, &match, &ofpacts);
+
+ if (!tag && !strcmp(binding->type, "localnet")) {
+ /* Add a second flow for frames that lack any 802.1Q
+ * header. For these, drop the OFPACT_STRIP_VLAN
+ * action. */
+ ofpbuf_pull(&ofpacts, ofpacts_orig_size);
+ match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
+ ofctrl_add_flow(flow_table, 0, 100, &match, &ofpacts);
}
/* Table 33, priority 100.
put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts);
} else if (port->chassis) {
sset_add(&remote_chassis, port->chassis->name);
- } else if (!strcmp(port->type, "localnet")) {
- const char *network = smap_get(&port->options, "network_name");
- if (!network) {
- continue;
- }
- if (!simap_contains(&localnet_to_ofport, network)) {
- continue;
- }
- put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
- put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts);
}
}
free(tun);
}
hmap_destroy(&tunnels);
-
- /* Table 0, Priority 100.
- * ======================
- *
- * We have now determined the full set of port bindings associated with
- * each "localnet" network. Only create flows for datapaths that have
- * another local binding. Otherwise, we know it would just be dropped.
- */
- struct shash_node *ln_bindings_node, *ln_bindings_node_next;
- SHASH_FOR_EACH_SAFE (ln_bindings_node, ln_bindings_node_next,
- &localnet_inputs) {
- struct localnet_bindings *ln_bindings = ln_bindings_node->data;
- struct localnet_vlan *ln_vlan, *ln_vlan_next;
- HMAP_FOR_EACH_SAFE (ln_vlan, ln_vlan_next, node, &ln_bindings->vlans) {
- struct match match;
- match_init_catchall(&match);
- match_set_in_port(&match, ln_bindings->ofport);
- if (ln_vlan->tag) {
- match_set_dl_vlan(&match, htons(ln_vlan->tag));
- } else {
- /* Match priority-tagged frames, e.g. VLAN ID 0.
- *
- * We'll add a second flow for frames that lack any 802.1Q
- * header later. */
- match_set_dl_tci_masked(&match, htons(VLAN_CFI),
- htons(VLAN_VID_MASK | VLAN_CFI));
- }
-
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
-
- ofpact_put_STRIP_VLAN(&ofpacts);
- uint32_t ofpacts_orig_size = ofpacts.size;
-
- struct binding_elem *b;
- LIST_FOR_EACH_POP (b, list_elem, &ln_vlan->bindings) {
- struct hmap_node *ld;
- ld = hmap_first_with_hash(local_datapaths,
- b->binding->datapath->tunnel_key);
- if (ld) {
- /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
- put_load(b->binding->datapath->tunnel_key, MFF_LOG_DATAPATH,
- 0, 64, &ofpacts);
- put_load(b->binding->tunnel_key, MFF_LOG_INPORT, 0, 32,
- &ofpacts);
- put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
- }
-
- free(b);
- }
-
- if (ofpacts.size > ofpacts_orig_size) {
- ofctrl_add_flow(flow_table, 0, 100, &match, &ofpacts);
-
- if (!ln_vlan->tag) {
- /* Add a second flow for frames that lack any 802.1Q
- * header. For these, drop the OFPACT_STRIP_VLAN
- * action. */
- ofpbuf_pull(&ofpacts, ofpacts_orig_size);
- match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
- ofctrl_add_flow(flow_table, 0, 100, &match, &ofpacts);
- }
- }
-
- ofpbuf_uninit(&ofpacts);
-
- hmap_remove(&ln_bindings->vlans, &ln_vlan->node);
- free(ln_vlan);
- }
- shash_delete(&localnet_inputs, ln_bindings_node);
- hmap_destroy(&ln_bindings->vlans);
- free(ln_bindings);
- }
- shash_destroy(&localnet_inputs);
-
- simap_destroy(&localnet_to_ofport);
}