Skip to content

Commit

Permalink
ovn: Support a new Logical_Switch_Port.type - 'external'
Browse files Browse the repository at this point in the history
In the case of OpenStack + OVN, when the VMs are booted on
hypervisors supporting SR-IOV nics, there are no OVS ports
for these VMs. When these VMs sends DHCPv4, DHPCv6 or IPv6
Router Solicitation requests, the local ovn-controller
cannot reply to these packets. OpenStack Neutron dhcp agent
service needs to be run to serve these requests.

With the new logical port type - 'external', OVN itself can
handle these requests avoiding the need to deploy any
external services like neutron dhcp agent.

To make use of this feature, CMS has to
 - create a logical port for such VMs
 - set the type to 'external'
 - create an HA chassis group and associate the logical port
   to it or associate an already existing HA chassis group.
 - create a localnet port for the logical switch
 - configure the ovn-bridge-mappings option in the OVS db.

HA chassis with the highest priority becomes the master of
the HA chassis group and the ovn-controller running in that
'chassis', claims the Port_Binding for that logical port
and it adds the necessary DHCPv4/v6 OF flows. Since the packet
enters the logical switch pipeline via the localnet port,
the inport register (reg14) is set
to the tunnel key of localnet port in the match conditions.

In case the chassis goes down for some reason, next higher
priority HA chassis becomes the master and claims the port.

When the VM with the external port, sends an ARP request for
the router ips, only the chassis which has claimed the port,
will reply to the ARP requests. Rest of the chassis on
receiving these packets drop them in the ingress switch
datapath stage - S_SWITCH_IN_EXTERNAL_PORT which is just
before S_SWITCH_IN_L2_LKUP.

This would guarantee that only the chassis which has claimed
the external ports will run the router datapath pipeline.

Acked-by: Mark Michelson <mmichels@redhat.com>
Acked-by: Han Zhou <hzhou8@ebay.com>
Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information
numansiddique authored and blp committed Apr 16, 2019
1 parent bddb73d commit 9608008
Show file tree
Hide file tree
Showing 11 changed files with 1,141 additions and 23 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Post-v2.11.0
- OVN:
* Select IPAM mac_prefix in a random manner if not provided by the user
* Added the HA chassis group support.
* Added 'external' logical port support.
- New QoS type "linux-netem" on Linux.

v2.11.0 - 19 Feb 2019
Expand Down
12 changes: 12 additions & 0 deletions ovn/controller/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,18 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
* for them. */
sset_add(local_lports, binding_rec->logical_port);
our_chassis = false;
} else if (!strcmp(binding_rec->type, "external")) {
if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
chassis_rec)) {
our_chassis = ha_chassis_group_is_active(
binding_rec->ha_chassis_group,
active_tunnels, chassis_rec);

add_local_datapath(sbrec_datapath_binding_by_key,
sbrec_port_binding_by_datapath,
sbrec_port_binding_by_name,
binding_rec->datapath, false, local_datapaths);
}
}

