On Wed, Jul 7, 2021 at 5:03 PM Mark Michelson <[email protected]> wrote:
>
> On 7/7/21 2:50 PM, Mark Michelson wrote:
> > On 7/7/21 1:41 PM, Numan Siddique wrote:
> >> On Wed, Jun 30, 2021 at 7:57 PM Mark Michelson <[email protected]>
> >> wrote:
> >>>
> >>> Previously, ARP TPAs were filtered down only to "reachable" addresses.
> >>> Reachable addresses are all router interface addresses, as well as NAT
> >>> external addresses and load balancer VIPs that are within the subnet
> >>> handled by a router's port.
> >>>
> >>> However, it is possible that in some configurations, CMSes purposely
> >>> configure NAT or load balancer addresses on a router that are outside
> >>> the router's subnets, and they expect the router to respond to ARPs for
> >>> those addresses.
> >>>
> >>> This commit adds a higher priority flow to logical switches that makes
> >>> it so ARPs targeted at "unreachable" addresses are flooded to all ports.
> >>> This way, the ARPs can reach the router appropriately and receive a
> >>> response.
> >>>
> >>> Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1929901
> >>>
> >>> Signed-off-by: Mark Michelson <[email protected]>
> >>
> >> Acked-by: Numan Siddique <[email protected]>
> >>
> >> I've one comment which probably can be addressed.
> >>
> >> If a configured NAT entry on a logical router is unreachable within
> >> that router, this patch floods
> >> the packet for the ARP destined to that NAT IP in the logical switch
> >> pipeline.
> >>
> >> Before adding the flow to flood, can't we check if the NAT IP is
> >> reachable from other router ports
> >> connected to this logical switch. If so, we can do "outport ==
> >> <REACHABLE_LRP_PORT>" instead
> >> of flooding.
> >>
> >> I think this is possible given that the logical flow is added in the
> >> switch pipeline. We just need to loop through
> >> all the router ports of the logical switch. The question is - is this
> >> efficient and takes up some time on a scaled environment ?
> >>
> >> What do you think ? If this seems fine, it can be a follow up patch
> >> too.
> >
> > I don't think your suggestion would add any appreciable time compared to
> > what we're already doing in ovn-northd. I will attempt this approach and
> > let you know how it goes.
> >
>
> On second thought, I think this should be a follow-up patch as you
> suggested. This particular work has been going on for too long to delay
> for this purpose, IMO.
Sounds good to me.
Numan
>
> >>
> >> Numan
> >>
> >>> ---
> >>> northd/ovn-northd.8.xml | 8 ++
> >>> northd/ovn-northd.c | 162 +++++++++++++++++++++++++++-------------
> >>> northd/ovn_northd.dl | 91 ++++++++++++++++++----
> >>> tests/ovn-northd.at | 99 ++++++++++++++++++++++++
> >>> tests/system-ovn.at | 102 +++++++++++++++++++++++++
> >>> 5 files changed, 395 insertions(+), 67 deletions(-)
> >>>
> >>> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> >>> index beaf5a183..5aedd6619 100644
> >>> --- a/northd/ovn-northd.8.xml
> >>> +++ b/northd/ovn-northd.8.xml
> >>> @@ -1587,6 +1587,14 @@ output;
> >>> logical ports.
> >>> </li>
> >>>
> >>> + <li>
> >>> + Priority-90 flows for each IP address/VIP/NAT address
> >>> configured
> >>> + outside its owning router port's subnet. These flows match ARP
> >>> + requests and ND packets for the specific IP addresses.
> >>> Matched packets
> >>> + are forwarded to the <code>MC_FLOOD</code> multicast group
> >>> which
> >>> + contains all connected logical ports.
> >>> + </li>
> >>> +
> >>> <li>
> >>> Priority-75 flows for each port connected to a logical router
> >>> matching self originated ARP request/ND packets. These
> >>> packets
> >>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> >>> index f6fad281b..d0b325748 100644
> >>> --- a/northd/ovn-northd.c
> >>> +++ b/northd/ovn-northd.c
> >>> @@ -6555,38 +6555,41 @@
> >>> build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
> >>> ds_destroy(&match);
> >>> }
> >>>
> >>> -/*
> >>> - * Ingress table 19: Flows that forward ARP/ND requests only to the
> >>> routers
> >>> - * that own the addresses. Other ARP/ND packets are still flooded in
> >>> the
> >>> - * switching domain as regular broadcast.
> >>> - */
> >>> static void
> >>> -build_lswitch_rport_arp_req_flow_for_ip(struct ds *ip_match,
> >>> - int addr_family,
> >>> - struct ovn_port *patch_op,
> >>> - struct ovn_datapath *od,
> >>> - uint32_t priority,
> >>> - struct hmap *lflows,
> >>> - const struct ovsdb_idl_row
> >>> *stage_hint)
> >>> +arp_nd_ns_match(struct ds *ips, int addr_family, struct ds *match)
> >>> {
> >>> - struct ds match = DS_EMPTY_INITIALIZER;
> >>> - struct ds actions = DS_EMPTY_INITIALIZER;
> >>> -
> >>> /* Packets received from VXLAN tunnels have already been
> >>> through the
> >>> * router pipeline so we should skip them. Normally this is
> >>> done by the
> >>> * multicast_group implementation (VXLAN packets skip table 32
> >>> which
> >>> * delivers to patch ports) but we're bypassing multicast_groups.
> >>> */
> >>> - ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && ");
> >>> + ds_put_cstr(match, FLAGBIT_NOT_VXLAN " && ");
> >>>
> >>> if (addr_family == AF_INET) {
> >>> - ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { ");
> >>> + ds_put_cstr(match, "arp.op == 1 && arp.tpa == {");
> >>> } else {
> >>> - ds_put_cstr(&match, "nd_ns && nd.target == { ");
> >>> + ds_put_cstr(match, "nd_ns && nd.target == {");
> >>> }
> >>>
> >>> - ds_put_cstr(&match, ds_cstr_ro(ip_match));
> >>> - ds_put_cstr(&match, "}");
> >>> + ds_put_cstr(match, ds_cstr_ro(ips));
> >>> + ds_put_cstr(match, "}");
> >>> +}
> >>> +
> >>> +/*
> >>> + * Ingress table 19: Flows that forward ARP/ND requests only to the
> >>> routers
> >>> + * that own the addresses. Other ARP/ND packets are still flooded in
> >>> the
> >>> + * switching domain as regular broadcast.
> >>> + */
> >>> +static void
> >>> +build_lswitch_rport_arp_req_flow_for_reachable_ip(struct ds *ips,
> >>> + int addr_family, struct ovn_port *patch_op, struct ovn_datapath
> >>> *od,
> >>> + uint32_t priority, struct hmap *lflows,
> >>> + const struct ovsdb_idl_row *stage_hint)
> >>> +{
> >>> + struct ds match = DS_EMPTY_INITIALIZER;
> >>> + struct ds actions = DS_EMPTY_INITIALIZER;
> >>> +
> >>> + arp_nd_ns_match(ips, addr_family, &match);
> >>>
> >>> /* Send a the packet to the router pipeline. If the switch has
> >>> non-router
> >>> * ports then flood it there as well.
> >>> @@ -6609,6 +6612,30 @@ build_lswitch_rport_arp_req_flow_for_ip(struct
> >>> ds *ip_match,
> >>> ds_destroy(&actions);
> >>> }
> >>>
> >>> +/*
> >>> + * Ingress table 19: Flows that forward ARP/ND requests for
> >>> "unreachable" IPs
> >>> + * (NAT or load balancer IPs configured on a router that are outside
> >>> the
> >>> + * router's configured subnets).
> >>> + * These ARP/ND packets are flooded in the switching domain as regular
> >>> + * broadcast.
> >>> + */
> >>> +static void
> >>> +build_lswitch_rport_arp_req_flow_for_unreachable_ip(struct ds *ips,
> >>> + int addr_family, struct ovn_datapath *od, uint32_t priority,
> >>> + struct hmap *lflows, const struct ovsdb_idl_row *stage_hint)
> >>> +{
> >>> + struct ds match = DS_EMPTY_INITIALIZER;
> >>> +
> >>> + arp_nd_ns_match(ips, addr_family, &match);
> >>> +
> >>> + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP,
> >>> + priority, ds_cstr(&match),
> >>> + "outport = \""MC_FLOOD"\"; output;",
> >>> + stage_hint);
> >>> +
> >>> + ds_destroy(&match);
> >>> +}
> >>> +
> >>> /*
> >>> * Ingress table 19: Flows that forward ARP/ND requests only to the
> >>> routers
> >>> * that own the addresses.
> >>> @@ -6635,8 +6662,10 @@ build_lswitch_rport_arp_req_flows(struct
> >>> ovn_port *op,
> >>> * router port.
> >>> * Priority: 80.
> >>> */
> >>> - struct ds ips_v4_match = DS_EMPTY_INITIALIZER;
> >>> - struct ds ips_v6_match = DS_EMPTY_INITIALIZER;
> >>> + struct ds ips_v4_match_reachable = DS_EMPTY_INITIALIZER;
> >>> + struct ds ips_v6_match_reachable = DS_EMPTY_INITIALIZER;
> >>> + struct ds ips_v4_match_unreachable = DS_EMPTY_INITIALIZER;
> >>> + struct ds ips_v6_match_unreachable = DS_EMPTY_INITIALIZER;
> >>>
> >>> const char *ip_addr;
> >>> SSET_FOR_EACH (ip_addr, &op->od->lb_ips_v4) {
> >>> @@ -6645,9 +6674,12 @@ build_lswitch_rport_arp_req_flows(struct
> >>> ovn_port *op,
> >>> /* Check if the ovn port has a network configured on which
> >>> we could
> >>> * expect ARP requests for the LB VIP.
> >>> */
> >>> - if (ip_parse(ip_addr, &ipv4_addr) &&
> >>> - lrouter_port_ipv4_reachable(op, ipv4_addr)) {
> >>> - ds_put_format(&ips_v4_match, "%s, ", ip_addr);
> >>> + if (ip_parse(ip_addr, &ipv4_addr)) {
> >>> + if (lrouter_port_ipv4_reachable(op, ipv4_addr)) {
> >>> + ds_put_format(&ips_v4_match_reachable, "%s, ",
> >>> ip_addr);
> >>> + } else {
> >>> + ds_put_format(&ips_v4_match_unreachable, "%s, ",
> >>> ip_addr);
> >>> + }
> >>> }
> >>> }
> >>> SSET_FOR_EACH (ip_addr, &op->od->lb_ips_v6) {
> >>> @@ -6656,9 +6688,12 @@ build_lswitch_rport_arp_req_flows(struct
> >>> ovn_port *op,
> >>> /* Check if the ovn port has a network configured on which
> >>> we could
> >>> * expect NS requests for the LB VIP.
> >>> */
> >>> - if (ipv6_parse(ip_addr, &ipv6_addr) &&
> >>> - lrouter_port_ipv6_reachable(op, &ipv6_addr)) {
> >>> - ds_put_format(&ips_v6_match, "%s, ", ip_addr);
> >>> + if (ipv6_parse(ip_addr, &ipv6_addr)) {
> >>> + if (lrouter_port_ipv6_reachable(op, &ipv6_addr)) {
> >>> + ds_put_format(&ips_v6_match_reachable, "%s, ",
> >>> ip_addr);
> >>> + } else {
> >>> + ds_put_format(&ips_v6_match_unreachable, "%s, ",
> >>> ip_addr);
> >>> + }
> >>> }
> >>> }
> >>>
> >>> @@ -6680,44 +6715,67 @@ build_lswitch_rport_arp_req_flows(struct
> >>> ovn_port *op,
> >>> if (nat_entry_is_v6(nat_entry)) {
> >>> struct in6_addr *addr =
> >>> &nat_entry->ext_addrs.ipv6_addrs[0].addr;
> >>>
> >>> - if (lrouter_port_ipv6_reachable(op, addr) &&
> >>> - !sset_contains(&op->od->lb_ips_v6,
> >>> nat->external_ip)) {
> >>> - ds_put_format(&ips_v6_match, "%s, ", nat->external_ip);
> >>> + if (!sset_contains(&op->od->lb_ips_v6, nat->external_ip)) {
> >>> + if (lrouter_port_ipv6_reachable(op, addr)) {
> >>> + ds_put_format(&ips_v6_match_reachable, "%s, ",
> >>> + nat->external_ip);
> >>> + } else {
> >>> + ds_put_format(&ips_v6_match_unreachable, "%s, ",
> >>> + nat->external_ip);
> >>> + }
> >>> }
> >>> } else {
> >>> ovs_be32 addr = nat_entry->ext_addrs.ipv4_addrs[0].addr;
> >>> -
> >>> - if (lrouter_port_ipv4_reachable(op, addr) &&
> >>> - !sset_contains(&op->od->lb_ips_v4,
> >>> nat->external_ip)) {
> >>> - ds_put_format(&ips_v4_match, "%s, ", nat->external_ip);
> >>> + if (!sset_contains(&op->od->lb_ips_v4, nat->external_ip)) {
> >>> + if (lrouter_port_ipv4_reachable(op, addr)) {
> >>> + ds_put_format(&ips_v4_match_reachable, "%s, ",
> >>> + nat->external_ip);
> >>> + } else {
> >>> + ds_put_format(&ips_v4_match_unreachable, "%s, ",
> >>> + nat->external_ip);
> >>> + }
> >>> }
> >>> }
> >>> }
> >>>
> >>> for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> >>> - ds_put_format(&ips_v4_match, "%s, ",
> >>> + ds_put_format(&ips_v4_match_reachable, "%s, ",
> >>> op->lrp_networks.ipv4_addrs[i].addr_s);
> >>> }
> >>> for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> >>> - ds_put_format(&ips_v6_match, "%s, ",
> >>> + ds_put_format(&ips_v6_match_reachable, "%s, ",
> >>> op->lrp_networks.ipv6_addrs[i].addr_s);
> >>> }
> >>>
> >>> - if (ds_last(&ips_v4_match) != EOF) {
> >>> - ds_chomp(&ips_v4_match, ' ');
> >>> - ds_chomp(&ips_v4_match, ',');
> >>> - build_lswitch_rport_arp_req_flow_for_ip(&ips_v4_match,
> >>> AF_INET, sw_op,
> >>> - sw_od, 80, lflows,
> >>> - stage_hint);
> >>> + if (ds_last(&ips_v4_match_reachable) != EOF) {
> >>> + ds_chomp(&ips_v4_match_reachable, ' ');
> >>> + ds_chomp(&ips_v4_match_reachable, ',');
> >>> + build_lswitch_rport_arp_req_flow_for_reachable_ip(
> >>> + &ips_v4_match_reachable, AF_INET, sw_op, sw_od, 80, lflows,
> >>> + stage_hint);
> >>> }
> >>> - if (ds_last(&ips_v6_match) != EOF) {
> >>> - ds_chomp(&ips_v6_match, ' ');
> >>> - ds_chomp(&ips_v6_match, ',');
> >>> - build_lswitch_rport_arp_req_flow_for_ip(&ips_v6_match,
> >>> AF_INET6, sw_op,
> >>> - sw_od, 80, lflows,
> >>> - stage_hint);
> >>> + if (ds_last(&ips_v6_match_reachable) != EOF) {
> >>> + ds_chomp(&ips_v6_match_reachable, ' ');
> >>> + ds_chomp(&ips_v6_match_reachable, ',');
> >>> + build_lswitch_rport_arp_req_flow_for_reachable_ip(
> >>> + &ips_v6_match_reachable, AF_INET6, sw_op, sw_od, 80,
> >>> lflows,
> >>> + stage_hint);
> >>> }
> >>>
> >>> + if (ds_last(&ips_v4_match_unreachable) != EOF) {
> >>> + ds_chomp(&ips_v4_match_unreachable, ' ');
> >>> + ds_chomp(&ips_v4_match_unreachable, ',');
> >>> + build_lswitch_rport_arp_req_flow_for_unreachable_ip(
> >>> + &ips_v4_match_unreachable, AF_INET, sw_od, 90, lflows,
> >>> + stage_hint);
> >>> + }
> >>> + if (ds_last(&ips_v6_match_unreachable) != EOF) {
> >>> + ds_chomp(&ips_v6_match_unreachable, ' ');
> >>> + ds_chomp(&ips_v6_match_unreachable, ',');
> >>> + build_lswitch_rport_arp_req_flow_for_unreachable_ip(
> >>> + &ips_v6_match_unreachable, AF_INET6, sw_od, 90, lflows,
> >>> + stage_hint);
> >>> + }
> >>> /* Self originated ARP requests/ND need to be flooded as usual.
> >>> *
> >>> * However, if the switch doesn't have any non-router ports we
> >>> shouldn't
> >>> @@ -6728,8 +6786,10 @@ build_lswitch_rport_arp_req_flows(struct
> >>> ovn_port *op,
> >>> if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> >>> build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> >>> lflows);
> >>> }
> >>> - ds_destroy(&ips_v4_match);
> >>> - ds_destroy(&ips_v6_match);
> >>> + ds_destroy(&ips_v4_match_reachable);
> >>> + ds_destroy(&ips_v6_match_reachable);
> >>> + ds_destroy(&ips_v4_match_unreachable);
> >>> + ds_destroy(&ips_v6_match_unreachable);
> >>> }
> >>>
> >>> static void
> >>> diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
> >>> index c9ee742d1..dd6430849 100644
> >>> --- a/northd/ovn_northd.dl
> >>> +++ b/northd/ovn_northd.dl
> >>> @@ -4109,9 +4109,13 @@ Flow(.logical_datapath = sw._uuid,
> >>> * router port.
> >>> * Priority: 80.
> >>> */
> >>> -function get_arp_forward_ips(rp: Intern<RouterPort>): (Set<string>,
> >>> Set<string>) = {
> >>> - var all_ips_v4 = set_empty();
> >>> - var all_ips_v6 = set_empty();
> >>> +function get_arp_forward_ips(rp: Intern<RouterPort>):
> >>> + (Set<string>, Set<string>, Set<string>, Set<string>) =
> >>> +{
> >>> + var reachable_ips_v4 = set_empty();
> >>> + var reachable_ips_v6 = set_empty();
> >>> + var unreachable_ips_v4 = set_empty();
> >>> + var unreachable_ips_v6 = set_empty();
> >>>
> >>> (var lb_ips_v4, var lb_ips_v6)
> >>> = get_router_load_balancer_ips(rp.router, false);
> >>> @@ -4121,7 +4125,9 @@ function get_arp_forward_ips(rp:
> >>> Intern<RouterPort>): (Set<string>, Set<string>)
> >>> */
> >>> match (ip_parse(a)) {
> >>> Some{ipv4} -> if (lrouter_port_ip_reachable(rp,
> >>> IPv4{ipv4})) {
> >>> - all_ips_v4.insert(a)
> >>> + reachable_ips_v4.insert(a)
> >>> + } else {
> >>> + unreachable_ips_v4.insert(a)
> >>> },
> >>> _ -> ()
> >>> }
> >>> @@ -4132,7 +4138,9 @@ function get_arp_forward_ips(rp:
> >>> Intern<RouterPort>): (Set<string>, Set<string>)
> >>> */
> >>> match (ipv6_parse(a)) {
> >>> Some{ipv6} -> if (lrouter_port_ip_reachable(rp,
> >>> IPv6{ipv6})) {
> >>> - all_ips_v6.insert(a)
> >>> + reachable_ips_v6.insert(a)
> >>> + } else {
> >>> + unreachable_ips_v6.insert(a)
> >>> },
> >>> _ -> ()
> >>> }
> >>> @@ -4145,22 +4153,45 @@ function get_arp_forward_ips(rp:
> >>> Intern<RouterPort>): (Set<string>, Set<string>)
> >>> */
> >>> if (lrouter_port_ip_reachable(rp, nat.external_ip)) {
> >>> match (nat.external_ip) {
> >>> - IPv4{_} -> all_ips_v4.insert(nat.nat.external_ip),
> >>> - IPv6{_} -> all_ips_v6.insert(nat.nat.external_ip)
> >>> + IPv4{_} ->
> >>> reachable_ips_v4.insert(nat.nat.external_ip),
> >>> + IPv6{_} ->
> >>> reachable_ips_v6.insert(nat.nat.external_ip)
> >>> + }
> >>> + } else {
> >>> + match (nat.external_ip) {
> >>> + IPv4{_} ->
> >>> unreachable_ips_v4.insert(nat.nat.external_ip),
> >>> + IPv6{_} ->
> >>> unreachable_ips_v6.insert(nat.nat.external_ip),
> >>> }
> >>> }
> >>> }
> >>> };
> >>>
> >>> for (a in rp.networks.ipv4_addrs) {
> >>> - all_ips_v4.insert("${a.addr}")
> >>> + reachable_ips_v4.insert("${a.addr}")
> >>> };
> >>> for (a in rp.networks.ipv6_addrs) {
> >>> - all_ips_v6.insert("${a.addr}")
> >>> + reachable_ips_v6.insert("${a.addr}")
> >>> };
> >>>
> >>> - (all_ips_v4, all_ips_v6)
> >>> + (reachable_ips_v4, reachable_ips_v6, unreachable_ips_v4,
> >>> unreachable_ips_v6)
> >>> }
> >>> +
> >>> +relation &SwitchPortARPForwards(
> >>> + port: Intern<SwitchPort>,
> >>> + reachable_ips_v4: Set<string>,
> >>> + reachable_ips_v6: Set<string>,
> >>> + unreachable_ips_v4: Set<string>,
> >>> + unreachable_ips_v6: Set<string>
> >>> +)
> >>> +
> >>> +&SwitchPortARPForwards(.port = port,
> >>> + .reachable_ips_v4 = reachable_ips_v4,
> >>> + .reachable_ips_v6 = reachable_ips_v6,
> >>> + .unreachable_ips_v4 = unreachable_ips_v4,
> >>> + .unreachable_ips_v6 = unreachable_ips_v6) :-
> >>> + port in &SwitchPort(.peer = Some{rp}),
> >>> + rp.is_enabled(),
> >>> + (var reachable_ips_v4, var reachable_ips_v6, var
> >>> unreachable_ips_v4, var unreachable_ips_v6) = get_arp_forward_ips(rp).
> >>> +
> >>> /* Packets received from VXLAN tunnels have already been through the
> >>> * router pipeline so we should skip them. Normally this is done by
> >>> the
> >>> * multicast_group implementation (VXLAN packets skip table 32 which
> >>> @@ -4172,7 +4203,7 @@ Flow(.logical_datapath = sw._uuid,
> >>> .priority = 80,
> >>> .__match = fLAGBIT_NOT_VXLAN() ++
> >>> " && arp.op == 1 && arp.tpa == { " ++
> >>> - all_ips_v4.to_vec().join(", ") ++ "}",
> >>> + ipv4.to_vec().join(", ") ++ "}",
> >>> .actions = if (sw.has_non_router_port) {
> >>> "clone {outport = ${sp.json_name};
> >>> output; }; "
> >>> "outport = ${mc_flood_l2}; output;"
> >>> @@ -4182,15 +4213,15 @@ Flow(.logical_datapath = sw._uuid,
> >>> .external_ids = stage_hint(sp.lsp._uuid)) :-
> >>> sp in &SwitchPort(.sw = sw, .peer = Some{rp}),
> >>> rp.is_enabled(),
> >>> - (var all_ips_v4, _) = get_arp_forward_ips(rp),
> >>> - not all_ips_v4.is_empty(),
> >>> + &SwitchPortARPForwards(.port = sp, .reachable_ips_v4 = ipv4),
> >>> + not ipv4.is_empty(),
> >>> var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0).
> >>> Flow(.logical_datapath = sw._uuid,
> >>> .stage = s_SWITCH_IN_L2_LKUP(),
> >>> .priority = 80,
> >>> .__match = fLAGBIT_NOT_VXLAN() ++
> >>> " && nd_ns && nd.target == { " ++
> >>> - all_ips_v6.to_vec().join(", ") ++ "}",
> >>> + ipv6.to_vec().join(", ") ++ "}",
> >>> .actions = if (sw.has_non_router_port) {
> >>> "clone {outport = ${sp.json_name};
> >>> output; }; "
> >>> "outport = ${mc_flood_l2}; output;"
> >>> @@ -4200,10 +4231,38 @@ Flow(.logical_datapath = sw._uuid,
> >>> .external_ids = stage_hint(sp.lsp._uuid)) :-
> >>> sp in &SwitchPort(.sw = sw, .peer = Some{rp}),
> >>> rp.is_enabled(),
> >>> - (_, var all_ips_v6) = get_arp_forward_ips(rp),
> >>> - not all_ips_v6.is_empty(),
> >>> + &SwitchPortARPForwards(.port = sp, .reachable_ips_v6 = ipv6),
> >>> + not ipv6.is_empty(),
> >>> var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0).
> >>>
> >>> +Flow(.logical_datapath = sw._uuid,
> >>> + .stage = s_SWITCH_IN_L2_LKUP(),
> >>> + .priority = 90,
> >>> + .__match = fLAGBIT_NOT_VXLAN() ++
> >>> + " && arp.op == 1 && arp.tpa == {" ++
> >>> + ipv4.to_vec().join(", ") ++ "}",
> >>> + .actions = "outport = ${flood}; output;",
> >>> + .external_ids = stage_hint(sp.lsp._uuid)) :-
> >>> + sp in &SwitchPort(.sw = sw, .peer = Some{rp}),
> >>> + rp.is_enabled(),
> >>> + &SwitchPortARPForwards(.port = sp, .unreachable_ips_v4 = ipv4),
> >>> + not ipv4.is_empty(),
> >>> + var flood = json_string_escape(mC_FLOOD().0).
> >>> +
> >>> +Flow(.logical_datapath = sw._uuid,
> >>> + .stage = s_SWITCH_IN_L2_LKUP(),
> >>> + .priority = 90,
> >>> + .__match = fLAGBIT_NOT_VXLAN() ++
> >>> + " && nd_ns && nd.target == {" ++
> >>> + ipv6.to_vec().join(", ") ++ "}",
> >>> + .actions = "outport = ${flood}; output;",
> >>> + .external_ids = stage_hint(sp.lsp._uuid)) :-
> >>> + sp in &SwitchPort(.sw = sw, .peer = Some{rp}),
> >>> + rp.is_enabled(),
> >>> + &SwitchPortARPForwards(.port = sp, .unreachable_ips_v6 = ipv6),
> >>> + not ipv6.is_empty(),
> >>> + var flood = json_string_escape(mC_FLOOD().0).
> >>> +
> >>> for (SwitchPortNewDynamicAddress(.port = &SwitchPort{.lsp = lsp,
> >>> .json_name = json_name, .sw = sw},
> >>> .address = Some{addrs})
> >>> if lsp.__type != "external") {
> >>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> >>> index 0c8a09ced..f591537e1 100644
> >>> --- a/tests/ovn-northd.at
> >>> +++ b/tests/ovn-northd.at
> >>> @@ -4033,3 +4033,102 @@ check ovn-nbctl --wait=sb clear
> >>> logical_router_port ro2-sw ha_chassis_group
> >>> check_lflows 0
> >>>
> >>> AT_CLEANUP
> >>> +
> >>> +OVN_FOR_EACH_NORTHD([
> >>> +AT_SETUP([ovn -- ARP flood for unreachable addresses])
> >>> +ovn_start
> >>> +
> >>> +AS_BOX([Setting up the logical network])
> >>> +
> >>> +# This network is the same as the one from "Router Address Propagation"
> >>> +check ovn-nbctl ls-add sw
> >>> +
> >>> +check ovn-nbctl lr-add ro1
> >>> +check ovn-nbctl lrp-add ro1 ro1-sw 00:00:00:00:00:01 10.0.0.1/24
> >>> +check ovn-nbctl lsp-add sw sw-ro1
> >>> +check ovn-nbctl lsp-set-type sw-ro1 router
> >>> +check ovn-nbctl lsp-set-addresses sw-ro1 router
> >>> +check ovn-nbctl lsp-set-options sw-ro1 router-port=ro1-sw
> >>> +
> >>> +check ovn-nbctl lr-add ro2
> >>> +check ovn-nbctl lrp-add ro2 ro2-sw 00:00:00:00:00:02 20.0.0.1/24
> >>> +check ovn-nbctl lsp-add sw sw-ro2
> >>> +check ovn-nbctl lsp-set-type sw-ro2 router
> >>> +check ovn-nbctl lsp-set-addresses sw-ro2 router
> >>> +check ovn-nbctl --wait=sb lsp-set-options sw-ro2 router-port=ro2-sw
> >>> +
> >>> +check ovn-nbctl ls-add ls1
> >>> +check ovn-nbctl lsp-add ls1 vm1
> >>> +check ovn-nbctl lsp-set-addresses vm1 "00:00:00:00:01:02 192.168.1.2"
> >>> +check ovn-nbctl lrp-add ro1 ro1-ls1 00:00:00:00:01:01 192.168.1.1/24
> >>> +check ovn-nbctl lsp-add ls1 ls1-ro1
> >>> +check ovn-nbctl lsp-set-type ls1-ro1 router
> >>> +check ovn-nbctl lsp-set-addresses ls1-ro1 router
> >>> +check ovn-nbctl lsp-set-options ls1-ro1 router-port=ro1-ls1
> >>> +
> >>> +check ovn-nbctl ls-add ls2
> >>> +check ovn-nbctl lsp-add ls2 vm2
> >>> +check ovn-nbctl lsp-set-addresses vm2 "00:00:00:00:02:02 192.168.2.2"
> >>> +check ovn-nbctl lrp-add ro2 ro2-ls2 00:00:00:00:02:01 192.168.2.1/24
> >>> +check ovn-nbctl lsp-add ls2 ls2-ro2
> >>> +check ovn-nbctl lsp-set-type ls2-ro2 router
> >>> +check ovn-nbctl lsp-set-addresses ls2-ro2 router
> >>> +check ovn-nbctl lsp-set-options ls2-ro2 router-port=ro2-ls2
> >>> +
> >>> +AS_BOX([Ensure that unreachable flood flows are not installed, since
> >>> no addresses are unreachable])
> >>> +
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep "ls_in_l2_lkup" | grep
> >>> "priority=90" -c], [1], [dnl
> >>> +0
> >>> +])
> >>> +
> >>> +AS_BOX([Adding some reachable NAT addresses])
> >>> +
> >>> +check ovn-nbctl lr-nat-add ro1 dnat 10.0.0.100 192.168.1.100
> >>> +check ovn-nbctl lr-nat-add ro1 snat 10.0.0.200 192.168.1.200/30
> >>> +
> >>> +check ovn-nbctl lr-nat-add ro2 dnat 20.0.0.100 192.168.2.100
> >>> +check ovn-nbctl --wait=sb lr-nat-add ro2 snat 20.0.0.200
> >>> 192.168.2.200/30
> >>> +
> >>> +AS_BOX([Ensure that unreachable flood flows are not installed, since
> >>> all addresses are reachable])
> >>> +
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep "ls_in_l2_lkup" | grep
> >>> "priority=90" -c], [1], [dnl
> >>> +0
> >>> +])
> >>> +
> >>> +AS_BOX([Adding some unreachable NAT addresses])
> >>> +
> >>> +check ovn-nbctl lr-nat-add ro1 dnat 30.0.0.100 192.168.1.130
> >>> +check ovn-nbctl lr-nat-add ro1 snat 30.0.0.200 192.168.1.148/30
> >>> +
> >>> +check ovn-nbctl lr-nat-add ro2 dnat 40.0.0.100 192.168.2.130
> >>> +check ovn-nbctl --wait=sb lr-nat-add ro2 snat 40.0.0.200
> >>> 192.168.2.148/30
> >>> +
> >>> +AS_BOX([Ensure that unreachable flood flows are installed, since
> >>> there are unreachable addresses])
> >>> +
> >>> +ovn-sbctl lflow-list
> >>> +
> >>> +# We expect two flows to be installed, one per connected router port
> >>> on sw
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep
> >>> priority=90 -c], [0], [dnl
> >>> +2
> >>> +])
> >>> +
> >>> +# We expect that the installed flows will match the unreachable DNAT
> >>> addresses only.
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep
> >>> priority=90 | grep "arp.tpa == {30.0.0.100}" -c], [0], [dnl
> >>> +1
> >>> +])
> >>> +
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep
> >>> priority=90 | grep "arp.tpa == {40.0.0.100}" -c], [0], [dnl
> >>> +1
> >>> +])
> >>> +
> >>> +# Ensure that we do not create flows for SNAT addresses
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep
> >>> priority=90 | grep "arp.tpa == {30.0.0.200}" -c], [1], [dnl
> >>> +0
> >>> +])
> >>> +
> >>> +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep
> >>> priority=90 | grep "arp.tpa == {40.0.0.200}" -c], [1], [dnl
> >>> +0
> >>> +])
> >>> +
> >>> +AT_CLEANUP
> >>> +])
> >>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> >>> index a5f10ce38..bdd13b009 100644
> >>> --- a/tests/system-ovn.at
> >>> +++ b/tests/system-ovn.at
> >>> @@ -6482,3 +6482,105 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error
> >>> receiving.*/d
> >>>
> >>> AT_CLEANUP
> >>> ])
> >>> +
> >>> +OVN_FOR_EACH_NORTHD([
> >>> +AT_SETUP([ovn -- Floating IP outside router subnet IPv4])
> >>> +AT_KEYWORDS(NAT)
> >>> +
> >>> +ovn_start
> >>> +
> >>> +OVS_TRAFFIC_VSWITCHD_START()
> >>> +ADD_BR([br-int])
> >>> +
> >>> +# Set external-ids in br-int needed for ovn-controller
> >>> +ovs-vsctl \
> >>> + -- set Open_vSwitch . external-ids:system-id=hv1 \
> >>> + -- set Open_vSwitch .
> >>> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> >>> + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> >>> + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> >>> + -- set bridge br-int fail-mode=secure
> >>> other-config:disable-in-band=true
> >>> +
> >>> +start_daemon ovn-controller
> >>> +
> >>> +# Logical network:
> >>> +# Two VMs
> >>> +# * VM1 with IP address 192.168.100.5
> >>> +# * VM2 with IP address 192.168.200.5
> >>> +#
> >>> +# VM1 connects to logical switch ls1. ls1 connects to logical router
> >>> lr1.
> >>> +# VM2 connects to logical switch ls2. ls2 connects to logical router
> >>> lr2.
> >>> +# lr1 and lr2 both connect to logical switch ls-pub.
> >>> +# * lr1's interface that connects to ls-pub has IP address
> >>> 172.18.2.110/24
> >>> +# * lr2's interface that connects to ls-pub has IP address
> >>> 172.18.1.173/24
> >>> +#
> >>> +# lr1 has the following attributes:
> >>> +# * It has a DNAT rule that translates 172.18.2.11 to
> >>> 192.168.100.5 (VM1)
> >>> +#
> >>> +# lr2 has the following attributes:
> >>> +# * It has a DNAT rule that translates 172.18.2.12 to
> >>> 192.168.200.5 (VM2)
> >>> +#
> >>> +# In this test, we want to ensure that a ping from VM1 to IP address
> >>> 172.18.2.12 reaches VM2.
> >>> +# When the NAT rules are set up, there should be MAC_Bindings
> >>> created that allow for traffic
> >>> +# to exit lr1, go through ls-pub, and reach the NAT external IP
> >>> configured on lr2.
> >>> +
> >>> +check ovn-nbctl ls-add ls1
> >>> +check ovn-nbctl lsp-add ls1 vm1 -- lsp-set-addresses vm1
> >>> "00:00:00:00:01:05 192.168.100.5"
> >>> +
> >>> +check ovn-nbctl ls-add ls2
> >>> +check ovn-nbctl lsp-add ls2 vm2 -- lsp-set-addresses vm2
> >>> "00:00:00:00:02:05 192.168.200.5"
> >>> +
> >>> +check ovn-nbctl ls-add ls-pub
> >>> +
> >>> +check ovn-nbctl lr-add lr1
> >>> +check ovn-nbctl lrp-add lr1 lr1-ls1 00:00:00:00:01:01 192.168.100.1/24
> >>> +check ovn-nbctl lsp-add ls1 ls1-lr1 \
> >>> + -- lsp-set-type ls1-lr1 router \
> >>> + -- lsp-set-addresses ls1-lr1 router \
> >>> + -- lsp-set-options ls1-lr1 router-port=lr1-ls1
> >>> +
> >>> +check ovn-nbctl lr-add lr2
> >>> +check ovn-nbctl lrp-add lr2 lr2-ls2 00:00:00:00:02:01 192.168.200.1/24
> >>> +check ovn-nbctl lsp-add ls2 ls2-lr2 \
> >>> + -- lsp-set-type ls2-lr2 router \
> >>> + -- lsp-set-addresses ls2-lr2 router \
> >>> + -- lsp-set-options ls2-lr2 router-port=lr2-ls2
> >>> +
> >>> +check ovn-nbctl lrp-add lr1 lr1-ls-pub 00:00:00:00:03:01
> >>> 172.18.2.110/24
> >>> +check ovn-nbctl lrp-set-gateway-chassis lr1-ls-pub hv1
> >>> +check ovn-nbctl lsp-add ls-pub ls-pub-lr1 \
> >>> + -- lsp-set-type ls-pub-lr1 router \
> >>> + -- lsp-set-addresses ls-pub-lr1 router \
> >>> + -- lsp-set-options ls-pub-lr1 router-port=lr1-ls-pub
> >>> +
> >>> +check ovn-nbctl lrp-add lr2 lr2-ls-pub 00:00:00:00:03:02
> >>> 172.18.1.173/24
> >>> +check ovn-nbctl lrp-set-gateway-chassis lr2-ls-pub hv1
> >>> +check ovn-nbctl lsp-add ls-pub ls-pub-lr2 \
> >>> + -- lsp-set-type ls-pub-lr2 router \
> >>> + -- lsp-set-addresses ls-pub-lr2 router \
> >>> + -- lsp-set-options ls-pub-lr2 router-port=lr2-ls-pub
> >>> +
> >>> +# Putting --add-route on these NAT rules means there is no need to
> >>> +# add any static routes.
> >>> +check ovn-nbctl --add-route lr-nat-add lr1 dnat_and_snat 172.18.2.11
> >>> 192.168.100.5 vm1 00:00:00:00:03:01
> >>> +check ovn-nbctl --add-route lr-nat-add lr2 dnat_and_snat 172.18.2.12
> >>> 192.168.200.5 vm2 00:00:00:00:03:02
> >>> +
> >>> +ADD_NAMESPACES(vm1)
> >>> +ADD_VETH(vm1, vm1, br-int, "192.168.100.5/24", "00:00:00:00:01:05", \
> >>> + "192.168.100.1")
> >>> +
> >>> +ADD_NAMESPACES(vm2)
> >>> +ADD_VETH(vm2, vm2, br-int, "192.168.200.5/24", "00:00:00:00:02:05", \
> >>> + "192.168.200.1")
> >>> +
> >>> +OVN_POPULATE_ARP
> >>> +check ovn-nbctl --wait=hv sync
> >>> +
> >>> +AS_BOX([Testing a ping])
> >>> +
> >>> +NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.12 |
> >>> FORMAT_PING], \
> >>> +[0], [dnl
> >>> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> >>> +])
> >>> +
> >>> +AT_CLEANUP
> >>> +])
> >>> --
> >>> 2.31.1
> >>>
> >>> _______________________________________________
> >>> dev mailing list
> >>> [email protected]
> >>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>
> >>
> >
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev