Skip to content

Commit 9608008

Browse files
numansiddiqueblp
authored andcommitted
ovn: Support a new Logical_Switch_Port.type - 'external'
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>
1 parent bddb73d commit 9608008

11 files changed

+1141
-23
lines changed

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Post-v2.11.0
2525
- OVN:
2626
* Select IPAM mac_prefix in a random manner if not provided by the user
2727
* Added the HA chassis group support.
28+
* Added 'external' logical port support.
2829
- New QoS type "linux-netem" on Linux.
2930

3031
v2.11.0 - 19 Feb 2019

ovn/controller/binding.c

+12
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,18 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
502502
* for them. */
503503
sset_add(local_lports, binding_rec->logical_port);
504504
our_chassis = false;
505+
} else if (!strcmp(binding_rec->type, "external")) {
506+
if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
507+
chassis_rec)) {
508+
our_chassis = ha_chassis_group_is_active(
509+
binding_rec->ha_chassis_group,
510+
active_tunnels, chassis_rec);
511+
512+
add_local_datapath(sbrec_datapath_binding_by_key,
513+
sbrec_port_binding_by_datapath,
514+
sbrec_port_binding_by_name,
515+
binding_rec->datapath, false, local_datapaths);
516+
}
505517
}
506518

507519
if (our_chassis

ovn/controller/ovn-controller.c

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
145145
* ports that have a Gateway_Chassis that point's to our own
146146
* chassis */
147147
sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "chassisredirect");
148+
sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external");
148149
if (chassis) {
149150
/* This should be mostly redundant with the other clauses for port
150151
* bindings, but it allows us to catch any ports that are assigned to

ovn/lib/ovn-util.c

+1
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ static const char *OVN_NB_LSP_TYPES[] = {
319319
"localport",
320320
"router",
321321
"vtep",
322+
"external",
322323
};
323324

324325
bool

ovn/northd/ovn-northd.8.xml

+35-2
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,8 @@ nd_na_router {
626626
<p>
627627
This table adds the DHCPv4 options to a DHCPv4 packet from the
628628
logical ports configured with IPv4 address(es) and DHCPv4 options,
629-
and similarly for DHCPv6 options.
629+
and similarly for DHCPv6 options. This table also adds flows for the
630+
logical ports of type <code>external</code>.
630631
</p>
631632

632633
<ul>
@@ -827,7 +828,39 @@ output;
827828
</li>
828829
</ul>
829830

830-
<h3>Ingress Table 16 Destination Lookup</h3>
831+
<h3>Ingress table 16 External ports</h3>
832+
833+
<p>
834+
Traffic from the <code>external</code> logical ports enter the ingress
835+
datapath pipeline via the <code>localnet</code> port. This table adds the
836+
below logical flows to handle the traffic from these ports.
837+
</p>
838+
839+
<ul>
840+
<li>
841+
<p>
842+
A priority-100 flow is added for each <code>external</code> logical
843+
port which doesn't reside on a chassis to drop the ARP/IPv6 NS
844+
request to the router IP(s) (of the logical switch) which matches
845+
on the <code>inport</code> of the <code>external</code> logical port
846+
and the valid <code>eth.src</code> address(es) of the
847+
<code>external</code> logical port.
848+
</p>
849+
850+
<p>
851+
This flow guarantees that the ARP/NS request to the router IP
852+
address from the external ports is responded by only the chassis
853+
which has claimed these external ports. All the other chassis,
854+
drops these packets.
855+
</p>
856+
</li>
857+
858+
<li>
859+
A priority-0 flow that matches all packets to advances to table 17.
860+
</li>
861+
</ul>
862+
863+
<h3>Ingress Table 17 Destination Lookup</h3>
831864

832865
<p>
833866
This table implements switching behavior. It contains these logical

ovn/northd/ovn-northd.c

+132-11
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ enum ovn_stage {
119119
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 13, "ls_in_dhcp_response") \
120120
PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 14, "ls_in_dns_lookup") \
121121
PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 15, "ls_in_dns_response") \
122-
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 16, "ls_in_l2_lkup") \
122+
PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 16, "ls_in_external_port") \
123+
PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 17, "ls_in_l2_lkup") \
123124
\
124125
/* Logical switch egress stages. */ \
125126
PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
@@ -2342,6 +2343,18 @@ ovn_port_update_sbrec(struct northd_context *ctx,
23422343
}
23432344

23442345
sbrec_port_binding_set_nat_addresses(op->sb, NULL, 0);
2346+
2347+
if (!strcmp(op->nbsp->type, "external")) {
2348+
if (op->nbsp->ha_chassis_group) {
2349+
sync_ha_chassis_group_for_sbpb(
2350+
ctx, op->nbsp->ha_chassis_group,
2351+
sbrec_chassis_by_name, op->sb);
2352+
sset_add(active_ha_chassis_grps,
2353+
op->nbsp->ha_chassis_group->name);
2354+
} else {
2355+
sbrec_port_binding_set_ha_chassis_group(op->sb, NULL);
2356+
}
2357+
}
23452358
} else {
23462359
const char *chassis = NULL;
23472360
if (op->peer && op->peer->od && op->peer->od->nbr) {
@@ -3039,6 +3052,12 @@ lsp_is_up(const struct nbrec_logical_switch_port *lsp)
30393052
return !lsp->up || *lsp->up;
30403053
}
30413054

3055+
static bool
3056+
lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
3057+
{
3058+
return !strcmp(nbsp->type, "external");
3059+
}
3060+
30423061
static bool
30433062
build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
30443063
struct ds *options_action, struct ds *response_action,
@@ -3936,6 +3955,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
39363955
* logical ports of the datapath if the CMS has configured DHCPv4 options.
39373956
* */
39383957
for (size_t i = 0; i < od->nbs->n_ports; i++) {
3958+
if (lsp_is_external(od->nbs->ports[i])) {
3959+
continue;
3960+
}
3961+
39393962
if (od->nbs->ports[i]->dhcpv4_options) {
39403963
const char *server_id = smap_get(
39413964
&od->nbs->ports[i]->dhcpv4_options->options, "server_id");
@@ -4322,6 +4345,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
43224345
continue;
43234346
}
43244347

4348+
if (lsp_is_external(op->nbsp)) {
4349+
continue;
4350+
}
4351+
43254352
ds_clear(&match);
43264353
ds_clear(&actions);
43274354
ds_put_format(&match, "inport == %s", op->json_key);
@@ -4388,6 +4415,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
43884415
continue;
43894416
}
43904417

4418+
if (lsp_is_external(op->nbsp)) {
4419+
continue;
4420+
}
4421+
43914422
for (size_t i = 0; i < op->n_lsp_addrs; i++) {
43924423
for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
43934424
ds_clear(&match);
@@ -4496,6 +4527,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
44964527
continue;
44974528
}
44984529

4530+
bool is_external = lsp_is_external(op->nbsp);
4531+
if (is_external && (!op->od->localnet_port ||
4532+
!op->nbsp->ha_chassis_group)) {
4533+
/* If it's an external port and there is no localnet port
4534+
* and if it doesn't belong to an HA chassis group ignore it. */
4535+
continue;
4536+
}
4537+
44994538
for (size_t i = 0; i < op->n_lsp_addrs; i++) {
45004539
for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
45014540
struct ds options_action = DS_EMPTY_INITIALIZER;
@@ -4508,9 +4547,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
45084547
ds_put_format(
45094548
&match, "inport == %s && eth.src == %s && "
45104549
"ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
4511-
"udp.src == 68 && udp.dst == 67", op->json_key,
4550+
"udp.src == 68 && udp.dst == 67",
4551+
is_external ? op->od->localnet_port->json_key :
4552+
op->json_key,
45124553
op->lsp_addrs[i].ea_s);
45134554

4555+
if (is_external) {
4556+
ds_put_format(&match, " && is_chassis_resident(%s)",
4557+
op->json_key);
4558+
}
4559+
45144560
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
45154561
100, ds_cstr(&match),
45164562
ds_cstr(&options_action));
@@ -4525,9 +4571,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
45254571
*/
45264572
ds_put_format(
45274573
&match, "inport == %s && eth.src == %s && "
4528-
"%s && udp.src == 68 && udp.dst == 67", op->json_key,
4574+
"%s && udp.src == 68 && udp.dst == 67",
4575+
is_external ? op->od->localnet_port->json_key :
4576+
op->json_key,
45294577
op->lsp_addrs[i].ea_s, ds_cstr(&ipv4_addr_match));
45304578

4579+
if (is_external) {
4580+
ds_put_format(&match, " && is_chassis_resident(%s)",
4581+
op->json_key);
4582+
}
4583+
45314584
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
45324585
100, ds_cstr(&match),
45334586
ds_cstr(&options_action));
@@ -4538,8 +4591,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
45384591
ds_put_format(
45394592
&match, "inport == %s && eth.src == %s && "
45404593
"ip4 && udp.src == 68 && udp.dst == 67"
4541-
" && "REGBIT_DHCP_OPTS_RESULT, op->json_key,
4594+
" && "REGBIT_DHCP_OPTS_RESULT,
4595+
is_external ? op->od->localnet_port->json_key :
4596+
op->json_key,
45424597
op->lsp_addrs[i].ea_s);
4598+
4599+
if (is_external) {
4600+
ds_put_format(&match, " && is_chassis_resident(%s)",
4601+
op->json_key);
4602+
}
4603+
45434604
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
45444605
100, ds_cstr(&match),
45454606
ds_cstr(&response_action));
@@ -4560,9 +4621,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
45604621
ds_put_format(
45614622
&match, "inport == %s && eth.src == %s"
45624623
" && ip6.dst == ff02::1:2 && udp.src == 546 &&"
4563-
" udp.dst == 547", op->json_key,
4624+
" udp.dst == 547",
4625+
is_external ? op->od->localnet_port->json_key :
4626+
op->json_key,
45644627
op->lsp_addrs[i].ea_s);
45654628

4629+
if (is_external) {
4630+
ds_put_format(&match, " && is_chassis_resident(%s)",
4631+
op->json_key);
4632+
}
4633+
45664634
ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
45674635
ds_cstr(&match), ds_cstr(&options_action));
45684636

@@ -4614,7 +4682,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
46144682
/* Ingress table 12 and 13: DHCP options and response, by default goto
46154683
* next. (priority 0).
46164684
* Ingress table 14 and 15: DNS lookup and response, by default goto next.
4617-
* (priority 0).*/
4685+
* (priority 0).
4686+
* Ingress table 16 - External port handling, by default goto next.
4687+
* (priority 0). */
46184688

46194689
HMAP_FOR_EACH (od, key_node, datapaths) {
46204690
if (!od->nbs) {
@@ -4625,9 +4695,60 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
46254695
ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;");
46264696
ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
46274697
ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1", "next;");
4698+
ovn_lflow_add(lflows, od, S_SWITCH_IN_EXTERNAL_PORT, 0, "1", "next;");
46284699
}
46294700

4630-
/* Ingress table 16: Destination lookup, broadcast and multicast handling
4701+
HMAP_FOR_EACH (op, key_node, ports) {
4702+
if (!op->nbsp || !lsp_is_external(op->nbsp) ||
4703+
!op->od->localnet_port) {
4704+
continue;
4705+
}
4706+
4707+
/* Table 16: External port. Drop ARP request for router ips from
4708+
* external ports on chassis not binding those ports.
4709+
* This makes the router pipeline to be run only on the chassis
4710+
* binding the external ports. */
4711+
4712+
for (size_t i = 0; i < op->n_lsp_addrs; i++) {
4713+
for (size_t j = 0; j < op->od->n_router_ports; j++) {
4714+
struct ovn_port *rp = op->od->router_ports[j];
4715+
for (size_t k = 0; k < rp->n_lsp_addrs; k++) {
4716+
for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs;
4717+
l++) {
4718+
ds_clear(&match);
4719+
ds_put_format(
4720+
&match, "inport == %s && eth.src == %s"
4721+
" && !is_chassis_resident(%s)"
4722+
" && arp.tpa == %s && arp.op == 1",
4723+
op->od->localnet_port->json_key,
4724+
op->lsp_addrs[i].ea_s, op->json_key,
4725+
rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
4726+
ovn_lflow_add(lflows, op->od,
4727+
S_SWITCH_IN_EXTERNAL_PORT, 100,
4728+
ds_cstr(&match), "drop;");
4729+
}
4730+
for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs;
4731+
l++) {
4732+
ds_clear(&match);
4733+
ds_put_format(
4734+
&match, "inport == %s && eth.src == %s"
4735+
" && !is_chassis_resident(%s)"
4736+
" && nd_ns && ip6.dst == {%s, %s} && "
4737+
"nd.target == %s",
4738+
op->od->localnet_port->json_key,
4739+
op->lsp_addrs[i].ea_s, op->json_key,
4740+
rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
4741+
rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
4742+
rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
4743+
ovn_lflow_add(lflows, op->od,
4744+
S_SWITCH_IN_EXTERNAL_PORT, 100,
4745+
ds_cstr(&match), "drop;");
4746+
}
4747+
}
4748+
}
4749+
}
4750+
}
4751+
/* Ingress table 17: Destination lookup, broadcast and multicast handling
46314752
* (priority 100). */
46324753
HMAP_FOR_EACH (op, key_node, ports) {
46334754
if (!op->nbsp) {
@@ -4647,9 +4768,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
46474768
"outport = \""MC_FLOOD"\"; output;");
46484769
}
46494770

4650-
/* Ingress table 16: Destination lookup, unicast handling (priority 50), */
4771+
/* Ingress table 17: Destination lookup, unicast handling (priority 50), */
46514772
HMAP_FOR_EACH (op, key_node, ports) {
4652-
if (!op->nbsp) {
4773+
if (!op->nbsp || lsp_is_external(op->nbsp)) {
46534774
continue;
46544775
}
46554776

@@ -4766,7 +4887,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
47664887
}
47674888
}
47684889

4769-
/* Ingress table 16: Destination lookup for unknown MACs (priority 0). */
4890+
/* Ingress table 17: Destination lookup for unknown MACs (priority 0). */
47704891
HMAP_FOR_EACH (od, key_node, datapaths) {
47714892
if (!od->nbs) {
47724893
continue;
@@ -4801,7 +4922,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
48014922
* Priority 150 rules drop packets to disabled logical ports, so that they
48024923
* don't even receive multicast or broadcast packets. */
48034924
HMAP_FOR_EACH (op, key_node, ports) {
4804-
if (!op->nbsp) {
4925+
if (!op->nbsp || lsp_is_external(op->nbsp)) {
48054926
continue;
48064927
}
48074928

0 commit comments

Comments
 (0)