When a logical switch has a localnet port and the switch is attached to a distributed gateway port (DGP), northd restricts the handling of ARP requests to the chassis the DGP is resident on. That restriction is currently expressed on the router side, move the is_chassis_resident() check up to the logical switch level, so that it is applied only to the traffic that needs it, i.e. the traffic entering the switch through a localnet port.
Terminology used below: a switch-router peering is "centralized" when the switch has no localnet port and only one dgp port. In that case a chassisredirect port is created on the switch port and routing is done on the gateway chassis only. Otherwise the peering is "distributed" and routing happens on every chassis. Details: - For the IP addresses owned by the switch, the check already exists in build_lswitch_arp_nd_local_resp_match(): when the packet comes from a localnet port it is processed on the HA chassis only, otherwise it is allowed on all chassis. ARP and ND behave identically here. - For distributed peerings, apply the same chassis-residency condition to the router owned addresses in build_lswitch_rport_arp_req_flow(). The expectation matches the router side: requests for router and NAT addresses have to arrive on the active gateway chassis of the corresponding NAT gateway port, and requests for a distributed NAT address have to arrive on the chassis hosting the logical port that owns the private IP. All the remaining ARP requests are expected to be processed on the HA chassis only, so a priority-75 flow is added to drop them on the other chassis. - On the router side, drop the is_chassis_resident() check from the ARP flows of the lookup_neighbor stage, so that MAC bindings are learned no matter which chassis received the request. The rest of the router side ARP reply logic still relies on is_chassis_resident(), which matters when a switch is connected to more than one router. - The check for VIF port reachability in a switch with an external network was removed from its old test and folded into a new test that covers all of the cases above. Signed-off-by: Alexandra Rukomoinikova <[email protected]> --- v3: changed default drop priority for arp request on non ha chassis to 73 from 75, to allow self-origineted request with prio 75 --- northd/en-lr-nat.c | 2 +- northd/northd.c | 444 ++++++++++++++++++++++++---------- northd/northd.h | 15 ++ tests/ovn-northd.at | 327 ++++++++++++++++++++++--- tests/ovn.at | 139 +++++++++++ tests/system-common-macros.at | 3 + tests/system-ovn.at | 184 +++++++++++--- 7 files changed, 909 insertions(+), 205 deletions(-) diff --git a/northd/en-lr-nat.c b/northd/en-lr-nat.c index f0596d254..f621f26fd 100644 --- a/northd/en-lr-nat.c +++ b/northd/en-lr-nat.c @@ -354,7 +354,7 @@ lr_nat_entry_set_dgw_port(const struct ovn_datapath *od, * on the gateway chassis for the DGP's networks/subnets.) */ struct ovn_port *l3dgw_port = nat_entry->l3dgw_port; - if (l3dgw_port && l3dgw_port->peer && l3dgw_port->peer->cr_port) { + if (l3dgw_port && lrp_has_centralized_routing(l3dgw_port)) { return true; } diff --git a/northd/northd.c b/northd/northd.c index 9f33b50cf..60e8981bd 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -9913,132 +9913,254 @@ lrouter_port_ipv6_reachable(const struct ovn_port *op, return false; } -/* - * Ingress table 30: 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 inline void +build_lswitch_rport_arp_forward_action(const char *port_name, + bool has_non_router_ports, + struct ds *actions) +{ + if (has_non_router_ports) { + ds_put_format(actions, "clone {outport = %s; output; }; " + "outport = \""MC_UNKNOWN"\"; output;", + port_name); + } else { + ds_put_format(actions, "outport = %s; output;", port_name); + } +} + +static inline void +build_distributed_routing_arp_restrictions( + const struct ovn_port *op, + const char *nat_port, + struct ds *match) +{ + const char *crp_key = nat_port + ? nat_port + : op->peer->cr_port->json_key; + ds_put_format(match, + " && " LOCALNET_CHASSIS_RESIDENT_MATCH, + crp_key); +} + +static void +build_lswitch_rport_garp_flow(const char *arp_ip, + const struct ovn_port *switch_op, + const char *nat_port, + bool distributed_routing, + struct ds *arp_nd_base_match, + struct lflow_table *lflows, + struct lflow_ref *lflow_ref) +{ + struct ds match = DS_EMPTY_INITIALIZER; + + ds_clone(&match, arp_nd_base_match); + ds_put_format(&match, " && arp.spa == %s", arp_ip); + if (distributed_routing) { + build_distributed_routing_arp_restrictions(switch_op, + nat_port, + &match); + } + ovn_lflow_add(lflows, switch_op->od, S_SWITCH_IN_L2_LKUP, 90, + ds_cstr(&match), + "outport = \""MC_FLOOD_L2"\"; output;", + lflow_ref, WITH_HINT(&switch_op->nbsp->header_)); + + ds_destroy(&match); +} + static void -build_lswitch_rport_arp_req_flow( - const char *ips, int addr_family, struct ovn_port *patch_op, - const struct ovn_datapath *od, uint32_t priority, - struct lflow_table *lflows, const struct ovsdb_idl_row *stage_hint, +build_lswitch_rport_arp_req_flow_centralized_routing( + struct ovn_port *op, bool has_non_router_ports, + struct ds *arp_nd_base_match, struct lflow_table *lflows, struct lflow_ref *lflow_ref) { - struct ds match = DS_EMPTY_INITIALIZER; - struct ds m = DS_EMPTY_INITIALIZER; - struct ds m_garp = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; + struct ds match = DS_EMPTY_INITIALIZER; - arp_nd_ns_match(ips, addr_family, &m); - ds_clone(&match, &m); + ds_put_format(&match, "%s && is_chassis_resident(%s)", + ds_cstr(arp_nd_base_match), op->cr_port->json_key); + build_lswitch_rport_arp_forward_action(op->json_key, + has_non_router_ports, + &actions); + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 80, + ds_cstr(&match), ds_cstr(&actions), lflow_ref, + WITH_HINT(&op->nbsp->header_)); + ds_clear(&match); + ds_put_format(&match, "%s && !is_chassis_resident(%s)", + ds_cstr(arp_nd_base_match), op->cr_port->json_key); + ds_clear(&actions); + build_lswitch_rport_arp_forward_action(op->cr_port->json_key, + has_non_router_ports, + &actions); + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 80, + ds_cstr(&match), ds_cstr(&actions), lflow_ref, + WITH_HINT(&op->nbsp->header_)); - bool has_cr_port = patch_op->cr_port; + ds_destroy(&match); + ds_destroy(&actions); +} - /* If the patch_op has a chassis resident port, it means - * - its peer is a distributed gateway port (DGP) and - * - routing is centralized for the DGP's networks on - * the configured gateway chassis. - * - * If that's the case, make sure that the packets destined to - * the DGP's MAC are sent to the chassis where the DGP resides. - * */ +static void +build_lswitch_rport_arp_req_flow_distributed_routing( + struct ovn_port *op, const char *nat_port, + bool has_non_router_ports, struct ds *arp_nd_base_match, + struct lflow_table *lflows, struct lflow_ref *lflow_ref) +{ + struct ds actions = DS_EMPTY_INITIALIZER; + struct ds match = DS_EMPTY_INITIALIZER; - if (has_cr_port) { - ds_put_format(&match, " && is_chassis_resident(%s)", - patch_op->cr_port->json_key); - } + ds_clone(&match, arp_nd_base_match); + build_distributed_routing_arp_restrictions(op, nat_port, &match); + build_lswitch_rport_arp_forward_action(op->json_key, + has_non_router_ports, + &actions); + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 80, + ds_cstr(&match), ds_cstr(&actions), lflow_ref, + WITH_HINT(&op->nbsp->header_)); - if (addr_family == AF_INET) { - ds_clone(&m_garp, &m); - ds_put_format(&m_garp, " && arp.spa == %s", ips); - } + ds_destroy(&match); + ds_destroy(&actions); +} - /* Send a the packet to the router pipeline. If the switch has non-router - * ports then flood it there as well. - */ - if (vector_len(&od->router_ports) != od->nbs->n_ports) { - ds_put_format(&actions, "clone {outport = %s; output; }; " - "outport = \""MC_UNKNOWN"\"; output;", - patch_op->json_key); - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), ds_cstr(&actions), lflow_ref, - WITH_HINT(stage_hint)); - if (addr_family == AF_INET) { - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority + 10, - ds_cstr(&m_garp), - "outport = \""MC_FLOOD_L2"\"; output;", - lflow_ref, WITH_HINT(stage_hint)); - } - } else { - ds_put_format(&actions, "outport = %s; output;", patch_op->json_key); - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), ds_cstr(&actions), lflow_ref, - WITH_HINT(stage_hint)); - } +static void +build_lswitch_rport_arp_req_flow_default_routing( + struct ovn_port *op, bool switch_non_router_ports, + struct ds *arp_nd_base_match, struct lflow_table *lflows, + struct lflow_ref *lflow_ref) +{ + struct ds actions = DS_EMPTY_INITIALIZER; + struct ds match = DS_EMPTY_INITIALIZER; - if (has_cr_port) { - ds_clear(&match); - ds_put_format(&match, "%s && !is_chassis_resident(%s)", ds_cstr(&m), - patch_op->cr_port->json_key); - ds_clear(&actions); - if (vector_len(&od->router_ports) != od->nbs->n_ports) { - ds_put_format(&actions, "clone {outport = %s; output; }; " - "outport = \""MC_UNKNOWN"\"; output;", - patch_op->cr_port->json_key); - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), ds_cstr(&actions), lflow_ref, - WITH_HINT(stage_hint)); - } else { - ds_put_format(&actions, "outport = %s; output;", - patch_op->cr_port->json_key); - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), ds_cstr(&actions), lflow_ref, - WITH_HINT(stage_hint)); - } - } + ds_put_format(&match, "%s", ds_cstr(arp_nd_base_match)); + build_lswitch_rport_arp_forward_action(op->json_key, + switch_non_router_ports, + &actions); + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 80, + ds_cstr(&match), ds_cstr(&actions), lflow_ref, + WITH_HINT(&op->nbsp->header_)); - ds_destroy(&m); ds_destroy(&match); - ds_destroy(&m_garp); ds_destroy(&actions); } /* - * Ingress table 30: Flows that forward ARP/ND requests only to the routers - * that own the addresses. - * Priorities: - * - 80: self originated GARPs that need to follow regular processing. - * - 75: ARP requests to router owned IPs (interface IP/LB/NAT). + * Ingress table IN_L2_LKUP: + * + * This table contains flows that forward ARP/ND requests only to the + * router instances that own the corresponding addresses. + * + * Other ARP/ND packets are still flooded within the switching domain + * as regular broadcast traffic, subject to the rules described below. + * + * If a switch has physical ports (localnet/l2gateway) and a router port + * is associated with a chassisredirect (cr-port), routing is no longer + * strictly centralized on a single router chassis. + * Instead, routing may occur on all chassis, subject to the following rules: + * + * 1) Packets from localnet ports addressed to router-owned IPs: + * ARP/ND requests are allowed only on the HA (gateway) chassis. + * + * 2) Packets from localnet ports addressed to switch ports: + * ARP/ND requests are allowed only on the chassis where the target + * logical port resides. + * + * (Implemented in build_lswitch_arp_nd_local_resp_match.) + * + * 3) Packets addressed to a distributed NAT IP: + * ARP/ND requests are allowed only on the chassis where the logical + * port owning the corresponding private IP is bound. + * + * 4) No restrictions are imposed on internal ARP/ND traffic. + * + * 5) All other packets are dropped. For arp packets - they are dropped + * in switch pipeline, for ND packets - they are dropped on router. + * + * ---------------------------------------------------------------------- + * + * If the switch has no physical (localnet/l2gateway) ports, routing is + * centralized on the HA chassis: + * + * 1) Packets arriving on the HA chassis are delivered directly to the + * router port and flooded to unknown multicast group. + * + * 2) Packets arriving on non-HA chassis are forwarded to the HA chassis + * via the chassisredirect (cr-port) and unknown multicast group. + * + * ---------------------------------------------------------------------- + * + * In the default routing mode (when no router port has a cr-port), + * packets are simply forwarded directly to the router port and + * flooded to unknown multicast group. */ static void -build_lswitch_rport_arp_req_flows(struct ovn_port *op, - struct ovn_datapath *sw_od, - struct ovn_port *sw_op, - struct lflow_table *lflows, - const struct ovsdb_idl_row *stage_hint) +build_lswitch_rport_arp_req_flow(const char *arp_ip, + int addr_family, + struct ovn_port *switch_op, + const char *nat_port, + struct lflow_table *lflows, + struct lflow_ref *lflow_ref) { - if (!op || !op->nbrp) { + struct ds arp_nd_base_match = DS_EMPTY_INITIALIZER; + bool peer_has_cr_port = switch_op->peer && switch_op->peer->cr_port; + bool is_centralized_routing = + lsp_has_centralized_routing(switch_op); + bool distributed_routing = peer_has_cr_port && + !is_centralized_routing && + addr_family == AF_INET; + bool switch_non_router_ports = vector_len(&switch_op->od->router_ports) + != switch_op->od->nbs->n_ports; + + arp_nd_ns_match(arp_ip, addr_family, &arp_nd_base_match); + + if (is_centralized_routing) { + build_lswitch_rport_arp_req_flow_centralized_routing( + switch_op, switch_non_router_ports, &arp_nd_base_match, + lflows, lflow_ref); + } else if (distributed_routing) { + build_lswitch_rport_arp_req_flow_distributed_routing( + switch_op, nat_port, switch_non_router_ports, + &arp_nd_base_match, lflows, lflow_ref); + } else { + build_lswitch_rport_arp_req_flow_default_routing( + switch_op, switch_non_router_ports, &arp_nd_base_match, + lflows, lflow_ref); + } + + /* For IPv4, also add a GARP flow at higher priority so that + * self-originated GARPs are flooded to the entire L2 domain. + * Only needed when there are non-router ports. + */ + if (addr_family == AF_INET && switch_non_router_ports) { + build_lswitch_rport_garp_flow(arp_ip, switch_op, nat_port, + distributed_routing, + &arp_nd_base_match, + lflows, lflow_ref); + } + + ds_destroy(&arp_nd_base_match); +} + +static void +build_lswitch_rport_arp_req_flows(struct ovn_port *router_op, + struct ovn_port *switch_op, + struct lflow_table *lflows) +{ + if (!router_op || !router_op->nbrp) { return; } - if (!lrport_is_enabled(op->nbrp)) { + if (!lrport_is_enabled(router_op->nbrp)) { return; } - /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to this - * router port. - * Priority: 80. - */ - for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + for (size_t i = 0; i < router_op->lrp_networks.n_ipv4_addrs; i++) { build_lswitch_rport_arp_req_flow( - op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od, 80, - lflows, stage_hint, sw_op->lflow_ref); + router_op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, switch_op, + NULL, lflows, switch_op->lflow_ref); } - for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { + for (size_t i = 0; i < router_op->lrp_networks.n_ipv6_addrs; i++) { build_lswitch_rport_arp_req_flow( - op->lrp_networks.ipv6_addrs[i].addr_s, AF_INET6, sw_op, sw_od, 80, - lflows, stage_hint, sw_op->lflow_ref); + router_op->lrp_networks.ipv6_addrs[i].addr_s, AF_INET6, switch_op, + NULL, lflows, switch_op->lflow_ref); } } @@ -10053,8 +10175,7 @@ static void build_lswitch_rport_arp_req_flows_for_lbnats( struct ovn_port *op, const struct lr_stateful_record *lr_stateful_rec, const struct ovn_datapath *sw_od, struct ovn_port *sw_op, - struct lflow_table *lflows, const struct ovsdb_idl_row *stage_hint, - struct lflow_ref *lflow_ref) + struct lflow_table *lflows, struct lflow_ref *lflow_ref) { if (!op || !op->nbrp) { return; @@ -10081,9 +10202,8 @@ build_lswitch_rport_arp_req_flows_for_lbnats( */ if (ip_parse(ip_addr, &ipv4_addr) && lrouter_port_ipv4_reachable(op, ipv4_addr)) { - build_lswitch_rport_arp_req_flow( - ip_addr, AF_INET, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(ip_addr, AF_INET, sw_op, + NULL, lflows, lflow_ref); } } SSET_FOR_EACH (ip_addr, &lr_stateful_rec->lb_ips->ips_v6_reachable) { @@ -10094,9 +10214,8 @@ build_lswitch_rport_arp_req_flows_for_lbnats( */ if (ipv6_parse(ip_addr, &ipv6_addr) && lrouter_port_ipv6_reachable(op, &ipv6_addr)) { - build_lswitch_rport_arp_req_flow( - ip_addr, AF_INET6, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(ip_addr, AF_INET6, sw_op, + NULL, lflows, lflow_ref); } } } @@ -10131,24 +10250,38 @@ build_lswitch_rport_arp_req_flows_for_lbnats( continue; } + struct ds json_key = DS_EMPTY_INITIALIZER; + char *nat_logical_port; + if (nat_entry->is_distributed) { + json_string_escape(nat->logical_port, &json_key); + nat_logical_port = ds_steal_cstr(&json_key); + } else if (nat_entry->l3dgw_port) { + nat_logical_port = nat_entry->l3dgw_port->cr_port->json_key; + } else { + nat_logical_port = NULL; + } + /* Check if the ovn port has a network configured on which we could * expect ARP requests/NS for the DNAT external_ip. */ if (nat_entry_is_v6(nat_entry)) { if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v6_reachable, nat->external_ip)) { - build_lswitch_rport_arp_req_flow( - nat->external_ip, AF_INET6, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(nat->external_ip, AF_INET6, + sw_op, nat_logical_port, + lflows, lflow_ref); } } else { if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v4_reachable, nat->external_ip)) { - build_lswitch_rport_arp_req_flow( - nat->external_ip, AF_INET, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(nat->external_ip, AF_INET, + sw_op, nat_logical_port, + lflows, lflow_ref); } } + if (nat_entry->is_distributed) { + free(nat_logical_port); + } } struct shash_node *snat_snode; @@ -10175,22 +10308,25 @@ build_lswitch_rport_arp_req_flows_for_lbnats( continue; } + char *nat_logical_port = nat_entry->l3dgw_port ? + nat_entry->l3dgw_port->cr_port->json_key : + NULL; /* Check if the ovn port has a network configured on which we could * expect ARP requests/NS for the SNAT external_ip. */ if (nat_entry_is_v6(nat_entry)) { if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v6_reachable, nat->external_ip)) { - build_lswitch_rport_arp_req_flow( - nat->external_ip, AF_INET6, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(nat->external_ip, AF_INET6, + sw_op, nat_logical_port, + lflows, lflow_ref); } } else { if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v4_reachable, nat->external_ip)) { - build_lswitch_rport_arp_req_flow( - nat->external_ip, AF_INET, sw_op, sw_od, 80, lflows, - stage_hint, lflow_ref); + build_lswitch_rport_arp_req_flow(nat->external_ip, AF_INET, + sw_op, nat_logical_port, + lflows, lflow_ref); } } } @@ -10592,6 +10728,58 @@ build_lswitch_lflows_l2_unknown(struct ovn_datapath *od, "output;", lflow_ref); } +static void +build_lswitch_arp_restrictions_default_flows(struct ovn_datapath *od, + struct lflow_table *lflows, + struct lflow_ref *lflow_ref) +{ + struct sset ha_ports = SSET_INITIALIZER(&ha_ports); + + struct ovn_port *switch_op; + VECTOR_FOR_EACH (&od->router_ports, switch_op) { + if (lsp_has_centralized_routing(switch_op)) { + continue; + } + + if (switch_op->peer->cr_port) { + sset_add(&ha_ports, switch_op->peer->cr_port->json_key); + } + } + + if (sset_is_empty(&ha_ports)) { + sset_destroy(&ha_ports); + return; + } + + struct ds match = DS_EMPTY_INITIALIZER; + ds_put_cstr(&match, "flags.localnet == 1 && arp.op == 1 && "); + + size_t n_ports = sset_count(&ha_ports); + if (n_ports > 1) { + ds_put_cstr(&match, "("); + } + + const char *cr_port; + bool first = true; + SSET_FOR_EACH (cr_port, &ha_ports) { + if (!first) { + ds_put_cstr(&match, " && "); + } + ds_put_format(&match, "!is_chassis_resident(%s)", cr_port); + first = false; + } + + if (n_ports > 1) { + ds_put_cstr(&match, ")"); + } + + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 73, + ds_cstr(&match), "drop;", lflow_ref); + + ds_destroy(&match); + sset_destroy(&ha_ports); +} + /* Build LB, ACL, QOS related flows for logical switch pipeline stages: * - Ingress: tables 5-12, 17-19, 23 * - Egress: tables 2-8, 10-11 @@ -10728,9 +10916,8 @@ build_lswitch_arp_nd_local_resp_match(struct ds *match, return; } - ds_put_format(match, - " && ((flags.localnet == 1 && is_chassis_resident(%s))" - " || flags.localnet == 0)", op->json_key); + ds_put_format(match, " && " LOCALNET_CHASSIS_RESIDENT_MATCH, + op->json_key); } /* Ingress table 24: ARP/ND responder, reply for known IPs. @@ -11692,8 +11879,7 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op, * broadcast flooding of ARP/ND requests in table 22. We direct the * requests only to the router port that owns the IP address. */ - build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows, - &op->nbsp->header_); + build_lswitch_rport_arp_req_flows(op->peer, op, lflows); ds_clear(match); if (!eth_addr_is_zero(op->proxy_arp_addrs.ea)) { @@ -14498,7 +14684,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, return; } - if (op->peer && op->peer->cr_port) { + if (lrp_has_centralized_routing(op)) { /* We don't add the below flows if the router port's peer has * a chassisredirect port. That's because routing is centralized on * the gateway chassis for the router port networks/subnets. @@ -15270,10 +15456,6 @@ build_neigh_learning_flows_for_lrouter_port( op->lrp_networks.ipv4_addrs[i].network_s, op->lrp_networks.ipv4_addrs[i].plen, op->lrp_networks.ipv4_addrs[i].addr_s); - if (lrp_is_l3dgw(op)) { - ds_put_format(match, " && is_chassis_resident(%s)", - op->cr_port->json_key); - } const char *actions_s = REGBIT_LOOKUP_NEIGHBOR_RESULT " = lookup_arp(inport, arp.spa, arp.sha); " REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1;" @@ -15288,10 +15470,6 @@ build_neigh_learning_flows_for_lrouter_port( op->json_key, op->lrp_networks.ipv4_addrs[i].network_s, op->lrp_networks.ipv4_addrs[i].plen); - if (lrp_is_l3dgw(op)) { - ds_put_format(match, " && is_chassis_resident(%s)", - op->cr_port->json_key); - } ds_clear(actions); ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT " = lookup_arp(inport, arp.spa, arp.sha); %snext;", @@ -19172,8 +19350,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp, ovs_assert(lsp->nbsp); ovs_assert(lsp->peer); build_lswitch_rport_arp_req_flows_for_lbnats( - lsp->peer, lr_stateful_rec, lsp->od, lsp, - lflows, &lsp->nbsp->header_, lflow_ref); + lsp->peer, lr_stateful_rec, lsp->od, lsp, lflows, lflow_ref); build_lswitch_ip_unicast_lookup_for_nats(lsp, lr_stateful_rec, lflows, match, actions, lflow_ref); } @@ -20276,6 +20453,7 @@ build_lswitch_and_lrouter_iterate_by_ls(struct ovn_datapath *od, build_lswitch_lflows_l2_unknown(od, lsi->lflows, NULL); } build_mcast_flood_lswitch(od, lsi->lflows, &lsi->actions, NULL); + build_lswitch_arp_restrictions_default_flows(od, lsi->lflows, NULL); } /* Helper function to combine all lflow generation which is iterated by diff --git a/northd/northd.h b/northd/northd.h index 2e3a9e00d..2f5ea9acd 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -398,6 +398,9 @@ enum dynamic_routing_redistribute_mode { DRR_MODES #undef DRR_MODE +#define LOCALNET_CHASSIS_RESIDENT_MATCH \ + "((flags.localnet == 1 && is_chassis_resident(%s)) || flags.localnet == 0)" + /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or * sb->header_.uuid. */ struct ovn_datapath { @@ -1213,6 +1216,18 @@ od_is_centralized(const struct ovn_datapath *od) return !od->is_distributed; } +static inline bool +lrp_has_centralized_routing(struct ovn_port *router_op) +{ + return router_op->peer && router_op->peer->cr_port; +} + +static inline bool +lsp_has_centralized_routing(struct ovn_port *switch_op) +{ + return switch_op->cr_port; +} + struct ovn_port *ovn_port_find(const struct hmap *ports, const char *name); void build_igmp_lflows(struct hmap *igmp_groups, diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 2b9e95d7f..70c10baa5 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -5949,7 +5949,7 @@ check ovn-nbctl --wait=sb sync ovn-sbctl lflow-list sw > ls1_lflows AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | grep 10.0.10.100 | ovn_strip_lflows], [0], [dnl - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.10.100), action=(outport = "sw-ro2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.10.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-lrp2")) || flags.localnet == 0)), action=(outport = "sw-ro2"; output;) ]) OVN_CLEANUP_NORTHD @@ -6211,20 +6211,21 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | ovn_strip_lflows], [0], [dnl table=??(ls_in_l2_lkup ), priority=71 , match=(eth.mcast && arp), action=(outport = "_MC_flood"; output;) table=??(ls_in_l2_lkup ), priority=71 , match=(eth.mcast && ip), action=(outport = "_MC_flood_l2"; output;) table=??(ls_in_l2_lkup ), priority=72 , match=(eth.mcast && (nd_na || nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;) + table=??(ls_in_l2_lkup ), priority=73 , match=(flags.localnet == 1 && arp.op == 1 && !is_chassis_resident("cr-ro1-ls1")), action=(drop;) table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.200 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.200 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.100 && arp.spa == 10.0.0.100), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.200 && arp.spa == 10.0.0.200), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.100 && arp.spa == 192.168.1.100), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.100 && arp.spa == 30.0.0.100), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.200 && arp.spa == 30.0.0.200), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.100 && arp.spa == 10.0.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.0.0.200 && arp.spa == 10.0.0.200 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.100 && arp.spa == 192.168.1.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.100 && arp.spa == 30.0.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 30.0.0.200 && arp.spa == 30.0.0.200 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) ]) @@ -6237,8 +6238,8 @@ check ovn-nbctl --wait=sb lr-nat-add ro1 dnat_and_snat 192.168.4.100 192.168.1.1 ovn-sbctl lflow-list ls1 > ls1_lflows AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | grep "192.168.4.100" | ovn_strip_lflows], [0], [dnl - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.4.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.4.100 && arp.spa == 192.168.4.100), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.4.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.4.100 && arp.spa == 192.168.4.100 && ((flags.localnet == 1 && is_chassis_resident("cr-ro1-ls1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) ]) OVN_CLEANUP_NORTHD @@ -7716,9 +7717,6 @@ AT_CHECK([grep lr_in_admission lrflows | grep cr-DR | ovn_strip_lflows], [0], [d ]) # Check the flows in lr_in_lookup_neighbor stage AT_CHECK([grep lr_in_lookup_neighbor lrflows | grep cr-DR | ovn_strip_lflows], [0], [dnl - table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "DR-S1" && arp.spa == 172.16.1.0/24 && arp.op == 1 && is_chassis_resident("cr-DR-S1")), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) - table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "DR-S2" && arp.spa == 172.16.2.0/24 && arp.op == 1 && is_chassis_resident("cr-DR-S2")), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) - table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "DR-S3" && arp.spa == 172.16.3.0/24 && arp.op == 1 && is_chassis_resident("cr-DR-S3")), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) table=??(lr_in_lookup_neighbor), priority=120 , match=(inport == "DR-S1" && (nd_na || nd_ns) && eth.mcast && !is_chassis_resident("cr-DR-S1")), action=(reg9[[2]] = 1; next;) table=??(lr_in_lookup_neighbor), priority=120 , match=(inport == "DR-S2" && (nd_na || nd_ns) && eth.mcast && !is_chassis_resident("cr-DR-S2")), action=(reg9[[2]] = 1; next;) table=??(lr_in_lookup_neighbor), priority=120 , match=(inport == "DR-S3" && (nd_na || nd_ns) && eth.mcast && !is_chassis_resident("cr-DR-S3")), action=(reg9[[2]] = 1; next;) @@ -14913,16 +14911,17 @@ AT_CHECK([grep "ls_in_l2_lkup" publicflows | ovn_strip_lflows], [0], [dnl table=??(ls_in_l2_lkup ), priority=71 , match=(eth.mcast && arp), action=(outport = "_MC_flood"; output;) table=??(ls_in_l2_lkup ), priority=71 , match=(eth.mcast && ip), action=(outport = "_MC_flood_l2"; output;) table=??(ls_in_l2_lkup ), priority=72 , match=(eth.mcast && (nd_na || nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;) + table=??(ls_in_l2_lkup ), priority=73 , match=(flags.localnet == 1 && arp.op == 1 && !is_chassis_resident("cr-lr0-public")), action=(drop;) table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:ff:02, 30:54:00:00:00:03} && eth.dst == ff:ff:ff:ff:ff:ff && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.10), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.100), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.10 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::200:ff:fe00:ff02), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.10 && arp.spa == 172.168.0.10), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.100 && arp.spa == 172.168.0.100), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.10 && arp.spa == 172.168.0.10 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.100 && arp.spa == 172.168.0.100 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) ]) AT_CHECK([grep -Fe "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3" -e "30:54:00:00:00:03" -e "sw0-port1" lr0flows | ovn_strip_lflows], [0], [dnl @@ -14949,10 +14948,10 @@ AT_CHECK([grep -Fe "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3 AT_CHECK([grep -Fe "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3" -e "30:54:00:00:00:03" -e "sw0-port1" publicflows | ovn_strip_lflows], [0], [dnl table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && is_chassis_resident("sw0-port1")), action=(outport = "public-lr0"; output;) table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:ff:02, 30:54:00:00:00:03} && eth.dst == ff:ff:ff:ff:ff:ff && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) ]) } @@ -15199,10 +15198,10 @@ AT_CHECK([grep -Fe "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3 AT_CHECK([grep -Fe "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3" -e "30:54:00:00:00:03" -e "sw0-port1" publicflows | ovn_strip_lflows], [0], [dnl table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && is_chassis_resident("sw0-port1")), action=(outport = "public-lr0"; output;) table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:ff:02, 30:54:00:00:00:03} && eth.dst == ff:ff:ff:ff:ff:ff && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110), action=(outport = "_MC_flood_l2"; output;) - table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.110 && arp.spa == 172.168.0.110 && ((flags.localnet == 1 && is_chassis_resident("sw0-port1")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.168.0.120 && arp.spa == 172.168.0.120 && ((flags.localnet == 1 && is_chassis_resident("cr-lr0-public")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) ]) OVN_CLEANUP_NORTHD @@ -23645,3 +23644,263 @@ AT_CHECK([as northd ovn-appctl -t ovn-northd inc-engine/enable-stopwatch nonexis OVN_CLEANUP_NORTHD AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([Logical Switch ARP filtering]) +ovn_start + +check ovn-nbctl lr-add lr1 +check ovn-nbctl lr-add lr2 +check ovn-nbctl lrp-add lr1 down_link f0:00:00:00:00:f1 192.168.1.1/24 aef0::200:ff:fe00:3/64 +check ovn-nbctl lrp-add lr1 private_link f0:00:00:00:00:f2 172.31.1.1/24 fd11::1/64 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl ls-add private + +check ovn-nbctl lsp-add ls1 down_vif1 +check ovn-nbctl lsp-add ls1 down_ext +check ovn-nbctl lsp-add-router-port ls1 up_link down_link + +check ovn-nbctl lsp-add private private_vif1_ipv4 +check ovn-nbctl lsp-add private private_vif1_ipv6 +check ovn-nbctl lsp-add-router-port private pr_uplink private_link + +check ovn-nbctl lsp-set-addresses down_vif1 'f0:00:00:00:00:01 192.168.1.101' +check ovn-nbctl lsp-set-addresses private_vif1_ipv4 'f0:00:00:00:00:05 172.31.0.101' +check ovn-nbctl lsp-set-addresses private_vif1_ipv6 'f0:00:00:00:00:05 fd11::2' + +check ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.31.1.2 172.31.0.101 \ + private_vif1_ipv4 f0:00:00:00:00:07 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat fd12::2 fd11::2 \ + private_vif1_ipv6 f0:00:00:00:00:06 + +check ovn-nbctl lb-add lb1 99.99.99.99:80 172.31.0.9:10880 +check ovn-nbctl lr-lb-add lr1 lb1 + +# Create two NATs: one of them is part of the router subnet; +# for the second one, a gateway (GW) will need to be added later +# when testing with multiple DGPs +check ovn-nbctl lr-nat-add lr1 dnat_and_snat 10.10.10.13 172.31.0.103 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat fd12::3 fd11::3 +check ovn-nbctl lr-nat-add lr1 snat 192.168.1.14 172.31.0.0/24 +check ovn-nbctl lr-nat-add lr1 snat aef0::200:ff:fe00:4 fd11::/64 + +check ovn-nbctl --wait=sb sync + +# Check base routing (without any gateways): ARP/ND in ls_in_l2_lkup should be redirected to the router port and flooded to the unknown multicast group. +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp' | grep 'priority=50'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.1 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:f1; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:f1; arp.tpa = arp.spa; arp.spa = 192.168.1.1; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.101 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:01; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:01; arp.tpa = arp.spa; arp.spa = 192.168.1.101; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:3 && nd.target == aef0::200:ff:fe00:3), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = aef0::200:ff:fe00:3; nd.target = aef0::200:ff:fe00:3; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:f1 && nd.target == fe80::f200:ff:fe00:f1), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = fe80::f200:ff:fe00:f1; nd.target = fe80::f200:ff:fe00:f1; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=80|priority=90'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:3), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:4), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::2), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::3), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f1), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && arp.spa == 10.10.10.13), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && arp.spa == 172.31.1.2), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && arp.spa == 192.168.1.14), action=(outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_lookup_neighbor' | grep lookup_arp | ovn_strip_lflows], [0], [dnl + table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "down_link" && arp.spa == 192.168.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "private_link" && arp.spa == 172.31.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_ip_input' | grep -E 'priority=92|priority=91|priority=90'| ovn_strip_lflows | grep -E 'arp|nd'], [0], [dnl + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 10.10.10.13), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.31.1.2), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 192.168.1.14), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.0/24), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && ip6.dst == {aef0::200:ff:fe00:3, ff02::1:ff00:3} && nd_ns && nd.target == aef0::200:ff:fe00:3), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && ip6.dst == {fe80::f200:ff:fe00:f1, ff02::1:ff00:f1} && nd_ns && nd.target == fe80::f200:ff:fe00:f1), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && arp.op == 1 && arp.tpa == 172.31.1.1 && arp.spa == 172.31.1.0/24), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && ip6.dst == {fd11::1, ff02::1:ff00:1} && nd_ns && nd.target == fd11::1), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && ip6.dst == {fe80::f200:ff:fe00:f2, ff02::1:ff00:f2} && nd_ns && nd.target == fe80::f200:ff:fe00:f2), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {aef0::200:ff:fe00:4, ff02::1:ff00:4} && nd_ns && nd.target == aef0::200:ff:fe00:4), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {fd12::2, ff02::1:ff00:2} && nd_ns && nd.target == fd12::2), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {fd12::3, ff02::1:ff00:3} && nd_ns && nd.target == fd12::3), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) +]) + +# Create a gateway port to centralize routing on hv1 on router. +check ovn-nbctl lrp-set-gateway-chassis down_link hv1 +check ovn-nbctl --wait=sb sync + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp' | grep 'priority=50'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.1 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:f1; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:f1; arp.tpa = arp.spa; arp.spa = 192.168.1.1; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.101 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:01; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:01; arp.tpa = arp.spa; arp.spa = 192.168.1.101; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:3 && nd.target == aef0::200:ff:fe00:3), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = aef0::200:ff:fe00:3; nd.target = aef0::200:ff:fe00:3; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:f1 && nd.target == fe80::f200:ff:fe00:f1), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = fe80::f200:ff:fe00:f1; nd.target = fe80::f200:ff:fe00:f1; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=80|priority=90'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:3 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:3 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:4 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:4 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::2 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::2 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::3 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::3 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f1 && !is_chassis_resident("cr-up_link")), action=(clone {outport = "cr-up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f1 && is_chassis_resident("cr-up_link")), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && arp.spa == 10.10.10.13), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && arp.spa == 172.31.1.2), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && arp.spa == 192.168.1.14), action=(outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_lookup_neighbor' | grep lookup_arp | ovn_strip_lflows], [0], [dnl + table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "down_link" && arp.spa == 192.168.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "private_link" && arp.spa == 172.31.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_ip_input' | grep -E 'priority=92|priority=91|priority=90'| ovn_strip_lflows | grep -E 'arp|nd'], [0], [dnl + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 10.10.10.13), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.31.1.2), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 192.168.1.14), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.0/24), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && ip6.dst == {aef0::200:ff:fe00:3, ff02::1:ff00:3} && nd_ns && nd.target == aef0::200:ff:fe00:3 && is_chassis_resident("cr-down_link")), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "down_link" && ip6.dst == {fe80::f200:ff:fe00:f1, ff02::1:ff00:f1} && nd_ns && nd.target == fe80::f200:ff:fe00:f1 && is_chassis_resident("cr-down_link")), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && arp.op == 1 && arp.tpa == 172.31.1.1 && arp.spa == 172.31.1.0/24), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && ip6.dst == {fd11::1, ff02::1:ff00:1} && nd_ns && nd.target == fd11::1), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(inport == "private_link" && ip6.dst == {fe80::f200:ff:fe00:f2, ff02::1:ff00:f2} && nd_ns && nd.target == fe80::f200:ff:fe00:f2), action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {aef0::200:ff:fe00:4, ff02::1:ff00:4} && nd_ns && nd.target == aef0::200:ff:fe00:4), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {fd12::2, ff02::1:ff00:2} && nd_ns && nd.target == fd12::2), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == {fd12::3, ff02::1:ff00:3} && nd_ns && nd.target == fd12::3), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) +]) + +# Check routing become distributed. +check ovn-nbctl lsp-add-localnet-port ls1 localnet physical +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_arp_rsp' | grep 'priority=50'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.1 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff && ((flags.localnet == 1 && is_chassis_resident("up_link")) || flags.localnet == 0)), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:f1; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:f1; arp.tpa = arp.spa; arp.spa = 192.168.1.1; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.1.101 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff && ((flags.localnet == 1 && is_chassis_resident("down_vif1")) || flags.localnet == 0)), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:01; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:01; arp.tpa = arp.spa; arp.spa = 192.168.1.101; outport = inport; flags.loopback = 1; output;) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:3 && nd.target == aef0::200:ff:fe00:3 && ((flags.localnet == 1 && is_chassis_resident("up_link")) || flags.localnet == 0)), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = aef0::200:ff:fe00:3; nd.target = aef0::200:ff:fe00:3; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:f1 && nd.target == fe80::f200:ff:fe00:f1 && ((flags.localnet == 1 && is_chassis_resident("up_link")) || flags.localnet == 0)), action=(nd_na_router { eth.src = f0:00:00:00:00:f1; ip6.src = fe80::f200:ff:fe00:f1; nd.target = fe80::f200:ff:fe00:f1; nd.tll = f0:00:00:00:00:f1; outport = inport; flags.loopback = 1; output; };) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=80|priority=90'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && ((flags.localnet == 1 && is_chassis_resident("private_vif1_ipv4")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:3), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:4), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::2), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fd12::3), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f1), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 10.10.10.13 && arp.spa == 10.10.10.13 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 172.31.1.2 && arp.spa == 172.31.1.2 && ((flags.localnet == 1 && is_chassis_resident("private_vif1_ipv4")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && arp.spa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) +]) + +# Check default drop for arp +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep priority=73 | grep flags.localnet | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=73 , match=(flags.localnet == 1 && arp.op == 1 && !is_chassis_resident("cr-down_link")), action=(drop;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_lookup_neighbor' | grep lookup_arp | ovn_strip_lflows], [0], [dnl + table=??(lr_in_lookup_neighbor), priority=100 , match=(arp.op == 2), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "down_link" && arp.spa == 192.168.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) + table=??(lr_in_lookup_neighbor), priority=100 , match=(inport == "private_link" && arp.spa == 172.31.1.0/24 && arp.op == 1), action=(reg9[[2]] = lookup_arp(inport, arp.spa, arp.sha); next;) +]) + +AT_CHECK([ovn-sbctl lflow-list lr1 | grep 'lr_in_ip_input' | grep -E 'priority=92'| ovn_strip_lflows | grep -E 'arp|nd'], [0], [dnl + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && arp.op == 1 && arp.tpa == 10.10.10.13 && is_chassis_resident("cr-down_link")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && arp.op == 1 && arp.tpa == 172.31.1.2 && is_chassis_resident("private_vif1_ipv4")), action=(eth.dst = eth.src; eth.src = f0:00:00:00:00:07; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = f0:00:00:00:00:07; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && arp.op == 1 && arp.tpa == 192.168.1.14 && is_chassis_resident("cr-down_link")), action=(eth.dst = eth.src; eth.src = xreg0[[0..47]]; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = xreg0[[0..47]]; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && ip6.dst == {aef0::200:ff:fe00:4, ff02::1:ff00:4} && nd_ns && nd.target == aef0::200:ff:fe00:4 && is_chassis_resident("cr-down_link")), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && ip6.dst == {fd12::2, ff02::1:ff00:2} && nd_ns && nd.target == fd12::2 && is_chassis_resident("private_vif1_ipv6")), action=(nd_na { eth.src = f0:00:00:00:00:06; ip6.src = nd.target; nd.tll = f0:00:00:00:00:06; outport = inport; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "down_link" && ip6.dst == {fd12::3, ff02::1:ff00:3} && nd_ns && nd.target == fd12::3 && is_chassis_resident("cr-down_link")), action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) +]) + +# Add one more dgp port: check drop flow. +check ovn-nbctl lrp-add lr2 down_link_lr2 f0:00:00:00:00:f8 192.168.1.8/24 aef0::200:ff:fe00:8 +check ovn-nbctl lrp-set-gateway-chassis down_link_lr2 hv2 +check ovn-nbctl lsp-add-router-port ls1 up_link_lr2 down_link_lr2 +check ovn-nbctl --wait=sb sync + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep 'priority=73' | \ + grep -F 'flags.localnet == 1' | \ + grep -F 'arp.op == 1' | \ + grep -F 'is_chassis_resident("cr-down_link_lr2")' | \ + grep -F 'is_chassis_resident("cr-down_link")' | \ + grep -F 'action=(drop;)' | wc -l], [0], [1 +]) + +# Create a second DGP on the router and verify that ls_in_l2_lkup +# selects the correct CR port. Traffic destined for router-connected +# subnets should use the DGP in the corresponding subnet, while +# NATed traffic should use the DGP specified by the NAT rule. +# Routing remains distributed with multiple DGPs present. +check ovn-nbctl lrp-add lr1 lrp-gw2 f0:00:00:00:00:f1 11.11.11.1/24 aeff::200:ff:fe00:3/64 +check ovn-nbctl lrp-set-gateway-chassis lrp-gw2 hv2 + +lrp_uuid=$(fetch_column nb:logical_router_port _uuid name=lrp-gw2) +uuid=$(fetch_column nb:nat _uuid external_ip=10.10.10.13) +check ovn-nbctl set nat $uuid gateway_port=$lrp_uuid +uuid=$(fetch_column nb:nat _uuid external_ip='"fd12::3"') +check ovn-nbctl set nat $uuid gateway_port=$lrp_uuid +check ovn-nbctl --wait=sb sync + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=80|priority=90'| ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.8 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link_lr2")) || flags.localnet == 0)), action=(clone {outport = "up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:3), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:4), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:8), action=(clone {outport = "up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f1), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == fe80::f200:ff:fe00:f8), action=(clone {outport = "up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && arp.spa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.8 && arp.spa == 192.168.1.8 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link_lr2")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep priority=73 | grep flags.localnet | grep -F 'is_chassis_resident("cr-down_link_lr2")' | grep -F 'is_chassis_resident("cr-down_link")' | grep drop], [0], [ignore]) + +# Delete the localnet port. This should cause routing for up_link_lr2 to become centralized, while up_link routing remains distributed. +check ovn-nbctl lsp-del localnet +check ovn-nbctl --wait=sb sync +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=80|priority=90'| grep flags.localnet | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(clone {outport = "up_link"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.1 && arp.spa == 192.168.1.1 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.14 && arp.spa == 192.168.1.14 && ((flags.localnet == 1 && is_chassis_resident("cr-down_link")) || flags.localnet == 0)), action=(outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep priority=73 | grep flags.localnet | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=73 , match=(flags.localnet == 1 && arp.op == 1 && !is_chassis_resident("cr-down_link")), action=(drop;) +]) + +AT_CHECK([ovn-sbctl lflow-list ls1 | grep 'ls_in_l2_lkup' | grep -E 'priority=75|priority=80|priority=90'| grep -E 'aef0::200:ff:fe00:8|192.168.1.8' | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.8 && !is_chassis_resident("cr-up_link_lr2")), action=(clone {outport = "cr-up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.8 && is_chassis_resident("cr-up_link_lr2")), action=(clone {outport = "up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:8 && !is_chassis_resident("cr-up_link_lr2")), action=(clone {outport = "cr-up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && eth.mcast && nd_ns && nd.target == aef0::200:ff:fe00:8 && is_chassis_resident("cr-up_link_lr2")), action=(clone {outport = "up_link_lr2"; output; }; outport = "_MC_unknown"; output;) + table=??(ls_in_l2_lkup ), priority=90 , match=(flags[[1]] == 0 && eth.dst == ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.tpa == 192.168.1.8 && arp.spa == 192.168.1.8), action=(outport = "_MC_flood_l2"; output;) +]) + +OVN_CLEANUP_NORTHD +AT_CLEANUP +]) diff --git a/tests/ovn.at b/tests/ovn.at index 9c4698d76..19e941cc0 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -28121,6 +28121,8 @@ match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" as hv1 OVS_WAIT_UNTIL([ + ovs-ofctl dump-flows br-int | \ + grep -E "${match_arp_req}" | grep "${match_send_rtr1}" pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ grep n_packets=1 -c) @@ -28185,12 +28187,19 @@ AT_CAPTURE_FILE([sbflows2]) as hv1 AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl arp_tpa=10.0.0.1 +arp_tpa=10.0.0.1 +arp_tpa=10.0.0.11 arp_tpa=10.0.0.11 arp_tpa=10.0.0.111 +arp_tpa=10.0.0.111 +arp_tpa=10.0.0.121 arp_tpa=10.0.0.121 arp_tpa=10.0.0.122 arp_tpa=10.0.0.2 +arp_tpa=10.0.0.2 arp_tpa=10.0.0.22 +arp_tpa=10.0.0.22 +arp_tpa=10.0.0.222 arp_tpa=10.0.0.222 ]) AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl @@ -46867,3 +46876,133 @@ AT_CHECK([grep "skipping output to input port" \ OVN_CLEANUP([hv1]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([GARP delivery: gw and external ports]) +CHECK_SCAPY +ovn_start + +# Configure initial environment +# LR1: down_link <-> LS1: up_link +# set lr_down: gateway port (chassis redirect) bound to hv1 +# LS1: down_vif1 - vif port bound to hv1 +# down_vif2 - vif port bound to hv2 +# down_ext - outer (port will be iterated as localnet, l2gateway) +# +# Test: send GARP request from virtual ports (down_vif1, down_vif2) +# ensure mac_binding is always updated. +# (Fixing the issue: mac_binding is only updated for packets came from +# down_link's resident chassis) +# send GARP request from from localnet. +# ensure mac_binding is updated only if localnet bound to same hv as l3dgw + +check ovn-nbctl lr-add lr1 +check ovn-nbctl lrp-add lr1 down_link f0:00:00:00:00:f1 192.168.1.1/24 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl lsp-add ls1 up_link +check ovn-nbctl lsp-add ls1 down_vif1 +check ovn-nbctl lsp-add ls1 down_vif2 +check ovn-nbctl lsp-add ls1 down_ext + +check ovn-nbctl set Logical_Switch_Port up_link \ + type=router \ + options:router-port=down_link \ + addresses=router + +check ovn-nbctl lsp-set-addresses down_vif1 'f0:00:00:00:00:01 192.168.1.101' +check ovn-nbctl lsp-set-addresses down_vif2 'f0:00:00:00:00:02 192.168.1.102' + +check ovn-nbctl lsp-set-type down_ext localnet +check ovn-nbctl lsp-set-options down_ext network_name=physnet1 +check ovn-nbctl lrp-set-gateway-chassis down_link hv1 + +net_add n1 + +# Create hypervisor hv1 connected to n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl add-port br-int vif1 -- \ + set Interface vif1 external-ids:iface-id=down_vif1 \ + options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap + +# Create hypervisor hv2 connected to n1, add localnet here +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovs-vsctl add-br br-eth0 +ovn_attach n1 br-phys 192.168.0.2 +ovs-vsctl add-port br-int vif2 -- \ + set Interface vif2 external-ids:iface-id=down_vif2 \ + options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap + +ovs-vsctl set Open_vSwitch . external_ids:ovn-bridge-mappings="physnet1:br-eth0" + +ovs-vsctl add-port br-eth0 vif_ext -- \ + set Interface vif_ext options:tx_pcap=hv2/vif_ext-tx.pcap \ + options:rxq_pcap=hv2/vif_ext-rx.pcap + +# Pre-populate the hypervisors' ARP tables so that we don't lose any +# packets for ARP resolution (native tunneling doesn't queue packets +# for ARP resolution). +OVN_POPULATE_ARP + +wait_for_ports_up +check ovn-nbctl --wait=hv sync + +# Annonce 192.168.1.222 from localnet in hv2 +# result: drop, hv2 is not gateway chassis for down_link +sha=02:00:00:00:00:ee +tha=00:00:00:00:00:00 +spa=192.168.1.222 +tpa=$spa +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${sha}')/ \ + ARP(hwsrc='${sha}', hwdst='${tha}', psrc='${spa}', pdst='${tpa}')") +as hv2 ovs-appctl netdev-dummy/receive vif_ext $garp + +# Make hv2 gateway chassis +# Annonce 192.168.1.223 from localnet in hv2 +# result: ok, hv2 is gateway chassis for down_link +# +check ovn-nbctl lrp-set-gateway-chassis down_link hv2 + +wait_row_count Port_Binding 1 logical_port=cr-down_link 'chassis!=[[]]' +check ovn-nbctl --wait=hv sync + +sha=02:00:00:00:00:ee +tha=00:00:00:00:00:00 +spa=192.168.1.223 +tpa=$spa +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${sha}')/ \ + ARP(hwsrc='${sha}', hwdst='${tha}', psrc='${spa}', pdst='${tpa}')") +as hv2 ovs-appctl netdev-dummy/receive vif_ext $garp + +# Annonce 192.168.1.111, 112 from vif1, vif2 in hv1, hv2 +# result: ok, vif1, vif2 are virtual ports, restrictions are not applied. +sha=f0:00:00:00:00:01 +tha=00:00:00:00:00:00 +spa=192.168.1.111 +tpa=0.0.0.0 +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${sha}')/ \ + ARP(hwsrc='${sha}', hwdst='${tha}', psrc='${spa}', pdst='${tpa}')") +as hv1 ovs-appctl netdev-dummy/receive vif1 $garp + +sha=f0:00:00:00:00:02 +tha=00:00:00:00:00:00 +spa=192.168.1.112 +tpa=0.0.0.0 +garp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='${sha}')/ \ + ARP(hwsrc='${sha}', hwdst='${tha}', psrc='${spa}', pdst='${tpa}')") +as hv2 ovs-appctl netdev-dummy/receive vif2 $garp + +wait_row_count MAC_Binding 1 ip="192.168.1.111" mac='"f0:00:00:00:00:01"' logical_port='"down_link"' +wait_row_count MAC_Binding 1 ip="192.168.1.112" mac='"f0:00:00:00:00:02"' logical_port='"down_link"' +wait_row_count MAC_Binding 1 ip="192.168.1.223" mac='"02:00:00:00:00:ee"' logical_port='"down_link"' +wait_row_count MAC_Binding 0 ip="192.168.1.222" mac='"02:00:00:00:00:ee"' logical_port='"down_link"' + +check ovn-nbctl --wait=hv lsp-set-type down_ext localnet + +OVN_CLEANUP([hv1],[hv2]) +AT_CLEANUP +]) diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at index a7585eeb6..d4a8752a7 100644 --- a/tests/system-common-macros.at +++ b/tests/system-common-macros.at @@ -93,6 +93,9 @@ m4_define([ADD_VETH], # m4_define([FORMAT_PING], [grep "transmitted" | sed 's/time.*ms$/time 0ms/']) +m4_define([FORMAT_ARPING], +[[sed -n 's/^Unicast reply from \([0-9.][0-9.]*\) \[\([^][]*\)\].*/\1 \2/p']]) + # FORMAT_CT([ip-addr]) # # Strip content from the piped input which would differ from test to test diff --git a/tests/system-ovn.at b/tests/system-ovn.at index ee5c2b810..106fae546 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -22416,24 +22416,13 @@ AT_CLEANUP ]) OVN_FOR_EACH_NORTHD([ -AT_SETUP([VIF port connected to localnet network]) -# -# Topology: -# (fabric) -- localnet-port -- LS --- DGP(chassis2) -- LR -# | -# | -# VM (chassis1) -# -# It is expected that ARP requests to this port are allowed on the chassis that hosts this port. - +AT_SETUP([ARP restrictions: GARPs and ARP/ND to router ips centralized and distributed routing]) ovn_start OVS_TRAFFIC_VSWITCHD_START() ADD_BR([br-int]) -ADD_BR([br-ext]) +ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone]) -ovs-ofctl add-flow br-ext action=normal -# Set external-ids in br-int needed for ovn-controller -ovs-vsctl \ +check 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 \ @@ -22441,44 +22430,165 @@ ovs-vsctl \ -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true -# Start ovn-controller start_daemon ovn-controller check ovn-nbctl lr-add lr1 -check ovn-nbctl ls-add public +check ovn-nbctl lrp-add lr1 lr1-outside f0:00:00:00:00:f1 192.168.0.1/24 2001:db8::1/64 +check ovn-nbctl lrp-add lr1 lr1-private f0:00:00:00:00:f2 172.31.0.1/24 2002:db8::1/64 -check ovn-nbctl lrp-add lr1 rp-public 00:00:02:01:02:03 172.31.1.1/24 -check ovn-nbctl lsp-add-router-port public public-rp rp-public -check ovn-nbctl lsp-add-localnet-port public localnet phynet -check ovn-nbctl lrp-set-gateway-chassis rp-public hv2 +check ovn-nbctl ls-add outside +check ovn-nbctl ls-add private -ADD_NAMESPACES(ext) -ADD_VETH(ext, ext, br-ext, "172.31.1.2/24", "f0:00:00:01:02:02", \ - "172.31.1.1") -ADD_NAMESPACES(lsp1) -ADD_VETH(lsp1, lsp1, br-int, "172.31.1.3/24", "f0:00:00:01:02:03", \ - "172.31.1.1") -ADD_NAMESPACES(lsp2) -ADD_VETH(lsp2, lsp2, br-int, "172.31.1.4/24", "f0:00:00:01:02:04", \ - "172.31.1.1") +check ovn-nbctl lsp-add-router-port outside outside-lr1 lr1-outside +check ovn-nbctl lsp-add-router-port private private-lr1 lr1-private + +check ovn-nbctl lsp-add outside outside_vif +check ovn-nbctl lsp-add private private_vif +check ovn-nbctl lsp-add private private_nat + +check ovn-nbctl lsp-set-addresses outside_vif "02:ac:10:01:01:03 192.168.0.3 2001:db8::3" +ADD_NAMESPACES(outside_vif) +ADD_VETH(outside_vif, outside_vif, br-int, "2001:db8::3/64", "02:ac:10:01:01:03", \ + "2001:db8::1", "nodad", "192.168.0.3/24", "192.168.0.1") + +check ovn-nbctl lsp-set-addresses private_vif "02:ac:10:01:02:03 172.31.0.3 2002:db8::3" +ADD_NAMESPACES(private_vif) +ADD_VETH(private_vif, private_vif, br-int, "2002:db8::3/64", "02:ac:10:01:02:03", \ + "2002:db8::1", "nodad", "172.31.0.3/24", "172.31.0.1") + +check ovn-nbctl lsp-set-addresses private_nat "02:ac:10:01:02:04 172.31.0.4 2002:db8::4" +ADD_NAMESPACES(private_nat) +ADD_VETH(private_nat, private_nat, br-int, "2002:db8::4/64", "02:ac:10:01:02:04", \ + "2002:db8::1", "nodad", "172.31.0.4/24", "172.31.0.1") + +check ovn-nbctl lr-nat-add lr1 dnat_and_snat 10.10.10.1 172.31.0.3 private_nat f0:00:00:00:00:07 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat 10.10.10.2 172.31.0.4 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat fd12::1 2002:db8::3 private_nat f0:00:00:00:00:07 +check ovn-nbctl lr-nat-add lr1 dnat_and_snat fd12::2 2002:db8::4 -check ovn-nbctl lsp-add public lsp1 -check ovn-nbctl lsp-set-addresses lsp1 "f0:00:00:01:02:03 172.31.1.3" -check ovn-nbctl lsp-add public lsp2 -check ovn-nbctl lsp-set-addresses lsp2 "f0:00:00:01:02:04 172.31.1.4" +check ovn-nbctl lrp-set-gateway-chassis lr1-outside hv1 +check ovn-nbctl lsp-add-localnet-port outside localnet phynet +ADD_NAMESPACES(external) +ADD_VETH(external, external, br-ext, "2001:db8::2/64", "02:ac:10:01:01:02", \ + "2001:db8::1", "nodad", "192.168.0.2/24", "192.168.0.1") + +OVN_POPULATE_ARP +wait_for_ports_up check ovn-nbctl --wait=hv sync -NS_CHECK_EXEC([ext], [ping -q -c 3 -i 0.3 -w 2 172.31.1.3 | FORMAT_PING], \ -[0], [dnl +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 192.168.0.1 | FORMAT_ARPING], [0], +[dnl +192.168.0.1 F0:00:00:00:00:F1 +]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 192.168.0.3 | FORMAT_ARPING], [0], +[dnl +192.168.0.3 02:AC:10:01:01:03 +]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 10.10.10.1 | FORMAT_ARPING], [0], +[dnl +10.10.10.1 F0:00:00:00:00:07 +]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 10.10.10.2 | FORMAT_ARPING], [0], +[dnl +10.10.10.2 F0:00:00:00:00:F1 +]) + +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 2001:db8::1 | FORMAT_PING], [0], +[dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 2001:db8::3 | FORMAT_PING], [0], +[dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 fd12::1 | FORMAT_PING], [0], +[dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], [0], +[dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) -NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 172.31.1.4 | FORMAT_PING], \ -[0], [dnl +wait_row_count MAC_Binding 1 logical_port=lr1-outside ip=192.168.0.2 mac="02\:ac\:10\:01\:01\:02" +wait_row_count MAC_Binding 1 logical_port=lr1-outside ip='"2001:db8::2"' mac="02\:ac\:10\:01\:01\:02" + +# sending garp announcements +NS_CHECK_EXEC([external], [ip addr del 192.168.0.2/24 dev external; + ip addr add 192.168.0.34/24 dev external; + ip route add default via 192.168.0.1]) +NS_CHECK_EXEC([external], [arping -U -c 1 -I external -s 192.168.0.34 192.168.0.34 >/dev/null 2>&1], [0]) + +wait_row_count MAC_Binding 1 logical_port=lr1-outside ip=192.168.0.34 mac="02\:ac\:10\:01\:01\:02" + +# Change ha to a non-existent one - connectivity should be successful only to distributed nat and external_vif +check ovn-nbctl clear logical_router_port lr1-outside gateway_chassis +check ovn-nbctl lrp-set-gateway-chassis lr1-outside hv2 +check ovn-nbctl --wait=hv sync + +NS_CHECK_EXEC([external], [ip neigh flush dev external], [0]) +NS_CHECK_EXEC([external], [ip -6 neigh flush dev external], [0]) + +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 192.168.0.1 >/dev/null 2>&1], [1]) +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 2001:db8::1 >/dev/null 2>&1], [1]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 10.10.10.2 >/dev/null 2>&1], [1]) +NS_CHECK_EXEC([external], [ping6 -q -c 3 -i 0.3 -w 2 fd12::2 >/dev/null 2>&1], [1]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 10.10.10.1 >/dev/null 2>&1], [0]) +NS_CHECK_EXEC([external], [arping -f -c 3 -I external 192.168.0.3 >/dev/null 2>&1], [0]) + +# sending garp announcements - must be unsuccessful +NS_CHECK_EXEC([external], [ip addr del 192.168.0.34/24 dev external; + ip addr add 192.168.0.35/24 dev external; + ip route add default via 192.168.0.1]) +NS_CHECK_EXEC([external], [arping -U -c 1 -I external -s 192.168.0.35 192.168.0.35 >/dev/null 2>&1], [0]) + +wait_row_count MAC_Binding 0 logical_port=lr1-outside ip=192.168.0.35 + +# Check centralized routing: delete localnet port, check connetivity from outside_vif +# For a full check, another physical chassis is needed, so this test only checks when the port and HA are on the same host +check ovn-nbctl lrp-set-gateway-chassis lr1-outside hv1 +check ovn-nbctl lsp-del localnet + +check ovn-nbctl --wait=hv sync + +NS_CHECK_EXEC([outside_vif], [arping -f -c 3 -I outside_vif 192.168.0.1 | FORMAT_ARPING], [0], +[dnl +192.168.0.1 F0:00:00:00:00:F1 +]) +NS_CHECK_EXEC([outside_vif], [arping -f -c 3 -I outside_vif 10.10.10.1 | FORMAT_ARPING], [0], +[dnl +10.10.10.1 F0:00:00:00:00:F1 +]) +NS_CHECK_EXEC([outside_vif], [arping -f -c 3 -I outside_vif 10.10.10.2 | FORMAT_ARPING], [0], +[dnl +10.10.10.2 F0:00:00:00:00:F1 +]) + +NS_CHECK_EXEC([outside_vif], [ping6 -q -c 3 -i 0.3 -w 2 2001:db8::1 | FORMAT_PING], [0], +[dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +NS_CHECK_EXEC([outside_vif], [ping6 -q -c 3 -i 0.3 -w 2 fd12::1 | FORMAT_PING], [0], +[dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +NS_CHECK_EXEC([outside_vif], [ping6 -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], [0], +[dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) +# A GARP from a VIF of that switch still has to be learnt: VIF traffic has +# flags.localnet == 0 and is not subject to the localnet restrictions. +check ovn-nbctl clear logical_router_port lr1-outside gateway_chassis +check ovn-nbctl lrp-set-gateway-chassis lr1-outside hv2 +check ovn-nbctl lsp-add-localnet-port outside localnet phynet +check ovn-nbctl --wait=hv sync + +NS_CHECK_EXEC([outside_vif], [ip addr add 192.168.0.44/24 dev outside_vif]) +NS_CHECK_EXEC([outside_vif], [arping -U -c 1 -I outside_vif -s 192.168.0.44 192.168.0.44 >/dev/null 2>&1], [0]) +wait_row_count MAC_Binding 1 logical_port=lr1-outside ip=192.168.0.44 mac="02\:ac\:10\:01\:01\:03" + OVN_CLEANUP_CONTROLLER([hv1]) OVN_CLEANUP_NORTHD -- 2.48.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
