CC: Ben Pfaff <b...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev