[ovs-dev] [RFC] ovn: physical network integraiton (WIP)
Russell Bryant
rbryant at redhat.com
Mon Jul 6 17:45:03 UTC 2015
CC: Ben Pfaff <blp at nicira.com>
---
ovn/northd/ovn-northd.c | 42 ++++++++++++++++++++++++++++++++++++++++--
ovn/ovn-nb.ovsschema | 6 ++++++
ovn/ovn-nb.xml | 18 ++++++++++++++++++
ovn/ovn-nbctl.c | 29 +++++++++++++++++++++++++----
ovn/ovn-sb.xml | 2 ++
5 files changed, 91 insertions(+), 6 deletions(-)
During the OVN meeting last week I mentioned that I would share what I had so
far on this. It's far from complete. So far I was just looking at what was
needed in the northbound schema and how that would map to the Pipeline in the
southbound DB.
There was a previous thread that discussed the Neutron API extension called
"provider networks" that this is related to:
http://openvswitch.org/pipermail/dev/2015-June/056765.html
My approach so far is focused on the common use case and isn't trying to support
anything more. It allows you to specify a physical network and optionally a
VLAN ID on a logical switch. If these are set, the intention is that the
logical switch participates in existing L2 network instead of building an
overlay.
With this patch, I can run the following setup that creates 3 logical switches:
1) one "normal" logical switch with no physical network specified
2) one logical switch with a physical network and VLAN ID specified
+ ovn-nbctl lswitch-add sw0
+ ovn-nbctl lswitch-add sw1 physnet1 50
+ ovn-nbctl lport-add sw0 sw0-port1
+ ovn-nbctl lport-add sw0 sw0-port2
+ ovn-nbctl lport-add sw1 sw1-port1
+ ovn-nbctl lport-add sw1 sw1-port2
+ ovn-nbctl lport-set-macs sw0-port1 00:00:00:00:00:01
+ ovn-nbctl lport-set-macs sw0-port2 00:00:00:00:00:02
+ ovn-nbctl lport-set-macs sw1-port1 00:00:00:00:00:03
+ ovn-nbctl lport-set-macs sw1-port2 00:00:00:00:00:04
+ ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01
+ ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02
+ ovn-nbctl lport-set-port-security sw1-port1 00:00:00:00:00:03
+ ovn-nbctl lport-set-port-security sw1-port2 00:00:00:00:00:04
+ ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1
+ ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2
+ ovs-vsctl add-port br-int lport3 -- set Interface lport3 external_ids:iface-id=sw1-port1
+ ovs-vsctl add-port br-int lport4 -- set Interface lport4 external_ids:iface-id=sw1-port2
and here is the resulting Pipeline:
$ ovn-pipeline.py
+--------------------------------------+-----------------------------------------------------------------------------------------------+--------------------------------------+--------------------------------------------------------------+----------+----------+
| _uuid | actions | logical_datapath | match | priority | table_id |
+--------------------------------------+-----------------------------------------------------------------------------------------------+--------------------------------------+--------------------------------------------------------------+----------+----------+
| c35ef6a4-aabe-4ae1-aa2a-d5de754ced86 | "drop;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "eth.src[40]" | 100 | 0 |
| 809b8436-5417-418f-9777-19d2617b6f83 | "drop;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | vlan.present | 100 | 0 |
| 055291de-0d59-4e7d-b933-123b97d24eba | "next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "inport == \"sw0-port1\" && eth.src == {00:00:00:00:00:01}" | 50 | 0 |
| 61d85d82-3a0e-413d-afe0-ce5b45584a07 | "next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "inport == \"sw0-port2\" && eth.src == {00:00:00:00:00:02}" | 50 | 0 |
| 9b583a79-cecb-423c-a282-58033476bfa4 | "drop;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "1" | 0 | 0 |
| 4a96d92e-c257-4e20-8952-d8f270674f7c | "outport = \"sw0-port2\"; next; outport = \"sw0-port1\"; next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "eth.dst[40]" | 100 | 1 |
| 1384f65b-3aa6-4f4c-9174-a7ad3682852c | "outport = \"sw0-port1\"; next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "eth.dst == 00:00:00:00:00:01" | 50 | 1 |
| 15e473fd-0fde-4935-a9ef-fa797db54223 | "outport = \"sw0-port2\"; next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "eth.dst == 00:00:00:00:00:02" | 50 | 1 |
| 82cd5499-e152-486c-819b-f73a39335265 | "next;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "1" | 0 | 2 |
| 994a3e0a-be21-42bd-9036-a22cbf6114ef | "output;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "eth.dst[40]" | 100 | 3 |
| d24319c7-f597-4f5d-b521-1964cea39804 | "output;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "outport == \"sw0-port1\" && eth.dst == {00:00:00:00:00:01}" | 50 | 3 |
| d26a34f8-55de-477b-ae73-1dfc595af77a | "output;" | c409aec4-c61f-462e-b11f-ceb8530e4480 | "outport == \"sw0-port2\" && eth.dst == {00:00:00:00:00:02}" | 50 | 3 |
| dba7561d-b81e-46a6-b824-b484ce69af2d | "drop;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "eth.src[40]" | 100 | 0 |
| 65bab5ea-cbf5-493c-8e98-f1c44647cb24 | "drop;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | vlan.present | 100 | 0 |
| b0c5e1da-21d6-43a9-9e80-9ef60045e77e | "next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "inport == \"sw1-port1\" && eth.src == {00:00:00:00:00:03}" | 50 | 0 |
| 611e78f2-4044-487f-ab9d-34962ac90178 | "next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "inport == \"sw1-port2\" && eth.src == {00:00:00:00:00:04}" | 50 | 0 |
| a8f7ed4b-497b-4843-949b-4ce4ee6d796d | "drop;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "inport == \"sw1-port1\"" | 40 | 0 |
| 1739a4f5-f8cf-4015-bf97-dba01858c3c5 | "drop;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "inport == \"sw1-port2\"" | 40 | 0 |
| 0f644204-f00e-4ad3-96d5-2ca7bbb30198 | "next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "1" | 0 | 0 |
| 31a90046-92c8-4f65-8172-040eba56ed30 | "outport = \"physnet1\"; next; outport = \"sw1-port2\"; next; outport = \"sw1-port1\"; next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "eth.dst[40]" | 100 | 1 |
| c1ce1c42-5330-45d4-9563-9d7305d38cb3 | "outport = \"sw1-port1\"; next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "eth.dst == 00:00:00:00:00:03" | 50 | 1 |
| 65c77542-ad61-44ed-9c5d-6cd15f2ad4e6 | "outport = \"sw1-port2\"; next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "eth.dst == 00:00:00:00:00:04" | 50 | 1 |
| 8c4dbd70-6759-419f-a242-0e61508376b2 | "outport = \"physnet1\"; next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "1" | 0 | 1 |
| 928cb3d8-9fdf-4375-a525-e9199b2f8735 | "next;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "1" | 0 | 2 |
| 54102e76-07a6-48f1-b19e-bbae91897b0d | "output;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "eth.dst[40]" | 100 | 3 |
| 34b151db-a203-48a1-9cec-4417f409968c | "output;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "outport == \"physnet1\"" | 50 | 3 |
| 5c0a4013-4207-4a37-85ee-e8b1dc8c2dfe | "output;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "outport == \"sw1-port1\" && eth.dst == {00:00:00:00:00:03}" | 50 | 3 |
| 7f0fac60-f6b2-4666-a6c9-269b6a74dba7 | "output;" | ffd51033-7206-4072-abf1-fefd4ae416a7 | "outport == \"sw1-port2\" && eth.dst == {00:00:00:00:00:04}" | 50 | 3 |
+--------------------------------------+-----------------------------------------------------------------------------------------------+--------------------------------------+--------------------------------------------------------------+----------+----------+
I still have a couple of issues with how this works. First, I'm specifying the
physical network name as an output port. If a logical port had the same name,
this would cause a problem, but perhaps that's a reasonable constraint if it's
documented - "You can't name a logical port the same name as the logical switch
physical_network".
In the case of a logical switch with a "physical network" specified, I would
expect the outport behavior for a logical port to:
- output the packet to the port if it's local
- if not local, output the packet to the port used to reach the physical
network (specified in the local ovn-controller configuration)
The ovn-controller configuration would be a set of mappings between network
names and the ovs bridge used to connect to that network.
$ ovs-vsctl set Open_vSwitch . external-ids:bridge_mappings=physnet1:br-eth1,physnet2:br-eth2
The current Pipeline breaks down with how broadcast is handled right now. I
don't have a way to say "send the packet to the physical network *and* every
logical port that is local". My only idea right now would be to define a new
action called "broadcast" and let ovn-controller implement it however is
appropriate for that logical switch.
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index c790a90..0d6a9ba 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -241,6 +241,12 @@ lport_is_enabled(const struct nbrec_logical_port *lport)
return !lport->enabled || *lport->enabled;
}
+static bool
+lswitch_has_physical_network(const struct nbrec_logical_switch *lswitch)
+{
+ return lswitch->physical_network && *lswitch->physical_network;
+}
+
/* Updates the Pipeline table in the OVN_SB database, constructing its contents
* based on the OVN_NB database. */
static void
@@ -276,8 +282,10 @@ build_pipeline(struct northd_context *ctx)
/* Port security flows have priority 50 (see below) and will continue
* to the next table if packet source is acceptable. */
- /* Otherwise drop the packet. */
- pipeline_add(&pc, lswitch, 0, 0, "1", "drop;");
+ /* Otherwise drop the packet unless this lswitch is integrated with
+ * a physical network. */
+ pipeline_add(&pc, lswitch, 0, 0, "1",
+ lswitch_has_physical_network(lswitch) ? "next;" : "drop;");
}
/* Table 0: Ingress port security. */
@@ -287,6 +295,13 @@ build_pipeline(struct northd_context *ctx)
struct ds match = DS_EMPTY_INITIALIZER;
ds_put_cstr(&match, "inport == ");
json_string_escape(lport->name, &match);
+ /* When this lswitch is integrated with a physical network, we
+ * want to explicitly drop packets that we know came from a
+ * logical port but didn't pass port security, because the
+ * default after this will be to accept instead of drop. */
+ if (lswitch_has_physical_network(lswitch)) {
+ pipeline_add(&pc, lswitch, 0, 40, ds_cstr(&match), "drop;");
+ }
build_port_security("eth.src",
lport->port_security, lport->n_port_security,
&match);
@@ -308,6 +323,17 @@ build_pipeline(struct northd_context *ctx)
ds_init(&bcast);
ds_init(&unknown);
+
+ if (lswitch_has_physical_network(lswitch)) {
+ ds_put_cstr(&bcast, "outport = ");
+ json_string_escape(lswitch->physical_network, &bcast);
+ ds_put_cstr(&bcast, "; next; ");
+
+ ds_put_cstr(&unknown, "outport = ");
+ json_string_escape(lswitch->physical_network, &unknown);
+ ds_put_cstr(&unknown, "; next; ");
+ }
+
for (size_t i = 0; i < lswitch->n_ports; i++) {
const struct nbrec_logical_port *lport = lswitch->ports[i];
@@ -391,6 +417,18 @@ build_pipeline(struct northd_context *ctx)
ds_destroy(&match);
}
+
+ if (lswitch_has_physical_network(lswitch)) {
+ struct ds match;
+
+ ds_init(&match);
+ ds_put_cstr(&match, "outport == ");
+ json_string_escape(lswitch->physical_network, &match);
+
+ pipeline_add(&pc, lswitch, 3, 50, ds_cstr(&match), "output;");
+
+ ds_destroy(&match);
+ }
}
/* Delete any existing Pipeline rows that were not re-generated. */
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index f7da779..24af83a 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -18,6 +18,12 @@
"refTable": "Logical_Router_Port",
"refType": "strong"},
"min": 0, "max": 1}},
+ "physical_network": {"type": "string"},
+ "physical_network_tag": {
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 1}},
"external_ids": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index cafba14..cfc4a93 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -55,6 +55,24 @@
</p>
</column>
+ <column name="physical_network">
+ <p>
+ If a physical_network name is specified, all logical ports on this switch
+ will be connected to an existing network instead of OVN implementing
+ the network as an overlay. Connectivity to this network is via an OVS
+ bridge that has been configured in ovn-controller's bridge mappings
+ configuration in the <ref db="Open_vSwitch"/> database.
+ </p>
+ </column>
+
+ <column name="physical_network_tag">
+ <p>
+ This option is only relevant if <ref column="physical_network"/>
+ is also set. If a VLAN ID is provided in this column, traffic sent to the
+ <ref column="physical_network"/> will be tagged with this VLAN ID.
+ </p>
+ </column>
+
<column name="router_port">
<p>
The router port to which this logical switch is connected, or empty if
diff --git a/ovn/ovn-nbctl.c b/ovn/ovn-nbctl.c
index bcc5028..3c9925b 100644
--- a/ovn/ovn-nbctl.c
+++ b/ovn/ovn-nbctl.c
@@ -53,7 +53,11 @@ General commands:\n\
show LSWITCH print overview of database contents for LSWITCH\n\
\n\
Logical switch commands:\n\
- lswitch-add [LSWITCH] create a logical switch named LSWITCH\n\
+ lswitch-add [LSWITCH] [PHYSNET] [TAG]\n\
+ create a logical switch named LSWITCH. Optionally\n\
+ specify the name of the physical network that this\n\
+ logical switch should map to. If a physical network\n\
+ is specified, a VLAN ID may also be specified.\n\
lswitch-del LSWITCH delete LSWITCH and all its ports\n\
lswitch-list print the names of all logical switches\n\
lswitch-set-external-id LSWITCH KEY [VALUE]\n\
@@ -143,6 +147,12 @@ print_lswitch(const struct nbrec_logical_switch *lswitch)
{
printf(" lswitch "UUID_FMT" (%s)\n",
UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
+ if (lswitch->physical_network) {
+ printf(" physical network: %s (%"PRId64")\n",
+ lswitch->physical_network,
+ lswitch->physical_network_tag ?
+ lswitch->physical_network_tag[0] : 0);
+ }
for (size_t i = 0; i < lswitch->n_ports; i++) {
const struct nbrec_logical_port *lport = lswitch->ports[i];
@@ -187,9 +197,20 @@ do_lswitch_add(struct ovs_cmdl_context *ctx)
struct nbrec_logical_switch *lswitch;
lswitch = nbrec_logical_switch_insert(nb_ctx->txn);
- if (ctx->argc == 2) {
+ if (ctx->argc > 1) {
nbrec_logical_switch_set_name(lswitch, ctx->argv[1]);
}
+ if (ctx->argc > 2) {
+ nbrec_logical_switch_set_physical_network(lswitch, ctx->argv[2]);
+ }
+ if (ctx->argc > 3) {
+ int64_t tag;
+ if (!ovs_scan(ctx->argv[3], "%"SCNd64, &tag) || tag < 0 || tag > 4095) {
+ VLOG_WARN("Invalid tag '%s'", ctx->argv[4]);
+ return;
+ }
+ nbrec_logical_switch_set_physical_network_tag(lswitch, &tag, 1);
+ }
}
static void
@@ -695,9 +716,9 @@ static const struct ovs_cmdl_command all_commands[] = {
},
{
.name = "lswitch-add",
- .usage = "[LSWITCH]",
+ .usage = "[LSWITCH] [PHYSNET] [TAG]",
.min_args = 0,
- .max_args = 1,
+ .max_args = 3,
.handler = do_lswitch_add,
},
{
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 52fe969..9619cf3 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -572,6 +572,8 @@
<code>outport</code>. Output to the ingress port is implicitly
dropped, that is, <code>output</code> becomes a no-op if
<code>outport</code> == <code>inport</code>.
+ If instead <code>outport</code> refers to a physical network instead
+ of a logical port, the packet will be sent out on that network.
</dd>
<dt><code>next;</code></dt>
--
2.4.3
More information about the dev
mailing list