#include "ovn-controller.h"
#include "ovn/lib/ovn-sb-idl.h"
#include "openvswitch/vlog.h"
+#include "shash.h"
#include "simap.h"
+#include "smap.h"
#include "sset.h"
#include "vswitch-idl.h"
const struct ovsrec_bridge *br_int, const char *this_chassis_id,
struct hmap *flow_table)
{
- struct simap lport_to_ofport = SIMAP_INITIALIZER(&lport_to_ofport);
+ 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];
if (!strcmp(port_rec->name, br_int->name)) {
continue;
}
+ const char *localnet = smap_get(&port_rec->external_ids,
+ "ovn-patch-port");
+
for (int j = 0; j < port_rec->n_interfaces; j++) {
const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
continue;
}
- /* Record as chassis or local logical port. */
- if (chassis_id) {
+ /* Record as patch to local net, chassis, or local logical port. */
+ if (!strcmp(iface_rec->type, "patch") && localnet) {
+ simap_put(&localnet_to_ofport, localnet, ofport);
+ break;
+ } else if (chassis_id) {
enum chassis_tunnel_type tunnel_type;
if (!strcmp(iface_rec->type, "geneve")) {
tunnel_type = GENEVE;
const char *iface_id = smap_get(&iface_rec->external_ids,
"iface-id");
if (iface_id) {
- simap_put(&lport_to_ofport, iface_id, ofport);
+ simap_put(&localvif_to_ofport, iface_id, ofport);
}
}
}
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);
+
+ /* Contains bare "struct hmap_node"s whose hash values are the tunnel_key
+ * of datapaths with at least one local port binding. */
+ struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths);
+
/* 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;
SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
- /* Find the OpenFlow port for the logical port, as 'ofport'. If it's
- * on a remote chassis, this is the OpenFlow port for the tunnel to
- * that chassis (and set 'local' to false). Otherwise, if it's on the
- * chassis we're managing, this is the OpenFlow port for the vif itself
- * (and set 'local' to true). When 'parent_port' is set for a binding,
- * it implies a container sitting inside a VM reachable via a 'tag'.
+ /* Find the OpenFlow port for the logical port, as 'ofport'. This is
+ * one of:
+ *
+ * - 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.
+ *
+ * - 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 (binding->parent_port) {
- ofport = u16_to_ofp(simap_get(&lport_to_ofport,
- binding->parent_port));
+ 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) {
+ if (!binding->tag) {
+ continue;
+ }
+ ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
+ binding->parent_port));
+ if (ofport) {
+ tag = *binding->tag;
+ }
} else {
- ofport = u16_to_ofp(simap_get(&lport_to_ofport,
+ ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
binding->logical_port));
}
/* Table 0, Priority 150 and 100.
* ==============================
*
- * Priority 150 is for traffic belonging to containers. For such
- * traffic, match on the tags and then strip the tag.
- * Priority 100 is for traffic belonging to VMs.
+ * Priority 150 is for tagged traffic. This may be containers in a
+ * VM or a VLAN on a local network. For such traffic, match on the
+ * tags and then strip the tag.
+ *
+ * Priority 100 is for traffic belonging to VMs or untagged locally
+ * connected networks.
*
* For both types of traffic: set MFF_LOG_INPORT to the logical
* input port, MFF_LOG_DATAPATH to the logical datapath, and
* resubmit into the logical ingress pipeline starting at table
* 16. */
- match_init_catchall(&match);
- ofpbuf_clear(&ofpacts);
- match_set_in_port(&match, ofport);
- if (tag) {
- match_set_dl_vlan(&match, htons(tag));
- }
+ 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);
+ }
- /* 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);
+ struct binding_elem *b = xmalloc(sizeof *b);
+ b->binding = binding;
+ list_insert(&ln_vlan->bindings, &b->list_elem);
+ } else {
+ struct hmap_node *ld;
+ ld = hmap_first_with_hash(&local_datapaths,
+ binding->datapath->tunnel_key);
+ if (!ld) {
+ ld = xmalloc(sizeof *ld);
+ hmap_insert(&local_datapaths, ld,
+ binding->datapath->tunnel_key);
+ }
- /* Strip vlans. */
- if (tag) {
- ofpact_put_STRIP_VLAN(&ofpacts);
- }
+ ofpbuf_clear(&ofpacts);
+ match_init_catchall(&match);
+ match_set_in_port(&match, ofport);
+ if (tag) {
+ match_set_dl_vlan(&match, htons(tag));
+ }
- /* 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);
+ /* 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);
+
+ /* Strip vlans. */
+ if (tag) {
+ ofpact_put_STRIP_VLAN(&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);
+ }
/* Table 33, priority 100.
* =======================
continue;
}
- if (simap_contains(&lport_to_ofport, port->logical_port)) {
+ if (simap_contains(&localvif_to_ofport,
+ port->parent_port
+ ? port->parent_port : port->logical_port)) {
put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
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);
}
}
ofctrl_add_flow(flow_table, OFTABLE_DROP_LOOPBACK, 0, &match, &ofpacts);
ofpbuf_uninit(&ofpacts);
- simap_destroy(&lport_to_ofport);
+ simap_destroy(&localvif_to_ofport);
struct chassis_tunnel *tun_next;
HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) {
hmap_remove(&tunnels, &tun->hmap_node);
free(tun);
}
hmap_destroy(&tunnels);
+
+ /* Table 0, Priority 150 and 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.
+ *
+ * Use priority 150 for inputs that match both the network and a VLAN tag.
+ * Use priority 100 for matching untagged traffic from the local network.
+ */
+ 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));
+ }
+
+ struct ofpbuf ofpacts;
+ ofpbuf_init(&ofpacts, 0);
+
+ if (ln_vlan->tag) {
+ 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, ln_vlan->tag ? 150 : 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);
+
+ struct hmap_node *node;
+ while ((node = hmap_first(&local_datapaths))) {
+ hmap_remove(&local_datapaths, node);
+ free(node);
+ }
+ hmap_destroy(&local_datapaths);
+
+ simap_destroy(&localnet_to_ofport);
}