Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Support logical switches with multiple localnet ports
Assuming only a single localnet port is actually plugged mapped on
each chassis, this allows to maintain disjoint networks plugged to the
same switch.  This is useful to simplify resource management for
OpenStack "routed provider networks" feature [1] where a single
"network" (which traditionally maps to logical switches in OVN) is
comprised of multiple L2 segments and assumes external L3 routing
implemented between the segments.

[1]: https://docs.openstack.org/ocata/networking-guide/config-routed-networks.html

Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Acked-by: Numan Siddique <numans@ovn.org>
Signed-off-by: Numan Siddique <numans@ovn.org>
  • Loading branch information
booxter authored and numansiddique committed May 20, 2020
1 parent 6cced65 commit ba40eff
Show file tree
Hide file tree
Showing 7 changed files with 634 additions and 53 deletions.
13 changes: 13 additions & 0 deletions controller/binding.c
Expand Up @@ -694,12 +694,25 @@ add_localnet_egress_interface_mappings(
}
}

static bool
is_network_plugged(const struct sbrec_port_binding *binding_rec,
struct shash *bridge_mappings)
{
const char *network = smap_get(&binding_rec->options, "network_name");
return network ? !!shash_find_data(bridge_mappings, network) : false;
}

static void
consider_localnet_port(const struct sbrec_port_binding *binding_rec,
struct shash *bridge_mappings,
struct sset *egress_ifaces,
struct hmap *local_datapaths)
{
/* Ignore localnet ports for unplugged networks. */
if (!is_network_plugged(binding_rec, bridge_mappings)) {
return;
}

add_localnet_egress_interface_mappings(binding_rec,
bridge_mappings, egress_ifaces);

Expand Down
14 changes: 11 additions & 3 deletions controller/patch.c
Expand Up @@ -198,8 +198,10 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
continue;
}

bool is_localnet = false;
const char *patch_port_id;
if (!strcmp(binding->type, "localnet")) {
is_localnet = true;
patch_port_id = "ovn-localnet-port";
} else if (!strcmp(binding->type, "l2gateway")) {
if (!binding->chassis
Expand All @@ -224,9 +226,15 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
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 %s port '%s' "
"with network name '%s'",
binding->type, binding->logical_port, network);
if (!is_localnet) {
VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
"with network name '%s'",
binding->type, binding->logical_port, network);
} else {
VLOG_INFO_RL(&rl, "bridge not found for localnet port '%s' "
"with network name '%s'; skipping",
binding->logical_port, network);
}
continue;
}

Expand Down
62 changes: 39 additions & 23 deletions northd/ovn-northd.c
Expand Up @@ -543,7 +543,9 @@ struct ovn_datapath {
/* The "derived" OVN port representing the instance of l3dgw_port on
* the "redirect-chassis". */
struct ovn_port *l3redirect_port;
struct ovn_port *localnet_port;

struct ovn_port **localnet_ports;
size_t n_localnet_ports;

struct ovs_list lr_list; /* In list of logical router datapaths. */
/* The logical router group to which this datapath belongs.
Expand Down Expand Up @@ -611,6 +613,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
ovn_destroy_tnlids(&od->port_tnlids);
bitmap_free(od->ipam_info.allocated_ipv4s);
free(od->router_ports);
free(od->localnet_ports);
ovn_ls_port_group_destroy(&od->nb_pgs);
destroy_mcast_info_for_datapath(od);

Expand Down Expand Up @@ -2025,6 +2028,7 @@ join_logical_ports(struct northd_context *ctx,
struct ovn_datapath *od;
HMAP_FOR_EACH (od, key_node, datapaths) {
if (od->nbs) {
size_t n_allocated_localnet_ports = 0;
for (size_t i = 0; i < od->nbs->n_ports; i++) {
const struct nbrec_logical_switch_port *nbsp
= od->nbs->ports[i];
Expand Down Expand Up @@ -2059,7 +2063,12 @@ join_logical_ports(struct northd_context *ctx,
}

if (!strcmp(nbsp->type, "localnet")) {
od->localnet_port = op;
if (od->n_localnet_ports >= n_allocated_localnet_ports) {
od->localnet_ports = x2nrealloc(
od->localnet_ports, &n_allocated_localnet_ports,
sizeof *od->localnet_ports);
}
od->localnet_ports[od->n_localnet_ports++] = op;
}

op->lsp_addrs
Expand Down Expand Up @@ -3016,7 +3025,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
"reside-on-redirect-chassis", false) ||
op->peer == op->peer->od->l3dgw_port)) {
add_router_port_garp = true;
} else if (chassis && op->od->localnet_port) {
} else if (chassis && op->od->n_localnet_ports) {
add_router_port_garp = true;
}

Expand Down Expand Up @@ -4734,8 +4743,8 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
for (size_t i = 0; i < od->n_router_ports; i++) {
build_pre_acl_flows(od, od->router_ports[i], lflows);
}
if (od->localnet_port) {
build_pre_acl_flows(od, od->localnet_port, lflows);
for (size_t i = 0; i < od->n_localnet_ports; i++) {
build_pre_acl_flows(od, od->localnet_ports[i], lflows);
}

/* Ingress and Egress Pre-ACL Table (Priority 110).
Expand Down Expand Up @@ -6001,9 +6010,9 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
/* Send a the packet only to the router pipeline and skip flooding it
* in the broadcast domain (except for the localnet port).
*/
if (od->localnet_port) {
for (size_t i = 0; i < od->n_localnet_ports; i++) {
ds_put_format(&actions, "clone { outport = %s; output; }; ",
od->localnet_port->json_key);
od->localnet_ports[i]->json_key);
}
ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
Expand Down Expand Up @@ -6585,25 +6594,31 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
}

