[ovs-dev] [PATCH ovn v8 6/6] northd: Flood ARPs to routers for "unreachable" addresses.
Krzysztof Klimonda
kklimonda at syntaxhighlighted.com
Wed Jun 23 13:00:43 UTC 2021
Hi Mark,
Would it be possible to install /32 IP on the router to avoid flooding all router ports? This can be hundreds if not thousands of ports and I think I've seen issues with flooding at that scale before. I'd have to test it in the lab though first.
Best Regards,
Krzysztof
On Thu, Jun 3, 2021, at 20:49, Mark Michelson 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 <mmichels at redhat.com>
> ---
> northd/ovn-northd.8.xml | 8 +++
> northd/ovn-northd.c | 153 +++++++++++++++++++++++++++-------------
> northd/ovn_northd.dl | 101 ++++++++++++++++++++------
> tests/ovn-northd.at | 99 ++++++++++++++++++++++++++
> tests/system-ovn.at | 102 +++++++++++++++++++++++++++
> 5 files changed, 391 insertions(+), 72 deletions(-)
>
> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> index bb77689de..8bb77bf6c 100644
> --- a/northd/ovn-northd.8.xml
> +++ b/northd/ovn-northd.8.xml
> @@ -1549,6 +1549,14 @@ output;
> logical ports.
> </li>
>
> + <li>
> + Priority-80 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 414bf9c48..eacbab96a 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -6539,44 +6539,48 @@
> 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 sset *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)
> +arp_nd_ns_match(struct sset *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 == {");
> }
>
> const char *ip_address;
> SSET_FOR_EACH (ip_address, ips) {
> - ds_put_format(&match, "%s, ", ip_address);
> + ds_put_format(match, "%s, ", ip_address);
> }
>
> - ds_chomp(&match, ' ');
> - ds_chomp(&match, ',');
> - ds_put_cstr(&match, "}");
> + ds_chomp(match, ' ');
> + ds_chomp(match, ',');
> + 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 sset *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.
> @@ -6599,6 +6603,30 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
> 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 sset *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_unique_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.
> @@ -6625,39 +6653,48 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
> * router port.
> * Priority: 80.
> */
> - struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
> - struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
> + struct sset lb_ips_v4 = SSET_INITIALIZER(&lb_ips_v4);
> + struct sset lb_ips_v6 = SSET_INITIALIZER(&lb_ips_v6);
>
> - get_router_load_balancer_ips(op->od, false, &all_ips_v4, &all_ips_v6);
> + get_router_load_balancer_ips(op->od, false, &lb_ips_v4, &lb_ips_v6);
> +
> + struct sset reachable_ips_v4 = SSET_INITIALIZER(&reachable_ips_v4);
> + struct sset reachable_ips_v6 = SSET_INITIALIZER(&reachable_ips_v6);
> + struct sset unreachable_ips_v4 = SSET_INITIALIZER(&unreachable_ips_v4);
> + struct sset unreachable_ips_v6 = SSET_INITIALIZER(&unreachable_ips_v6);
>
> const char *ip_addr;
> const char *ip_addr_next;
> - SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v4) {
> + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &lb_ips_v4) {
> ovs_be32 ipv4_addr;
>
> /* 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)) {
> - continue;
> + if (ip_parse(ip_addr, &ipv4_addr)) {
> + if (lrouter_port_ipv4_reachable(op, ipv4_addr)) {
> + sset_add(&reachable_ips_v4, ip_addr);
> + } else {
> + sset_add(&unreachable_ips_v4, ip_addr);
> + }
> }
> -
> - sset_delete(&all_ips_v4, SSET_NODE_FROM_NAME(ip_addr));
> }
> - SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v6) {
> + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &lb_ips_v6) {
> struct in6_addr ipv6_addr;
>
> /* 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)) {
> - continue;
> + if (ipv6_parse(ip_addr, &ipv6_addr)) {
> + if (lrouter_port_ipv6_reachable(op, &ipv6_addr)) {
> + sset_add(&reachable_ips_v6, ip_addr);
> + } else {
> + sset_add(&unreachable_ips_v6, ip_addr);
> + }
> }
> -
> - sset_delete(&all_ips_v6, SSET_NODE_FROM_NAME(ip_addr));
> }
> + sset_destroy(&lb_ips_v4);
> + sset_destroy(&lb_ips_v6);
>
> for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> @@ -6678,37 +6715,53 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
> struct in6_addr *addr = &nat_entry->ext_addrs.ipv6_addrs[0].addr;
>
> if (lrouter_port_ipv6_reachable(op, addr)) {
> - sset_add(&all_ips_v6, nat->external_ip);
> + sset_add(&reachable_ips_v6, nat->external_ip);
> + } else {
> + sset_add(&unreachable_ips_v6, nat->external_ip);
> }
> } else {
> ovs_be32 addr = nat_entry->ext_addrs.ipv4_addrs[0].addr;
>
> if (lrouter_port_ipv4_reachable(op, addr)) {
> - sset_add(&all_ips_v4, nat->external_ip);
> + sset_add(&reachable_ips_v4, nat->external_ip);
> + } else {
> + sset_add(&unreachable_ips_v4, nat->external_ip);
> }
> }
> }
>
> for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> - sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s);
> + sset_add(&reachable_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s);
> }
> for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> - sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s);
> + sset_add(&reachable_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s);
> }
>
> - if (!sset_is_empty(&all_ips_v4)) {
> - build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
> - sw_od, 80, lflows,
> - stage_hint);
> + if (!sset_is_empty(&reachable_ips_v4)) {
> + build_lswitch_rport_arp_req_flow_for_reachable_ip(&reachable_ips_v4,
> + AF_INET, sw_op,
> + sw_od, 80, lflows,
> + stage_hint);
> + }
> + if (!sset_is_empty(&reachable_ips_v6)) {
> + build_lswitch_rport_arp_req_flow_for_reachable_ip(&reachable_ips_v6,
> + AF_INET6, sw_op,
> + sw_od, 80, lflows,
> + stage_hint);
> }
> - if (!sset_is_empty(&all_ips_v6)) {
> - build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
> - sw_od, 80, lflows,
> - stage_hint);
> + if (!sset_is_empty(&unreachable_ips_v4)) {
> + build_lswitch_rport_arp_req_flow_for_unreachable_ip(
> + &unreachable_ips_v4, AF_INET, sw_od, 90, lflows, stage_hint);
> + }
> + if (!sset_is_empty(&unreachable_ips_v6)) {
> + build_lswitch_rport_arp_req_flow_for_unreachable_ip(
> + &unreachable_ips_v6, AF_INET6, sw_od, 90, lflows, stage_hint);
> }
>
> - sset_destroy(&all_ips_v4);
> - sset_destroy(&all_ips_v6);
> + sset_destroy(&reachable_ips_v4);
> + sset_destroy(&reachable_ips_v6);
> + sset_destroy(&unreachable_ips_v4);
> + sset_destroy(&unreachable_ips_v6);
>
> /* Self originated ARP requests/ND need to be flooded as usual.
> *
> diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
> index 794b86ee1..1061fae8b 100644
> --- a/northd/ovn_northd.dl
> +++ b/northd/ovn_northd.dl
> @@ -4103,9 +4103,13 @@ UniqueFlow[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);
> @@ -4115,7 +4119,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)
> },
> _ -> ()
> }
> @@ -4126,7 +4132,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)
> },
> _ -> ()
> }
> @@ -4139,22 +4147,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
> @@ -4165,8 +4196,8 @@ AnnotatedFlow(.f = Flow{.logical_datapath =
> sw._uuid,
> .stage = s_SWITCH_IN_L2_LKUP(),
> .priority = 80,
> .__match = fLAGBIT_NOT_VXLAN() ++
> - " && arp.op == 1 &&
> arp.tpa == { " ++
> -
> all_ips_v4.to_vec().join(", ") ++ "}",
> + " && arp.op == 1 &&
> arp.tpa == {" ++
> + ipv4.to_vec().join(", ")
> ++ "}",
> .actions = if
> (sw.has_non_router_port) {
> "clone {outport =
> ${sp.json_name}; output; }; "
> "outport =
> ${mc_flood_l2}; output;"
> @@ -4175,17 +4206,16 @@ AnnotatedFlow(.f = Flow{.logical_datapath =
> sw._uuid,
> },
> .external_ids = stage_hint(sp.lsp._uuid)},
> .shared = not sw.has_non_router_port) :-
> - 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(),
> + sp in &SwitchPort(.sw = sw),
> + &SwitchPortARPForwards(.port = sp, .reachable_ips_v4 = ipv4),
> + not ipv4.is_empty(),
> var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0).
> AnnotatedFlow(.f = 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(", ") ++ "}",
> + " && nd_ns && nd.target ==
> {" ++
> + ipv6.to_vec().join(", ")
> ++ "}",
> .actions = if
> (sw.has_non_router_port) {
> "clone {outport =
> ${sp.json_name}; output; }; "
> "outport =
> ${mc_flood_l2}; output;"
> @@ -4194,12 +4224,39 @@ AnnotatedFlow(.f = Flow{.logical_datapath =
> sw._uuid,
> },
> .external_ids = stage_hint(sp.lsp._uuid)},
> .shared = not sw.has_non_router_port) :-
> - 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(),
> + sp in &SwitchPort(.sw = sw),
> + &SwitchPortARPForwards(.port = sp, .reachable_ips_v6 = ipv6),
> + not ipv6.is_empty(),
> var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0).
>
> +AnnotatedFlow(.f = 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)},
> + .shared = not sw.has_non_router_port) :-
> + sp in &SwitchPort(.sw = sw),
> + &SwitchPortARPForwards(.port = sp, .unreachable_ips_v4 = ipv4),
> + not ipv4.is_empty(),
> + var flood = json_string_escape(mC_FLOOD().0).
> +
> +AnnotatedFlow(.f = 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)},
> + .shared = not sw.has_non_router_port) :-
> + sp in &SwitchPort(.sw = sw),
> + &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 9e4f2ca62..13ead49ba 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -3840,3 +3840,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 3824788c4..6539a66cb 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -6293,3 +6293,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
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
--
Krzysztof Klimonda
kklimonda at syntaxhighlighted.com
More information about the dev
mailing list