if (our_chassis
Expand Down
1 change: 1 addition & 0 deletions ovn/controller/ovn-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
* ports that have a Gateway_Chassis that point's to our own
* chassis */
sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "chassisredirect");
sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external");
if (chassis) {
/* This should be mostly redundant with the other clauses for port
* bindings, but it allows us to catch any ports that are assigned to
Expand Down
1 change: 1 addition & 0 deletions ovn/lib/ovn-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ static const char *OVN_NB_LSP_TYPES[] = {
"localport",
"router",
"vtep",
"external",
};

bool
Expand Down
37 changes: 35 additions & 2 deletions ovn/northd/ovn-northd.8.xml
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ nd_na_router {
<p>
This table adds the DHCPv4 options to a DHCPv4 packet from the
logical ports configured with IPv4 address(es) and DHCPv4 options,
and similarly for DHCPv6 options.
and similarly for DHCPv6 options. This table also adds flows for the
logical ports of type <code>external</code>.
</p>

<ul>
Expand Down Expand Up @@ -827,7 +828,39 @@ output;
</li>
</ul>

<h3>Ingress Table 16 Destination Lookup</h3>
<h3>Ingress table 16 External ports</h3>

<p>
Traffic from the <code>external</code> logical ports enter the ingress
datapath pipeline via the <code>localnet</code> port. This table adds the
below logical flows to handle the traffic from these ports.
</p>

<ul>
<li>
<p>
A priority-100 flow is added for each <code>external</code> logical
port which doesn't reside on a chassis to drop the ARP/IPv6 NS
request to the router IP(s) (of the logical switch) which matches
on the <code>inport</code> of the <code>external</code> logical port
and the valid <code>eth.src</code> address(es) of the
<code>external</code> logical port.
</p>

<p>
This flow guarantees that the ARP/NS request to the router IP
address from the external ports is responded by only the chassis
which has claimed these external ports. All the other chassis,
drops these packets.
</p>
</li>

<li>
A priority-0 flow that matches all packets to advances to table 17.
</li>
</ul>

<h3>Ingress Table 17 Destination Lookup</h3>

<p>
This table implements switching behavior. It contains these logical
Expand Down
143 changes: 132 additions & 11 deletions ovn/northd/ovn-northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ enum ovn_stage {
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 13, "ls_in_dhcp_response") \
PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 14, "ls_in_dns_lookup") \
PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 15, "ls_in_dns_response") \
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 16, "ls_in_l2_lkup") \
PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 16, "ls_in_external_port") \
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 17, "ls_in_l2_lkup") \
\
/* Logical switch egress stages. */ \
PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
Expand Down Expand Up @@ -2342,6 +2343,18 @@ ovn_port_update_sbrec(struct northd_context *ctx,
}

sbrec_port_binding_set_nat_addresses(op->sb, NULL, 0);

if (!strcmp(op->nbsp->type, "external")) {
if (op->nbsp->ha_chassis_group) {
sync_ha_chassis_group_for_sbpb(
ctx, op->nbsp->ha_chassis_group,
sbrec_chassis_by_name, op->sb);
sset_add(active_ha_chassis_grps,
op->nbsp->ha_chassis_group->name);
} else {
sbrec_port_binding_set_ha_chassis_group(op->sb, NULL);
}
}
} else {
const char *chassis = NULL;
if (op->peer && op->peer->od && op->peer->od->nbr) {
Expand Down Expand Up @@ -3039,6 +3052,12 @@ lsp_is_up(const struct nbrec_logical_switch_port *lsp)
return !lsp->up || *lsp->up;
}

static bool
lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
{
return !strcmp(nbsp->type, "external");
}

static bool
build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
struct ds *options_action, struct ds *response_action,
Expand Down Expand Up @@ -3936,6 +3955,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
* logical ports of the datapath if the CMS has configured DHCPv4 options.
* */
for (size_t i = 0; i < od->nbs->n_ports; i++) {
if (lsp_is_external(od->nbs->ports[i])) {
continue;
}

if (od->nbs->ports[i]->dhcpv4_options) {
const char *server_id = smap_get(
&od->nbs->ports[i]->dhcpv4_options->options, "server_id");
Expand Down Expand Up @@ -4322,6 +4345,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}

if (lsp_is_external(op->nbsp)) {
continue;
}

ds_clear(&match);
ds_clear(&actions);
ds_put_format(&match, "inport == %s", op->json_key);
Expand Down Expand Up @@ -4388,6 +4415,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}

if (lsp_is_external(op->nbsp)) {
continue;
}

for (size_t i = 0; i < op->n_lsp_addrs; i++) {
for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
ds_clear(&match);
Expand Down Expand Up @@ -4496,6 +4527,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}

bool is_external = lsp_is_external(op->nbsp);
if (is_external && (!op->od->localnet_port ||
!op->nbsp->ha_chassis_group)) {
/* If it's an external port and there is no localnet port
* 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++) {
for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
struct ds options_action = DS_EMPTY_INITIALIZER;
Expand All @@ -4508,9 +4547,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ds_put_format(
&match, "inport == %s && eth.src == %s && "
"ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
"udp.src == 68 && udp.dst == 67", op->json_key,
"udp.src == 68 && udp.dst == 67",
is_external ? op->od->localnet_port->json_key :
op->json_key,
op->lsp_addrs[i].ea_s);

if (is_external) {
ds_put_format(&match, " && is_chassis_resident(%s)",
op->json_key);
}

ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
100, ds_cstr(&match),
ds_cstr(&options_action));
Expand All @@ -4525,9 +4571,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
*/
ds_put_format(
&match, "inport == %s && eth.src == %s && "
"%s && udp.src == 68 && udp.dst == 67", op->json_key,
"%s && udp.src == 68 && udp.dst == 67",
is_external ? op->od->localnet_port->json_key :
op->json_key,
op->lsp_addrs[i].ea_s, ds_cstr(&ipv4_addr_match));

if (is_external) {
ds_put_format(&match, " && is_chassis_resident(%s)",
op->json_key);
}

ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
100, ds_cstr(&match),
ds_cstr(&options_action));
Expand All @@ -4538,8 +4591,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ds_put_format(
&match, "inport == %s && eth.src == %s && "
"ip4 && udp.src == 68 && udp.dst == 67"
" && "REGBIT_DHCP_OPTS_RESULT, op->json_key,
" && "REGBIT_DHCP_OPTS_RESULT,
is_external ? op->od->localnet_port->json_key :
op->json_key,
op->lsp_addrs[i].ea_s);

if (is_external) {
ds_put_format(&match, " && is_chassis_resident(%s)",
op->json_key);
}

ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
100, ds_cstr(&match),
ds_cstr(&response_action));
Expand All @@ -4560,9 +4621,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ds_put_format(
&match, "inport == %s && eth.src == %s"
" && ip6.dst == ff02::1:2 && udp.src == 546 &&"
" udp.dst == 547", op->json_key,
" udp.dst == 547",
is_external ? op->od->localnet_port->json_key :
op->json_key,
op->lsp_addrs[i].ea_s);