bool is_external = lsp_is_external(op->nbsp);
if (is_external && (!op->od->localnet_port ||
if (is_external && (!op->od->n_localnet_ports ||
!op->nbsp->ha_chassis_group)) {
/* If it's an external port and there is no localnet port
/* If it's an external port and there are no localnet ports
* and if it doesn't belong to an HA chassis group ignore it. */
continue;
}

for (size_t i = 0; i < op->n_lsp_addrs; i++) {
const char *json_key;
if (is_external) {
json_key = op->od->localnet_port->json_key;
for (size_t j = 0; j < op->od->n_localnet_ports; j++) {
build_dhcpv4_options_flows(
op, &op->lsp_addrs[i],
op->od->localnet_ports[j]->json_key, is_external,
lflows);
build_dhcpv6_options_flows(
op, &op->lsp_addrs[i],
op->od->localnet_ports[j]->json_key, is_external,
lflows);
}
} else {
json_key = op->json_key;
build_dhcpv4_options_flows(op, &op->lsp_addrs[i], op->json_key,
is_external, lflows);
build_dhcpv6_options_flows(op, &op->lsp_addrs[i], op->json_key,
is_external, lflows);
}
build_dhcpv4_options_flows(op, &op->lsp_addrs[i], json_key,
is_external, lflows);

build_dhcpv6_options_flows(op, &op->lsp_addrs[i], json_key,
is_external, lflows);
}
}

Expand Down Expand Up @@ -6659,17 +6674,18 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
}

HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp || !lsp_is_external(op->nbsp) ||
!op->od->localnet_port) {
if (!op->nbsp || !lsp_is_external(op->nbsp)) {
continue;
}

/* Table 18: External port. Drop ARP request for router ips from
* external ports on chassis not binding those ports.
* This makes the router pipeline to be run only on the chassis
* binding the external ports. */
build_drop_arp_nd_flows_for_unbound_router_ports(
op, op->od->localnet_port, lflows);
for (size_t i = 0; i < op->od->n_localnet_ports; i++) {
build_drop_arp_nd_flows_for_unbound_router_ports(
op, op->od->localnet_ports[i], lflows);
}
}

char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
Expand Down Expand Up @@ -6887,7 +6903,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ETH_ADDR_ARGS(mac));
if (op->peer->od->l3dgw_port
&& op->peer->od->l3redirect_port
&& op->od->localnet_port) {
&& op->od->n_localnet_ports) {
bool add_chassis_resident_check = false;
if (op->peer == op->peer->od->l3dgw_port) {
/* The peer of this port represents a distributed
Expand Down Expand Up @@ -8193,7 +8209,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
op->lrp_networks.ipv4_addrs[i].addr_s);

if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer
&& op->peer->od->localnet_port) {
&& op->peer->od->n_localnet_ports) {
bool add_chassis_resident_check = false;
if (op == op->od->l3dgw_port) {
/* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
Expand Down
50 changes: 37 additions & 13 deletions ovn-architecture.7.xml
Expand Up @@ -441,9 +441,8 @@

<p>
A <code>localnet</code> logical switch port bridges a logical switch to a
physical VLAN. Any given logical switch should have no more than one
<code>localnet</code> port. Such a logical switch is used in two
scenarios:
physical VLAN. A logical switch may have one or more <code>localnet</code>
ports. Such a logical switch is used in two scenarios:
</p>

<ul>
Expand All @@ -463,6 +462,31 @@
</li>
</ul>

<p>
When a logical switch contains multiple <code>localnet</code> ports, the
following is assumed.
</p>

<ul>
<li>
Each chassis has a bridge mapping for one of the <code>localnet</code>
physical networks only.
</li>

<li>
To facilitate interconnectivity between VIF ports of the switch that are
located on different chassis with different physical network
connectivity, the fabric implements L3 routing between these adjacent
physical network segments.
</li>
</ul>

<p>
Note: nothing said above implies that a chassis cannot be plugged to
multiple physical networks as long as they belong to different
switches.
</p>

<p>
A <code>localport</code> logical switch port is a special kind of VIF
logical switch port. These ports are present in every chassis, not bound
Expand Down Expand Up @@ -1951,13 +1975,13 @@
<ol>
<li>
The packet first enters the ingress pipeline, and then egress pipeline of
the source localnet logical switch datapath and is sent out via the
the source localnet logical switch datapath and is sent out via a
localnet port of the source localnet logical switch (instead of sending
it to router pipeline).
</li>

<li>
The gateway chassis receives the packet via the localnet port of the
The gateway chassis receives the packet via a localnet port of the
source localnet logical switch and sends it to the integration bridge.
The packet then enters the ingress pipeline, and then egress pipeline of
the source localnet logical switch datapath and enters the ingress
Expand All @@ -1972,11 +1996,11 @@
From the router datapath, packet enters the ingress pipeline and then
egress pipeline of the destination localnet logical switch datapath.
It then goes out of the integration bridge to the provider bridge (
belonging to the destination logical switch) via the localnet port.
belonging to the destination logical switch) via a localnet port.
</li>

<li>
The destination chassis receives the packet via the localnet port and
The destination chassis receives the packet via a localnet port and
sends it to the integration bridge. The packet enters the
ingress pipeline and then egress pipeline of the destination localnet
logical switch and finally delivered to the destination VM port.
Expand All @@ -1991,13 +2015,13 @@
<ol>
<li>
The packet first enters the ingress pipeline, and then egress pipeline of
the source localnet logical switch datapath and is sent out via the
the source localnet logical switch datapath and is sent out via a
localnet port of the source localnet logical switch (instead of sending
it to router pipeline).
</li>

<li>
The gateway chassis receives the packet via the localnet port of the
The gateway chassis receives the packet via a localnet port of the
source localnet logical switch and sends it to the integration bridge.
The packet then enters the ingress pipeline, and then egress pipeline of
the source localnet logical switch datapath and enters the ingress
Expand All @@ -2013,7 +2037,7 @@
egress pipeline of the localnet logical switch datapath which provides
external connectivity. It then goes out of the integration bridge to the
provider bridge (belonging to the logical switch which provides external
connectivity) via the localnet port.
connectivity) via a localnet port.
</li>
</ol>

Expand All @@ -2023,7 +2047,7 @@

<ol>
<li>
The gateway chassis receives the packet from the localnet port of
The gateway chassis receives the packet from a localnet port of
the logical switch which provides external connectivity. The packet then
enters the ingress pipeline and then egress pipeline of the localnet
logical switch (which provides external connectivity). The packet then
Expand All @@ -2034,12 +2058,12 @@
The ingress pipeline of the logical router datapath applies the unNATting
rules. The packet then enters the ingress pipeline and then egress
pipeline of the source localnet logical switch. Since the source VM
doesn't reside in the gateway chassis, the packet is sent out via the
doesn't reside in the gateway chassis, the packet is sent out via a
localnet port of the source logical switch.
</li>

<li>
The source chassis receives the packet via the localnet port and
The source chassis receives the packet via a localnet port and
sends it to the integration bridge. The packet enters the
ingress pipeline and then egress pipeline of the source localnet
logical switch and finally gets delivered to the source VM port.
Expand Down
23 changes: 14 additions & 9 deletions ovn-nb.xml
Expand Up @@ -244,14 +244,14 @@
<p>
There are two kinds of logical switches, that is, ones that fully
virtualize the network (overlay logical switches) and ones that provide
simple connectivity to a physical network (bridged logical switches).
simple connectivity to physical networks (bridged logical switches).
They work in the same way when providing connectivity between logical
ports on same chasis, but differently when connecting remote logical
ports on same chassis, but differently when connecting remote logical
ports. Overlay logical switches connect remote logical ports by tunnels,
while bridged logical switches provide connectivity to remote ports by
bridging the packets to directly connected physical L2 segment with the
bridging the packets to directly connected physical L2 segments with the
help of <code>localnet</code> ports. Each bridged logical switch has
one and only one <code>localnet</code> port, which has only one special
one or more <code>localnet</code> ports, which have only one special
address <code>unknown</code>.
</p>

Expand Down Expand Up @@ -527,10 +527,15 @@

<dt><code>localnet</code></dt>
<dd>
A connection to a locally accessible network from each
<code>ovn-controller</code> instance. A logical switch can only
have a single <code>localnet</code> port attached. This is used
to model direct connectivity to an existing network.
A connection to a locally accessible network from
<code>ovn-controller</code> instances that have a corresponding
bridge mapping. A logical switch can have multiple
<code>localnet</code> ports attached. This type is used to model
direct connectivity to existing networks. In this case, each
chassis should have a mapping for one of the physical networks
only. Note: nothing said above implies that a chassis cannot be
plugged to multiple physical networks as long as they belong to
different switches.
</dd>

<dt><code>localport</code></dt>
Expand Down Expand Up @@ -721,7 +726,7 @@
Required. The name of the network to which the <code>localnet</code>
port is connected. Each hypervisor, via <code>ovn-controller</code>,
uses its local configuration to determine exactly how to connect to
this locally accessible network.
this locally accessible network, if at all.
</column>
</group>

Expand Down

0 comments on commit ba40eff

Please sign in to comment.