if (is_external) {
ds_put_format(&match, " && is_chassis_resident(%s)",
op->json_key);
}

ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
ds_cstr(&match), ds_cstr(&options_action));

Expand Down Expand Up @@ -4614,7 +4682,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
/* Ingress table 12 and 13: DHCP options and response, by default goto
* next. (priority 0).
* Ingress table 14 and 15: DNS lookup and response, by default goto next.
* (priority 0).*/
* (priority 0).
* Ingress table 16 - External port handling, by default goto next.
* (priority 0). */

HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbs) {
Expand All @@ -4625,9 +4695,60 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_IN_EXTERNAL_PORT, 0, "1", "next;");
}

/* Ingress table 16: Destination lookup, broadcast and multicast handling
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp || !lsp_is_external(op->nbsp) ||
!op->od->localnet_port) {
continue;
}

/* Table 16: 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. */

for (size_t i = 0; i < op->n_lsp_addrs; i++) {
for (size_t j = 0; j < op->od->n_router_ports; j++) {
struct ovn_port *rp = op->od->router_ports[j];
for (size_t k = 0; k < rp->n_lsp_addrs; k++) {
for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs;
l++) {
ds_clear(&match);
ds_put_format(
&match, "inport == %s && eth.src == %s"
" && !is_chassis_resident(%s)"
" && arp.tpa == %s && arp.op == 1",
op->od->localnet_port->json_key,
op->lsp_addrs[i].ea_s, op->json_key,
rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
ovn_lflow_add(lflows, op->od,
S_SWITCH_IN_EXTERNAL_PORT, 100,
ds_cstr(&match), "drop;");
}
for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs;
l++) {
ds_clear(&match);
ds_put_format(
&match, "inport == %s && eth.src == %s"
" && !is_chassis_resident(%s)"
" && nd_ns && ip6.dst == {%s, %s} && "
"nd.target == %s",
op->od->localnet_port->json_key,
op->lsp_addrs[i].ea_s, op->json_key,
rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
ovn_lflow_add(lflows, op->od,
S_SWITCH_IN_EXTERNAL_PORT, 100,
ds_cstr(&match), "drop;");
}
}
}
}
}
/* Ingress table 17: Destination lookup, broadcast and multicast handling
* (priority 100). */
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp) {
Expand All @@ -4647,9 +4768,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
"outport = \""MC_FLOOD"\"; output;");
}

/* Ingress table 16: Destination lookup, unicast handling (priority 50), */
/* Ingress table 17: Destination lookup, unicast handling (priority 50), */
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp) {
if (!op->nbsp || lsp_is_external(op->nbsp)) {
continue;
}

Expand Down Expand Up @@ -4766,7 +4887,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
}
}

/* Ingress table 16: Destination lookup for unknown MACs (priority 0). */
/* Ingress table 17: Destination lookup for unknown MACs (priority 0). */
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbs) {
continue;
Expand Down Expand Up @@ -4801,7 +4922,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
* Priority 150 rules drop packets to disabled logical ports, so that they
* don't even receive multicast or broadcast packets. */
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp) {
if (!op->nbsp || lsp_is_external(op->nbsp)) {
continue;
}

Expand Down

0 comments on commit 9608008

Please sign in to comment.