From patchwork Thu May 9 18:00:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1933560 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Vb0Dq6gyQz20fZ for ; Fri, 10 May 2024 04:00:59 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 3BD1483F01; Thu, 9 May 2024 18:00:58 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id N_pjAA1U1Zwx; Thu, 9 May 2024 18:00:57 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 27B3083F03 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTPS id 27B3083F03; Thu, 9 May 2024 18:00:57 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0F1B9C0077; Thu, 9 May 2024 18:00:57 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1FFB5C0037 for ; Thu, 9 May 2024 18:00:56 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id DE91F83EFE for ; Thu, 9 May 2024 18:00:54 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Vi9LixZApBxH for ; Thu, 9 May 2024 18:00:53 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.183.197; helo=relay5-d.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 38D2D83F01 Authentication-Results: smtp1.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 38D2D83F01 Received: from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net [217.70.183.197]) by smtp1.osuosl.org (Postfix) with ESMTPS id 38D2D83F01 for ; Thu, 9 May 2024 18:00:52 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 0D0C81C0004; Thu, 9 May 2024 18:00:49 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 9 May 2024 14:00:45 -0400 Message-ID: <20240509180045.1463906-1-numans@ovn.org> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240509180015.1463789-1-numans@ovn.org> References: <20240509180015.1463789-1-numans@ovn.org> MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v2 1/3] northd: Don't reparse lport's addresses while adding L2_LKUP flows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique The addresses are already parsed and stored in the "struct ovn_port"'s lsp_addrs field. Signed-off-by: Numan Siddique --- northd/northd.c | 165 +++++++++++++++++------------------------------- 1 file changed, 59 insertions(+), 106 deletions(-) diff --git a/northd/northd.c b/northd/northd.c index 0cabda7ea0..c0f477358a 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -9568,126 +9568,79 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op, return; } - /* For ports connected to logical routers add flows to bypass the - * broadcast flooding of ARP/ND requests in table 19. We direct the - * requests only to the router port that owns the IP address. - */ - if (lsp_is_router(op->nbsp)) { - build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows, - &op->nbsp->header_); - } - bool lsp_clone_to_unknown = lsp_is_clone_to_unknown(op->nbsp); + bool lsp_enabled = lsp_is_enabled(op->nbsp); + const char *action = lsp_enabled + ? ((lsp_clone_to_unknown && op->od->has_unknown) + ? "clone {outport = %s; output; };" + "outport = \""MC_UNKNOWN "\"; output;" + : "outport = %s; output;") + : debug_drop_action(); + ds_clear(actions); + ds_put_format(actions, action, op->json_key); - for (size_t i = 0; i < op->nbsp->n_addresses; i++) { - /* Addresses are owned by the logical port. - * Ethernet address followed by zero or more IPv4 - * or IPv6 addresses (or both). */ - struct eth_addr mac; - bool lsp_enabled = lsp_is_enabled(op->nbsp); - const char *action = lsp_enabled - ? ((lsp_clone_to_unknown && op->od->has_unknown) - ? "clone {outport = %s; output; };" - "outport = \""MC_UNKNOWN "\"; output;" - : "outport = %s; output;") - : debug_drop_action(); - - if (ovs_scan(op->nbsp->addresses[i], - ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) { - ds_clear(match); - ds_put_format(match, "eth.dst == "ETH_ADDR_FMT, - ETH_ADDR_ARGS(mac)); + if (lsp_is_router(op->nbsp) && op->peer && op->peer->nbrp) { + /* For ports connected to logical routers add flows to bypass the + * broadcast flooding of ARP/ND requests in table 19. 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_); - ds_clear(actions); - ds_put_format(actions, action, op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_L2_LKUP, - 50, ds_cstr(match), - ds_cstr(actions), - &op->nbsp->header_, - op->lflow_ref); - } else if (!strcmp(op->nbsp->addresses[i], "unknown")) { - continue; - } else if (is_dynamic_lsp_address(op->nbsp->addresses[i])) { - if (!op->nbsp->dynamic_addresses - || !ovs_scan(op->nbsp->dynamic_addresses, - ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) { - continue; - } - ds_clear(match); - ds_put_format(match, "eth.dst == "ETH_ADDR_FMT, - ETH_ADDR_ARGS(mac)); + ds_clear(match); + if (!eth_addr_is_zero(op->proxy_arp_addrs.ea)) { + ds_put_format(match, "eth.dst == { %s, %s }", + op->proxy_arp_addrs.ea_s, + op->peer->lrp_networks.ea_s); + } else { + ds_put_format(match, "eth.dst == %s", op->peer->lrp_networks.ea_s); + } - ds_clear(actions); - ds_put_format(actions, action, op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_L2_LKUP, - 50, ds_cstr(match), - ds_cstr(actions), - &op->nbsp->header_, - op->lflow_ref); - } else if (!strcmp(op->nbsp->addresses[i], "router")) { - if (!op->peer || !op->peer->nbrp - || !ovs_scan(op->peer->nbrp->mac, - ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) { - continue; - } - ds_clear(match); - ds_put_cstr(match, "eth.dst == "); - if (!eth_addr_is_zero(op->proxy_arp_addrs.ea)) { - ds_put_format(match, - "{ %s, "ETH_ADDR_FMT" }", - op->proxy_arp_addrs.ea_s, - ETH_ADDR_ARGS(mac)); + if (op->peer->od->n_l3dgw_ports && op->od->n_localnet_ports) { + bool add_chassis_resident_check = false; + const char *json_key; + if (is_l3dgw_port(op->peer)) { + /* The peer of this port represents a distributed + * gateway port. The destination lookup flow for the + * router's distributed gateway port MAC address should + * only be programmed on the gateway chassis. */ + add_chassis_resident_check = true; + json_key = op->peer->cr_port->json_key; } else { - ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); + /* Check if the option 'reside-on-redirect-chassis' + * is set to true on the peer port. If set to true + * and if the logical switch has a localnet port, it + * means the router pipeline for the packets from + * this logical switch should be run on the chassis + * hosting the gateway port. + */ + add_chassis_resident_check = smap_get_bool( + &op->peer->nbrp->options, + "reside-on-redirect-chassis", false) && + op->peer->od->n_l3dgw_ports == 1; + json_key = op->peer->od->l3dgw_ports[0]->cr_port->json_key; } - if (op->peer->od->n_l3dgw_ports - && op->od->n_localnet_ports) { - bool add_chassis_resident_check = false; - const char *json_key; - if (is_l3dgw_port(op->peer)) { - /* The peer of this port represents a distributed - * gateway port. The destination lookup flow for the - * router's distributed gateway port MAC address should - * only be programmed on the gateway chassis. */ - add_chassis_resident_check = true; - json_key = op->peer->cr_port->json_key; - } else { - /* Check if the option 'reside-on-redirect-chassis' - * is set to true on the peer port. If set to true - * and if the logical switch has a localnet port, it - * means the router pipeline for the packets from - * this logical switch should be run on the chassis - * hosting the gateway port. - */ - add_chassis_resident_check = smap_get_bool( - &op->peer->nbrp->options, - "reside-on-redirect-chassis", false) && - op->peer->od->n_l3dgw_ports == 1; - json_key = - op->peer->od->l3dgw_ports[0]->cr_port->json_key; - } - if (add_chassis_resident_check) { - ds_put_format(match, " && is_chassis_resident(%s)", - json_key); - } + if (add_chassis_resident_check) { + ds_put_format(match, " && is_chassis_resident(%s)", json_key); } + } + + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_L2_LKUP, 50, + ds_cstr(match), ds_cstr(actions), + &op->nbsp->header_, + op->lflow_ref); + } else { + for (size_t i = 0; i < op->n_lsp_addrs; i++) { + ds_clear(match); + ds_put_format(match, "eth.dst == %s", op->lsp_addrs[i].ea_s); - ds_clear(actions); - ds_put_format(actions, action, op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50, ds_cstr(match), ds_cstr(actions), &op->nbsp->header_, op->lflow_ref); - } else { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - - VLOG_INFO_RL(&rl, - "%s: invalid syntax '%s' in addresses column", - op->nbsp->name, op->nbsp->addresses[i]); } } } From patchwork Thu May 9 18:01:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1933561 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Vb0FD01yCz20fZ for ; Fri, 10 May 2024 04:01:19 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 27B2441EEB; Thu, 9 May 2024 18:01:18 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Q-2NTlSVi_sN; Thu, 9 May 2024 18:01:16 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 93B224156F Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 93B224156F; Thu, 9 May 2024 18:01:16 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 52CDFC0DCF; Thu, 9 May 2024 18:01:16 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 189DBC0037 for ; Thu, 9 May 2024 18:01:15 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 6C52A41DBF for ; Thu, 9 May 2024 18:01:12 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id VoWFv-Ty3xXa for ; Thu, 9 May 2024 18:01:11 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.183.195; helo=relay3-d.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp2.osuosl.org 1B7B04045A Authentication-Results: smtp2.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 1B7B04045A Received: from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net [217.70.183.195]) by smtp2.osuosl.org (Postfix) with ESMTPS id 1B7B04045A for ; Thu, 9 May 2024 18:01:10 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id C10AD60004; Thu, 9 May 2024 18:01:06 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 9 May 2024 14:01:03 -0400 Message-ID: <20240509180103.1463967-1-numans@ovn.org> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240509180015.1463789-1-numans@ovn.org> References: <20240509180015.1463789-1-numans@ovn.org> MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v2 2/3] northd: Refactor chassisresident port checking. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique The implementation of util functions "is_cr_port()" and "is_l3dgw_port()" are confusing and not very intuitive. This patch adds some documentation. It also renames the struct ovn_port member 'l3dgw_port' to 'primary_port'. If struct ovn_port->primary_port is set, then it means 'ovn_port' instance is a chassis resident port and it is derived from the primary port. An upcoming patch creates a chassisresident port even for a logical switch port of type "patch" and hence renamed to avoid confusion. Signed-off-by: Numan Siddique --- northd/northd.c | 48 +++++++++++++++++++++++++++++++++++------------- northd/northd.h | 10 ++++------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/northd/northd.c b/northd/northd.c index c0f477358a..66065db7ff 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -1077,16 +1077,36 @@ struct ovn_port_routable_addresses { static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *); +/* This function returns true if 'op' is a gateway router port. + * False otherwise. + * For 'op' to be a gateway router port. + * 1. op->nbrp->gateway_chassis or op->nbrp->ha_chassis_group should + * be configured. + * 2. op->cr_port should not be NULL. If op->nbrp->gateway_chassis or + * op->nbrp->ha_chassis_group is set by the user, northd WILL create + * a chassis resident port in the SB port binding. + * See join_logical_ports(). + */ static bool is_l3dgw_port(const struct ovn_port *op) { - return op->cr_port; + return op->cr_port && op->nbrp && + (op->nbrp->n_gateway_chassis || op->nbrp->ha_chassis_group); } +/* This function returns true if 'op' is a chassis resident + * derived port. False otherwise. + * There are 2 ways to check if 'op' is chassis resident port. + * 1. op->sb->type is "chassisresident" + * 2. op->primary_port is not NULL. If op->primary_port is set, + * it means 'op' is derived from the ovn_port op->primary_port. + * + * This function uses the (2) method as it doesn't involve strcmp(). + */ static bool is_cr_port(const struct ovn_port *op) { - return op->l3dgw_port; + return op->primary_port; } static void @@ -1171,7 +1191,7 @@ ovn_port_create(struct hmap *ports, const char *key, op->key = xstrdup(key); op->sb = sb; ovn_port_set_nb(op, nbsp, nbrp); - op->l3dgw_port = op->cr_port = NULL; + op->primary_port = op->cr_port = NULL; hmap_insert(ports, &op->key_node, hash_string(op->key, 0)); op->lflow_ref = lflow_ref_create(); @@ -1228,7 +1248,7 @@ ovn_port_destroy(struct hmap *ports, struct ovn_port *port) /* Don't remove port->list. The node should be removed from such lists * before calling this function. */ hmap_remove(ports, &port->key_node); - if (port->od && !port->l3dgw_port) { + if (port->od && !port->primary_port) { hmap_remove(&port->od->ports, &port->dp_node); } ovn_port_destroy_orphan(port); @@ -1388,7 +1408,7 @@ lrport_is_enabled(const struct nbrec_logical_router_port *lrport) static struct ovn_port * ovn_port_get_peer(const struct hmap *lr_ports, struct ovn_port *op) { - if (!op->nbsp || !lsp_is_router(op->nbsp) || op->l3dgw_port) { + if (!op->nbsp || !lsp_is_router(op->nbsp) || is_cr_port(op)) { return NULL; } @@ -2268,7 +2288,7 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, NULL, nbrp, NULL); ovs_list_push_back(nb_only, &crp->list); } - crp->l3dgw_port = op; + crp->primary_port = op; op->cr_port = crp; crp->od = od; free(redirect_name); @@ -2289,7 +2309,7 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, * to their peers. */ struct ovn_port *op; HMAP_FOR_EACH (op, key_node, ports) { - if (op->nbsp && lsp_is_router(op->nbsp) && !op->l3dgw_port) { + if (op->nbsp && lsp_is_router(op->nbsp) && !op->primary_port) { struct ovn_port *peer = ovn_port_get_peer(ports, op); if (!peer || !peer->nbrp) { continue; @@ -2343,7 +2363,7 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, op->enable_router_port_acl = smap_get_bool( &op->nbsp->options, "enable_router_port_acl", false); } - } else if (op->nbrp && op->nbrp->peer && !op->l3dgw_port) { + } else if (op->nbrp && op->nbrp->peer && !is_cr_port(op)) { struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer); if (peer) { if (peer->nbrp) { @@ -3879,7 +3899,7 @@ sync_pb_for_lrp(struct ovn_port *op, bool always_redirect = !lr_stateful_rec->lrnat_rec->has_distributed_nat && - !l3dgw_port_has_associated_vtep_lports(op->l3dgw_port); + !l3dgw_port_has_associated_vtep_lports(op->primary_port); const char *redirect_type = smap_get(&op->nbrp->options, "redirect-type"); @@ -4374,7 +4394,7 @@ ls_port_reinit(struct ovn_port *op, struct ovsdb_idl_txn *ovnsb_txn, ovn_port_cleanup(op); op->sb = sb; ovn_port_set_nb(op, nbsp, NULL); - op->l3dgw_port = op->cr_port = NULL; + op->primary_port = op->cr_port = NULL; return ls_port_init(op, ovnsb_txn, od, sb, sbrec_mirror_table, sbrec_chassis_table, sbrec_chassis_by_name, sbrec_chassis_by_hostname); @@ -13676,7 +13696,7 @@ build_dhcpv6_reply_flows_for_lrouter_port( struct lflow_ref *lflow_ref) { ovs_assert(op->nbrp); - if (op->l3dgw_port) { + if (is_cr_port(op)) { return; } for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { @@ -17793,9 +17813,11 @@ build_ha_chassis_group_ref_chassis(struct hmapx *lr_groups, * table indicating which chassis the distributed port is bond to. */ static void handle_cr_port_binding_changes(const struct sbrec_port_binding *sb, - struct ovn_port *orp) + struct ovn_port *orp) { - const struct nbrec_logical_router_port *nbrec_lrp = orp->l3dgw_port->nbrp; + ovs_assert(orp->primary_port); + const struct nbrec_logical_router_port *nbrec_lrp + = orp->primary_port->nbrp; if (sb->chassis) { nbrec_logical_router_port_update_status_setkey(nbrec_lrp, diff --git a/northd/northd.h b/northd/northd.h index 940926945e..146139bebc 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -586,13 +586,11 @@ struct ovn_port { /* Logical port multicast data. */ struct mcast_port_info mcast_info; - /* At most one of l3dgw_port and cr_port can be not NULL. */ + /* At most one of primary_port and cr_port can be not NULL. */ - /* This is set to a distributed gateway port if and only if this ovn_port - * is "derived" from it. Otherwise this is set to NULL. The derived - * ovn_port represents the instance of distributed gateway port on the - * gateway chassis.*/ - struct ovn_port *l3dgw_port; + /* If this ovn_port is a derived port, then 'primary_port' points to the + * port from which this ovn_port is derived. */ + struct ovn_port *primary_port; /* This is set to the "derived" chassis-redirect port of this port if and * only if this port is a distributed gateway port. Otherwise this is set From patchwork Thu May 9 18:01:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1933562 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Vb0FW1C8zz20fZ for ; Fri, 10 May 2024 04:01:35 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 4AAF64045A; Thu, 9 May 2024 18:01:33 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Fb5IjwB5M4vC; Thu, 9 May 2024 18:01:28 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 8B91941DCF Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTPS id 8B91941DCF; Thu, 9 May 2024 18:01:28 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7D37AC0DCF; Thu, 9 May 2024 18:01:28 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 4A884C0037 for ; Thu, 9 May 2024 18:01:27 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 6A69D41EA0 for ; Thu, 9 May 2024 18:01:26 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id sYG6KApYc6Uz for ; Thu, 9 May 2024 18:01:23 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.183.197; helo=relay5-d.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp4.osuosl.org 6D676405DE Authentication-Results: smtp4.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 6D676405DE Received: from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net [217.70.183.197]) by smtp4.osuosl.org (Postfix) with ESMTPS id 6D676405DE for ; Thu, 9 May 2024 18:01:22 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id AFC751C0003; Thu, 9 May 2024 18:01:19 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 9 May 2024 14:01:16 -0400 Message-ID: <20240509180116.1464002-1-numans@ovn.org> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240509180015.1463789-1-numans@ovn.org> References: <20240509180015.1463789-1-numans@ovn.org> MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v2 3/3] Add support for overlay provider networks. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique It is expected that a provider network logical switch has a localnet logical switch port in order to bridge the overlay traffic to the underlay traffic. There can be some usecases where a overlay logical switch (without a localnet port) can act as a provider network and presently NAT doesn't work as expected. This patch adds this support. A new config option "overlay_provider_network" is added to support this feature. This feature gets enabled for a logical switch 'P' if: - The above option is set to true in the Logical_Switch.other_config column. - The logical switch 'P' doesn't have any localnet ports. - The logical router port of a router 'R' connecting to 'P' is a gateway router port. - And the logical router 'R' has only one gateway router port. If all the above conditions are met, ovn-northd creates a chassisredirect port for the logical switch port (of type router) connecting to the router 'R'. For example, if the logical port is named as "P-R" and its peer router port is "R-P", then chassisredirect port cr-P-R is created along with cr-R-P. Gateway chassis binding the cr-R-P also binds cr-P-R. This ensures that the routing is centralized on this gateway chassis for the traffic coming from switch "P" towards the router or vice versa. This centralization is required in order to support NAT (both SNAT and DNAT). Distributed NAT (i.e if external_mac and router_port is set) is not supported and instead the router port mac is used for such traffic. Reported-at: https://issues.redhat.com/browse/FDP-364 Signed-off-by: Numan Siddique --- NEWS | 2 + controller/physical.c | 4 + northd/northd.c | 239 ++++++++++++++---- northd/northd.h | 1 + ovn-nb.xml | 27 ++ tests/multinode-macros.at | 2 +- tests/multinode.at | 177 +++++++++++++ tests/ovn-northd.at | 520 +++++++++++++++++++++++++++++++++++++- tests/ovn.at | 8 +- 9 files changed, 928 insertions(+), 52 deletions(-) diff --git a/NEWS b/NEWS index 3b5e93dc9a..faf80c7b72 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Post v24.03.0 external-ids, the option is no longer needed as it became effectively "true" for all scenarios. - Added DHCPv4 relay support. + - Added Overlay provider network support to a logical switch if + the config "overlay_provider_network" is set to true. OVN v24.03.0 - 01 Mar 2024 -------------------------- diff --git a/controller/physical.c b/controller/physical.c index 7ee3086940..625e37e8a7 100644 --- a/controller/physical.c +++ b/controller/physical.c @@ -1587,6 +1587,10 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, ct_zones); put_zones_ofpacts(&zone_ids, ofpacts_p); + /* Clear the MFF_INPORT. Its possible that the same packet may + * go out from the same tunnel inport. */ + put_load(ofp_to_u16(OFPP_NONE), MFF_IN_PORT, 0, 16, ofpacts_p); + /* Resubmit to table 41. */ put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p); } diff --git a/northd/northd.c b/northd/northd.c index 66065db7ff..a8f81e91c0 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -2088,6 +2088,53 @@ parse_lsp_addrs(struct ovn_port *op) } } +static struct ovn_port * +create_cr_port(struct ovn_port *op, struct hmap *ports, + struct ovs_list *both_dbs, struct ovs_list *nb_only) +{ + char *redirect_name = ovn_chassis_redirect_name( + op->nbsp ? op->nbsp->name : op->nbrp->name); + + struct ovn_port *crp = ovn_port_find(ports, redirect_name); + if (crp && crp->sb && crp->sb->datapath == op->od->sb) { + ovn_port_set_nb(crp, NULL, op->nbrp); + ovs_list_remove(&crp->list); + ovs_list_push_back(both_dbs, &crp->list); + } else { + crp = ovn_port_create(ports, redirect_name, + op->nbsp, op->nbrp, NULL); + ovs_list_push_back(nb_only, &crp->list); + } + + crp->primary_port = op; + op->cr_port = crp; + crp->od = op->od; + free(redirect_name); + + return crp; +} + +/* Returns true if chassis resident port needs to be created for + * op's peer logical switch. False otherwise. + * + * Chassis resident port needs to be created if the op's logical router + * - Has only one gateway router port and + * - op's peer logical switch has no localnet ports and + * - op's logical switch has the option 'overlay_provider_network' + * set to true. + */ +static bool +needs_cr_port_creation(struct ovn_port *op) +{ + if (op->od->n_l3dgw_ports == 1 && op->peer && op->peer->nbsp + && !op->peer->od->n_localnet_ports) { + return smap_get_bool(&op->peer->od->nbs->other_config, + "overlay_provider_network", false); + } + + return false; +} + static void join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, struct hmap *ls_datapaths, struct hmap *lr_datapaths, @@ -2195,9 +2242,10 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, tag_alloc_add_existing_tags(tag_alloc_table, nbsp); } } + + struct hmapx gw_ports = HMAPX_INITIALIZER(&gw_ports); HMAP_FOR_EACH (od, key_node, lr_datapaths) { ovs_assert(od->nbr); - size_t n_allocated_l3dgw_ports = 0; for (size_t i = 0; i < od->nbr->n_ports; i++) { const struct nbrec_logical_router_port *nbrp = od->nbr->ports[i]; @@ -2261,10 +2309,7 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, redirect_type && !strcasecmp(redirect_type, "bridged"); } - if (op->nbrp->ha_chassis_group || - op->nbrp->n_gateway_chassis) { - /* Additional "derived" ovn_port crp represents the - * instance of op on the gateway chassis. */ + if (op->nbrp->ha_chassis_group || op->nbrp->n_gateway_chassis) { const char *gw_chassis = smap_get(&op->od->nbr->options, "chassis"); if (gw_chassis) { @@ -2273,34 +2318,9 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, VLOG_WARN_RL(&rl, "Bad configuration: distributed " "gateway port configured on port %s " "on L3 gateway router", nbrp->name); - continue; - } - - char *redirect_name = - ovn_chassis_redirect_name(nbrp->name); - struct ovn_port *crp = ovn_port_find(ports, redirect_name); - if (crp && crp->sb && crp->sb->datapath == od->sb) { - ovn_port_set_nb(crp, NULL, nbrp); - ovs_list_remove(&crp->list); - ovs_list_push_back(both, &crp->list); } else { - crp = ovn_port_create(ports, redirect_name, - NULL, nbrp, NULL); - ovs_list_push_back(nb_only, &crp->list); - } - crp->primary_port = op; - op->cr_port = crp; - crp->od = od; - free(redirect_name); - - /* Add to l3dgw_ports in od, for later use during flow - * creation. */ - if (od->n_l3dgw_ports == n_allocated_l3dgw_ports) { - od->l3dgw_ports = x2nrealloc(od->l3dgw_ports, - &n_allocated_l3dgw_ports, - sizeof *od->l3dgw_ports); + hmapx_add(&gw_ports, op); } - od->l3dgw_ports[od->n_l3dgw_ports++] = op; } } } @@ -2357,12 +2377,6 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, arp_proxy, op->nbsp->name); } } - - /* Only used for the router type LSP whose peer is l3dgw_port */ - if (op->peer && is_l3dgw_port(op->peer)) { - op->enable_router_port_acl = smap_get_bool( - &op->nbsp->options, "enable_router_port_acl", false); - } } else if (op->nbrp && op->nbrp->peer && !is_cr_port(op)) { struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer); if (peer) { @@ -2383,6 +2397,56 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table, } } + struct hmapx_node *hmapx_node; + HMAPX_FOR_EACH (hmapx_node, &gw_ports) { + op = hmapx_node->data; + od = op->od; + ovs_assert(op->nbrp); + ovs_assert(op->nbrp->ha_chassis_group || op->nbrp->n_gateway_chassis); + + /* Additional "derived" ovn_port crp represents the instance of op on + * the gateway chassis. */ + struct ovn_port *crp = create_cr_port(op, ports, both, nb_only); + ovs_assert(crp); + + /* Add to l3dgw_ports in od, for later use during flow creation. */ + if (od->n_l3dgw_ports == od->n_allocated_l3dgw_ports) { + od->l3dgw_ports = x2nrealloc(od->l3dgw_ports, + &od->n_allocated_l3dgw_ports, + sizeof *od->l3dgw_ports); + } + od->l3dgw_ports[od->n_l3dgw_ports++] = op; + + if (op->peer && op->peer->nbsp) { + /* Only used for the router type LSP whose peer is l3dgw_port */ + op->peer->enable_router_port_acl = smap_get_bool( + &op->peer->nbsp->options, "enable_router_port_acl", false); + } + } + + + /* Create chassisresident port for the gateway router port's peer if + * - Gateway router port's router has only one gateway router port and + * - Its peer is a logical switch port and + * - It's peer's logical switch has no localnet ports. + * - Its peer's logical switch has the option overlay_provider_network + * is set to true in the other_config column. + * + * This is required to support NAT via geneve (for the overlay provider + * networks) and the routing coming from this logical switch destined to + * the router port and vice versa is centralized on the gateway chassis. + * + * Future enhancement: Support NAT via geneve if the logical router has + * multiple gateway ports. + * */ + HMAPX_FOR_EACH (hmapx_node, &gw_ports) { + op = hmapx_node->data; + if (needs_cr_port_creation(op)) { + create_cr_port(op->peer, ports, both, nb_only); + } + } + hmapx_destroy(&gw_ports); + /* Wait until all ports have been connected to add to IPAM since * it relies on proper peers to be set */ @@ -3165,16 +3229,28 @@ ovn_port_update_sbrec(struct ovsdb_idl_txn *ovnsb_txn, * type "l3gateway". */ if (chassis) { sbrec_port_binding_set_type(op->sb, "l3gateway"); + } else if (is_cr_port(op)) { + sbrec_port_binding_set_type(op->sb, "chassisredirect"); + ovs_assert(op->primary_port->peer); + ovs_assert(op->primary_port->peer->cr_port); + ovs_assert(op->primary_port->peer->cr_port->sb); + sbrec_port_binding_set_ha_chassis_group( + op->sb, + op->primary_port->peer->cr_port->sb->ha_chassis_group); + } else { sbrec_port_binding_set_type(op->sb, "patch"); } const char *router_port = smap_get(&op->nbsp->options, "router-port"); - if (router_port || chassis) { + if (router_port || chassis || is_cr_port(op)) { struct smap new; smap_init(&new); - if (router_port) { + + if (is_cr_port(op)) { + smap_add(&new, "distributed-port", op->nbsp->name); + } else if (router_port) { smap_add(&new, "peer", router_port); } if (chassis) { @@ -8188,9 +8264,18 @@ build_lswitch_rport_arp_req_flow( struct lflow_ref *lflow_ref) { struct ds match = DS_EMPTY_INITIALIZER; + struct ds m = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; - arp_nd_ns_match(ips, addr_family, &match); + arp_nd_ns_match(ips, addr_family, &m); + ds_clone(&match, &m); + + bool has_cr_port = patch_op->cr_port; + + if (has_cr_port) { + ds_put_format(&match, " && is_chassis_resident(%s)", + patch_op->cr_port->json_key); + } /* Send a the packet to the router pipeline. If the switch has non-router * ports then flood it there as well. @@ -8212,6 +8297,31 @@ build_lswitch_rport_arp_req_flow( lflow_ref); } + 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 (od->n_router_ports != od->nbs->n_ports) { + ds_put_format(&actions, "clone {outport = %s; output; }; " + "outport = \""MC_FLOOD_L2"\"; output;", + patch_op->cr_port->json_key); + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, + priority, ds_cstr(&match), + ds_cstr(&actions), stage_hint, + lflow_ref); + } else { + ds_put_format(&actions, "outport = %s; output;", + patch_op->cr_port->json_key); + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, + priority, ds_cstr(&match), + ds_cstr(&actions), + stage_hint, + lflow_ref); + } + } + + ds_destroy(&m); ds_destroy(&match); ds_destroy(&actions); } @@ -9584,7 +9694,11 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op, struct ds *actions, struct ds *match) { ovs_assert(op->nbsp); - if (lsp_is_external(op->nbsp)) { + + /* Note: A switch port can also have a chassis resident derived port. + * Check if 'op' is a chassis resident dervied port. If so, skip + * adding unicast lookup flows for this port. */ + if (lsp_is_external(op->nbsp) || is_cr_port(op)) { return; } @@ -9596,8 +9710,6 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op, "outport = \""MC_UNKNOWN "\"; output;" : "outport = %s; output;") : debug_drop_action(); - ds_clear(actions); - ds_put_format(actions, action, op->json_key); if (lsp_is_router(op->nbsp) && op->peer && op->peer->nbrp) { /* For ports connected to logical routers add flows to bypass the @@ -9644,14 +9756,35 @@ build_lswitch_ip_unicast_lookup(struct ovn_port *op, if (add_chassis_resident_check) { ds_put_format(match, " && is_chassis_resident(%s)", json_key); } + } else if (op->cr_port) { + ds_clear(actions); + ds_put_format(actions, action, op->cr_port->json_key); + + struct ds m = DS_EMPTY_INITIALIZER; + ds_put_format(&m, "eth.dst == %s && !is_chassis_resident(%s)", + op->peer->lrp_networks.ea_s, + op->cr_port->json_key); + + ovn_lflow_add_with_hint(lflows, op->od, + S_SWITCH_IN_L2_LKUP, 50, + ds_cstr(&m), ds_cstr(actions), + &op->nbsp->header_, + op->lflow_ref); + ds_destroy(&m); + ds_put_format(match, " && is_chassis_resident(%s)", + op->cr_port->json_key); } + ds_clear(actions); + ds_put_format(actions, action, op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50, ds_cstr(match), ds_cstr(actions), &op->nbsp->header_, op->lflow_ref); } else { + ds_clear(actions); + ds_put_format(actions, action, op->json_key); for (size_t i = 0; i < op->n_lsp_addrs; i++) { ds_clear(match); ds_put_format(match, "eth.dst == %s", op->lsp_addrs[i].ea_s); @@ -11749,6 +11882,15 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, return; } + if (op->peer && op->peer->cr_port) { + /* We don't add the below flows if the router port's peer has + * a chassisresident port. That's because routing is centralized on + * the gateway chassis for the traffic from the peer port to this + * router or from this router to the peer logical switch. + */ + return; + } + /* Mac address to use when replying to ARP/NS. */ const char *mac_s = REG_INPORT_ETH_ADDR; struct eth_addr mac; @@ -15138,6 +15280,17 @@ lrouter_check_nat_entry(const struct ovn_datapath *od, /* For distributed router NAT, determine whether this NAT rule * satisfies the conditions for distributed NAT processing. */ *distributed = false; + + /* NAT cannnot be distributed if the gateway port's peer + * has a chassisresident port (and the routing is centralized + * on the gateway chassis for the traffic from the peer + * to this router and traffic to the peer.) + */ + struct ovn_port *l3dgw_port = *nat_l3dgw_port; + if (l3dgw_port && l3dgw_port->peer && l3dgw_port->peer->cr_port) { + return 0; + } + if (od->n_l3dgw_ports && !strcmp(nat->type, "dnat_and_snat") && nat->logical_port && nat->external_mac) { if (eth_addr_from_string(nat->external_mac, mac)) { diff --git a/northd/northd.h b/northd/northd.h index 146139bebc..563e260a35 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -325,6 +325,7 @@ struct ovn_datapath { * will be NULL. */ struct ovn_port **l3dgw_ports; size_t n_l3dgw_ports; + size_t n_allocated_l3dgw_ports; /* router datapath has a logical port with redirect-type set to bridged. */ bool redirect_bridged; diff --git a/ovn-nb.xml b/ovn-nb.xml index 5cb6ba640e..6374724452 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -839,6 +839,33 @@ forwarded to all routers and therefor the mac bindings of the routers are no longer updated. + + +

+ Determines if the logical switch is an overlay provider network or + not. If set to true, then this logical switch can be connected to a + logical router via a gateway router port and NAT options can be + configured on its subnets. In order for OVN to consider this + logical switch as an overlay provider network, following conditions + needs to be satisfied. +

+ +
    +
  • + Logical router port connecting to this logical switch should be + a gateway router port. +
  • + +
  • + The logical router should have only one gateway router port. +
  • + +
  • + The logical switch should not have any localnet ports. +
  • +
+
diff --git a/tests/multinode-macros.at b/tests/multinode-macros.at index c04506a52a..25cfa186ee 100644 --- a/tests/multinode-macros.at +++ b/tests/multinode-macros.at @@ -66,7 +66,7 @@ m_count_rows() { m_check_row_count() { local db=$(parse_db $1) table=$(parse_table $1); shift local count=$1; shift - local found=$(m_count_rows $c $db:$table "$@") + local found=$(m_count_rows $db:$table "$@") echo echo "Checking for $count rows in $db $table${1+ with $*}... found $found" if test "$count" != "$found"; then diff --git a/tests/multinode.at b/tests/multinode.at index b959a25506..d549bedd66 100644 --- a/tests/multinode.at +++ b/tests/multinode.at @@ -890,4 +890,181 @@ M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1150"]) +# Reset back to geneve tunnels +for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1 +do + m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=geneve +done + +AT_CLEANUP + +AT_SETUP([ovn multinode NAT on a provider network with no localnet ports]) + +# Check that ovn-fake-multinode setup is up and running +check_fake_multinode_setup + +# Delete the multinode NB and OVS resources before starting the test. +cleanup_multinode_resources + +check multinode_nbctl ls-add sw0 +check multinode_nbctl lsp-add sw0 sw0-port1 +check multinode_nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 1000::3" +check multinode_nbctl lsp-add sw0 sw0-port2 +check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4" + +m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a +m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a + +m_wait_for_ports_up + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Create the second logical switch with one port +check multinode_nbctl ls-add sw1 +check multinode_nbctl lsp-add sw1 sw1-port1 +check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3" + +# Create a logical router and attach both logical switches +check multinode_nbctl lr-add lr0 +check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64 +check multinode_nbctl lsp-add sw0 sw0-lr0 +check multinode_nbctl lsp-set-type sw0-lr0 router +check multinode_nbctl lsp-set-addresses sw0-lr0 router +check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64 +check multinode_nbctl lsp-add sw1 sw1-lr0 +check multinode_nbctl lsp-set-type sw1-lr0 router +check multinode_nbctl lsp-set-addresses sw1-lr0 router +check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 + +m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a + +# create exteranl connection for N/S traffic +check multinode_nbctl ls-add public +check multinode_nbctl lsp-add public ln-public +check multinode_nbctl lsp-set-type ln-public localnet +check multinode_nbctl lsp-set-addresses ln-public unknown +check multinode_nbctl lsp-set-options ln-public network_name=public + +check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24 +check multinode_nbctl lsp-add public public-lr0 +check multinode_nbctl lsp-set-type public-lr0 router +check multinode_nbctl lsp-set-addresses public-lr0 router +check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public +check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10 + +check multinode_nbctl lr-nat-add lr0 dnat_and_snat 172.20.0.110 10.0.0.3 sw0-port1 30:54:00:00:00:03 +check multinode_nbctl lr-nat-add lr0 dnat_and_snat 172.20.0.120 20.0.0.3 +check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24 +check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24 + +# Create a logical port pub-p1 and bind it in ovn-chassis-1 +check multinode_nbctl lsp-add public public-port1 +check multinode_nbctl lsp-set-addresses public-port1 "60:54:00:00:00:03 172.168.0.50" + +m_as ovn-chassis-1 /data/create_fake_vm.sh public-port1 pubp1 60:54:00:00:00:03 172.20.0.50 24 172.20.0.100 + +check multinode_nbctl --wait=hv sync + +# First do basic ping tests before deleting the localnet port - ln-public. +# Once the localnet port is deleted from public ls, routing for 172.20.0.0/24 +# is centralized on ovn-gw-1. + +# This function checks the North-South traffic. +run_ns_traffic() { + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [arp -d 172.20.0.110], [ignore], [ignore]) + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [arp -d 172.20.0.120], [ignore], [ignore]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.100 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.120 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.50 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-2], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.50 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + # Now ping from pubp1 to 172.20.0.100, 172.20.0.110, 172.20.0.120, 10.0.0.3 and 20.0.0.3 + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.100 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.120 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + M_NS_CHECK_EXEC([ovn-chassis-1], [pubp1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +} + +# Test out the N-S traffic. +run_ns_traffic + +# Delete the localnet port by changing the type of ln-public to VIF port. +check multinode_nbctl --wait=hv lsp-set-type ln-public "" + +# cr-port should not be created for public-lr0 since the option +# overlay_provider_network=true is not yet set for public. +m_check_row_count Port_Binding 0 logical_port=cr-public-lr0 + +# Set the option now. +check multinode_nbctl --wait=hv set logical_switch public other_config:overlay_provider_network=true + +m_check_row_count Port_Binding 1 logical_port=cr-public-lr0 +m_check_column chassisredirect Port_Binding type logical_port=cr-public-lr0 + +# Test out the N-S traffic. +run_ns_traffic + +# Re-add the localnet port +check multinode_nbctl --wait=hv lsp-set-type ln-public localnet + +m_check_row_count Port_Binding 0 logical_port=cr-public-lr0 + +# Test out the N-S traffic. +run_ns_traffic + +# Delete the ln-public port this time. +check multinode_nbctl --wait=hv lsp-del ln-public + +m_check_row_count Port_Binding 1 logical_port=cr-public-lr0 +m_check_column chassisredirect Port_Binding type logical_port=cr-public-lr0 + +# Test out the N-S traffic. +run_ns_traffic + AT_CLEANUP diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 680d966755..6fd391bef2 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -2061,7 +2061,7 @@ match=(inport == "lrp-public" && nd_ns && nd.target == \$${lb_as_v6}), dnl action=(nd_na { eth.src = xreg0[[0..47]]; ip6.src = nd.target; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) ]) -# xreg0[0..47] isn't used anywhere else. +# xreg0[[0..47]] isn't used anywhere else. AT_CHECK([ovn-sbctl lflow-list | grep "xreg0\[[0..47\]]" | grep -vE 'lr_in_admission|lr_in_ip_input'], [1], []) # Test chassis redirect port. @@ -2089,7 +2089,7 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) ]) # Ingress router port is used for ARP reply/NA in lr_in_ip_input. -# xxreg0[0..47] is used unless external_mac is set. +# xxreg0[[0..47]] is used unless external_mac is set. # Priority 90 flows (per router). AT_CHECK_UNQUOTED([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | ovn_strip_lflows], [0], [dnl table=??(lr_in_ip_input ), priority=90 , dnl @@ -2164,7 +2164,7 @@ match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4 && is_chas action=(eth.dst = eth.src; eth.src = 00:00:00:00:00:02; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 00:00:00:00:00:02; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) ]) -# xreg0[0..47] isn't used anywhere else. +# xreg0[[0..47]] isn't used anywhere else. AT_CHECK([ovn-sbctl lflow-list | grep "xreg0\[[0..47\]]" | grep -vE 'lr_in_admission|lr_in_ip_input'], [1], []) AT_CLEANUP @@ -5471,13 +5471,14 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | grep "192.168.4.100" | grep "_MC_flo AS_BOX([Configuring ro1-ls1 router port as a gateway router port]) -ovn-nbctl --wait=sb lrp-set-gateway-chassis ro1-ls1 chassis-1 30 +check ovn-nbctl lrp-set-gateway-chassis ro1-ls1 chassis-1 30 +check ovn-nbctl --wait=sb lsp-add ls1 ln-ls1 -- lsp-set-type ln-ls1 localnet ovn-sbctl lflow-list ls1 > ls1_lflows AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | ovn_strip_lflows], [0], [dnl table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport = get_fdb(eth.dst); next;) table=??(ls_in_l2_lkup ), priority=110 , match=(eth.dst == $svc_monitor_mac && (tcp || icmp || icmp6)), action=(handle_svc_check(inport);) - table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;) + table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:01:01 && is_chassis_resident("cr-ro1-ls1")), action=(outport = "ls1-ro1"; output;) table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:01:02), action=(outport = "vm1"; output;) table=??(ls_in_l2_lkup ), priority=70 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;) @@ -12544,3 +12545,512 @@ check_engine_stats northd recompute nocompute check_engine_stats lflow recompute nocompute AT_CLEANUP + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([NAT on a provider network with no localnet ports]) +AT_KEYWORDS([dnat]) +ovn_start + +check ovn-nbctl -- ls-add sw0 -- ls-add sw1 +check ovn-nbctl lsp-add sw0 sw0-port1 +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +check ovn-nbctl lsp-add sw0 sw0-lr0 +check ovn-nbctl lsp-set-type sw0-lr0 router +check ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01 +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:03 20.0.0.1/24 +check ovn-nbctl lsp-add sw1 sw1-lr0 +check ovn-nbctl lsp-set-type sw1-lr0 router +check ovn-nbctl lsp-set-addresses sw1-lr0 router +check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 + +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 +check ovn-nbctl ls-add public +check ovn-nbctl lsp-add public pub-p1 + +# localnet port +check ovn-nbctl lsp-add public ln-public +check ovn-nbctl lsp-set-type ln-public localnet +check ovn-nbctl lsp-set-addresses ln-public unknown +check ovn-nbctl lsp-set-options ln-public network_name=public + +check ovn-nbctl lrp-add lr0 lr0-public 00:00:00:00:ff:02 172.168.0.10/24 +check ovn-nbctl lrp-set-gateway-chassis lr0-public gw1 + +check ovn-nbctl lsp-add public public-lr0 +check ovn-nbctl lsp-set-type public-lr0 router +check ovn-nbctl lsp-set-addresses public-lr0 router +check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public + +check ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.168.0.110 10.0.0.3 sw0-port1 30:54:00:00:00:03 +check ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.168.0.120 20.0.0.3 +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24 +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 20.0.0.0/24 + +check ovn-nbctl --wait=sb sync + +check_flows_no_cr_port_for_public_lr0() { + # check that there is no port binding cr-public-lr0 + check_row_count Port_Binding 0 logical_port=cr-public-lr0 + + ovn-sbctl dump-flows lr0 > lr0flows + ovn-sbctl dump-flows public > publicflows + +AT_CHECK([grep "lr_in_admission" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_admission ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_admission ), priority=100 , match=(vlan.present || eth.src[[40]]), action=(drop;) + table=??(lr_in_admission ), priority=110 , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;) + table=??(lr_in_admission ), priority=120 , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 00:00:00:00:ff:02 && !is_chassis_resident("cr-lr0-public") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "lr0-public"; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:01 && inport == "lr0-sw0"), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:02 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:03 && inport == "lr0-sw1"), action=(xreg0[[0..47]] = 00:00:00:00:ff:03; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && inport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-public"), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-sw0"), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-sw1"), action=(xreg0[[0..47]] = 00:00:00:00:ff:03; next;) +]) + +AT_CHECK([grep "lr_in_ip_input" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_ip_input ), priority=0 , match=(1), action=(next;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.0.1, 10.0.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {172.168.0.10, 172.168.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {20.0.0.1, 20.0.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src_mcast ||ip4.src == 255.255.255.255 || ip4.src == 127.0.0.0/8 || ip4.dst == 127.0.0.0/8 || ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff01 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff02 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff03 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=120 , match=(inport == "lr0-public" && ip4.src == 172.168.0.100), action=(next;) + table=??(lr_in_ip_input ), priority=30 , match=(ip.ttl == {0, 1}), action=(drop;) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-public" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst <-> ip4.src ; ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-sw0" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.0.0.1 ; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-sw1" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 20.0.0.1 ; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=32 , match=(ip.ttl == {0, 1} && !ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;) + table=??(lr_in_ip_input ), priority=50 , match=(eth.bcast), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {10.0.0.1}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {172.168.0.10}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {20.0.0.1}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff01}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff02}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff03}), action=(drop;) + table=??(lr_in_ip_input ), priority=82 , match=(ip4.mcast || ip6.mcast), action=(drop;) + table=??(lr_in_ip_input ), priority=83 , match=(ip6.mcast_rsvd), action=(drop;) + table=??(lr_in_ip_input ), priority=84 , match=(nd_rs || nd_ra), action=(next;) + table=??(lr_in_ip_input ), priority=85 , match=(arp || nd), action=(drop;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.100), 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.168.0.110), 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.168.0.120), 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 == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.10 && arp.spa == 172.168.0.0/24 && is_chassis_resident("cr-lr0-public")), 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 == "lr0-public" && ip6.dst == {fe80::200:ff:fe00:ff02, ff02::1:ff00:ff02} && nd_ns && nd.target == fe80::200:ff:fe00:ff02 && is_chassis_resident("cr-lr0-public")), 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 == "lr0-sw0" && arp.op == 1 && arp.tpa == 10.0.0.1 && arp.spa == 10.0.0.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 == "lr0-sw0" && ip6.dst == {fe80::200:ff:fe00:ff01, ff02::1:ff00:ff01} && nd_ns && nd.target == fe80::200:ff:fe00:ff01), 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 == "lr0-sw1" && arp.op == 1 && arp.tpa == 20.0.0.1 && arp.spa == 20.0.0.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 == "lr0-sw1" && ip6.dst == {fe80::200:ff:fe00:ff03, ff02::1:ff00:ff03} && nd_ns && nd.target == fe80::200:ff:fe00:ff03), 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=(ip4.dst == 10.0.0.1 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip4.dst == 172.168.0.10 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip4.dst == 20.0.0.1 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff01 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff02 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff03 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.100), action=(drop;) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110), action=(drop;) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120), action=(drop;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.100 && is_chassis_resident("cr-lr0-public")), 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 == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("sw0-port1")), action=(eth.dst = eth.src; eth.src = 30:54:00:00:00:03; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 30:54:00:00:00:03; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-lr0-public")), 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;) +]) + +AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_unsnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.100 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) +]) + +AT_CHECK([grep "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_arp_resolve ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_arp_resolve ), priority=1 , match=(ip4), action=(get_arp(outport, reg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(ip6), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.100), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 30:54:00:00:00:03; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.100), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_arp_resolve ), priority=500 , match=(ip4.mcast || ip6.mcast), action=(next;) +]) + +AT_CHECK([grep "lr_in_gw_redirect" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_gw_redirect ), priority=0 , match=(1), action=(next;) + table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.168.0.110; next;) + table=??(lr_in_gw_redirect ), priority=50 , match=(outport == "lr0-public"), action=(outport = "cr-lr0-public"; next;) +]) + +AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public"), action=(eth.src = 30:54:00:00:00:03; ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.100);) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 20.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.100);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1") && (!ct.trk || !ct.rpl)), action=(eth.src = 30:54:00:00:00:03; ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) +]) + +AT_CHECK([grep "lr_out_egr_loop" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_egr_loop ), priority=0 , match=(1), action=(next;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.100 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) +]) + +AT_CHECK([grep "ls_in_l2_lkup" publicflows | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport = get_fdb(eth.dst); next;) + table=??(ls_in_l2_lkup ), priority=110 , match=(eth.dst == $svc_monitor_mac && (tcp || icmp || icmp6)), action=(handle_svc_check(inport);) + table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:ff:02 && is_chassis_resident("cr-lr0-public")), action=(outport = "public-lr0"; output;) + 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=70 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) + table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:ff:02, 30:54:00:00:00:03} && (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 && arp.op == 1 && arp.tpa == 172.168.0.10), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.100), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:ff02), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([grep -e "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 + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && inport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 30:54:00:00:00:03; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) + table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.168.0.110; next;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.110), 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.168.0.120), 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=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110), action=(drop;) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120), action=(drop;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("sw0-port1")), action=(eth.dst = eth.src; eth.src = 30:54:00:00:00:03; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 30:54:00:00:00:03; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-lr0-public")), 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_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1") && (!ct.trk || !ct.rpl)), action=(eth.src = 30:54:00:00:00:03; ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public"), action=(eth.src = 30:54:00:00:00:03; ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep -e "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} && (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 && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) +} + +check_flows_cr_port_for_public_lr0() { + # check that there is port binding cr-public-lr0 + check_row_count Port_Binding 1 logical_port=cr-public-lr0 + check_column chassisredirect Port_Binding type logical_port=cr-public-lr0 + + ovn-sbctl dump-flows lr0 > lr0flows + ovn-sbctl dump-flows public > publicflows + +AT_CHECK([grep "lr_in_admission" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_admission ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_admission ), priority=100 , match=(vlan.present || eth.src[[40]]), action=(drop;) + table=??(lr_in_admission ), priority=110 , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;) + table=??(lr_in_admission ), priority=120 , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 00:00:00:00:ff:02 && !is_chassis_resident("cr-lr0-public") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "lr0-public"; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:01 && inport == "lr0-sw0"), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:02 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 00:00:00:00:ff:03 && inport == "lr0-sw1"), action=(xreg0[[0..47]] = 00:00:00:00:ff:03; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-public"), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-sw0"), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;) + table=??(lr_in_admission ), priority=50 , match=(eth.mcast && inport == "lr0-sw1"), action=(xreg0[[0..47]] = 00:00:00:00:ff:03; next;) +]) + +AT_CHECK([grep "lr_in_ip_input" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_ip_input ), priority=0 , match=(1), action=(next;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {10.0.0.1, 10.0.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {172.168.0.10, 172.168.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src == {20.0.0.1, 20.0.0.255} && reg9[[0]] == 0), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip4.src_mcast ||ip4.src == 255.255.255.255 || ip4.src == 127.0.0.0/8 || ip4.dst == 127.0.0.0/8 || ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8), action=(drop;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff01 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff02 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=100 , match=(ip6.dst == fe80::200:ff:fe00:ff03 && udp.src == 547 && udp.dst == 546), action=(reg0 = 0; handle_dhcpv6_reply;) + table=??(lr_in_ip_input ), priority=120 , match=(inport == "lr0-public" && ip4.src == 172.168.0.100), action=(next;) + table=??(lr_in_ip_input ), priority=30 , match=(ip.ttl == {0, 1}), action=(drop;) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-public" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst <-> ip4.src ; ip.ttl = 254; outport = "lr0-public"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-sw0" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 10.0.0.1 ; ip.ttl = 254; outport = "lr0-sw0"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=31 , match=(inport == "lr0-sw1" && ip4 && ip.ttl == {0, 1} && !ip.later_frag), action=(icmp4 {eth.dst <-> eth.src; icmp4.type = 11; /* Time exceeded */ icmp4.code = 0; /* TTL exceeded in transit */ ip4.dst = ip4.src; ip4.src = 20.0.0.1 ; ip.ttl = 254; outport = "lr0-sw1"; flags.loopback = 1; output; };) + table=??(lr_in_ip_input ), priority=32 , match=(ip.ttl == {0, 1} && !ip.later_frag && (ip4.mcast || ip6.mcast)), action=(drop;) + table=??(lr_in_ip_input ), priority=50 , match=(eth.bcast), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {10.0.0.1}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {172.168.0.10}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip4.dst == {20.0.0.1}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff01}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff02}), action=(drop;) + table=??(lr_in_ip_input ), priority=60 , match=(ip6.dst == {fe80::200:ff:fe00:ff03}), action=(drop;) + table=??(lr_in_ip_input ), priority=82 , match=(ip4.mcast || ip6.mcast), action=(drop;) + table=??(lr_in_ip_input ), priority=83 , match=(ip6.mcast_rsvd), action=(drop;) + table=??(lr_in_ip_input ), priority=84 , match=(nd_rs || nd_ra), action=(next;) + table=??(lr_in_ip_input ), priority=85 , match=(arp || nd), action=(drop;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.100), 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.168.0.110), 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.168.0.120), 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 == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.10 && arp.spa == 172.168.0.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 == "lr0-public" && ip6.dst == {fe80::200:ff:fe00:ff02, ff02::1:ff00:ff02} && nd_ns && nd.target == fe80::200:ff:fe00:ff02 && is_chassis_resident("cr-lr0-public")), 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 == "lr0-sw0" && arp.op == 1 && arp.tpa == 10.0.0.1 && arp.spa == 10.0.0.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 == "lr0-sw0" && ip6.dst == {fe80::200:ff:fe00:ff01, ff02::1:ff00:ff01} && nd_ns && nd.target == fe80::200:ff:fe00:ff01), 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 == "lr0-sw1" && arp.op == 1 && arp.tpa == 20.0.0.1 && arp.spa == 20.0.0.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 == "lr0-sw1" && ip6.dst == {fe80::200:ff:fe00:ff03, ff02::1:ff00:ff03} && nd_ns && nd.target == fe80::200:ff:fe00:ff03), 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=(ip4.dst == 10.0.0.1 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip4.dst == 172.168.0.10 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip4.dst == 20.0.0.1 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff01 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff02 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) + table=??(lr_in_ip_input ), priority=90 , match=(ip6.dst == fe80::200:ff:fe00:ff03 && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; ) +]) + +AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_unsnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.100 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) +]) + +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_defrag ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_dnat ), priority=0 , match=(1), action=(next;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) +]) + +AT_CHECK([grep "lr_in_arp_resolve" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_arp_resolve ), priority=0 , match=(1), action=(drop;) + table=??(lr_in_arp_resolve ), priority=1 , match=(ip4), action=(get_arp(outport, reg0); next;) + table=??(lr_in_arp_resolve ), priority=1 , match=(ip6), action=(get_nd(outport, xxreg0); next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.100), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.100), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_arp_resolve ), priority=500 , match=(ip4.mcast || ip6.mcast), action=(next;) +]) + +AT_CHECK([grep "lr_in_gw_redirect" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_in_gw_redirect ), priority=0 , match=(1), action=(next;) + table=??(lr_in_gw_redirect ), priority=50 , match=(outport == "lr0-public"), action=(outport = "cr-lr0-public"; next;) +]) + +AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_undnat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_post_undnat ), priority=0 , match=(1), action=(next;) +]) + +AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_snat ), priority=0 , match=(1), action=(next;) + table=??(lr_out_snat ), priority=120 , match=(nd_ns), action=(next;) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.100);) + table=??(lr_out_snat ), priority=153 , match=(ip && ip4.src == 20.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.100);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) +]) + +AT_CHECK([grep "lr_out_egr_loop" lr0flows | ovn_strip_lflows], [0], [dnl + table=??(lr_out_egr_loop ), priority=0 , match=(1), action=(next;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.100 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) +]) + +AT_CHECK([grep "ls_in_l2_lkup" publicflows | ovn_strip_lflows], [0], [dnl + table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport = get_fdb(eth.dst); next;) + table=??(ls_in_l2_lkup ), priority=110 , match=(eth.dst == $svc_monitor_mac && (tcp || icmp || icmp6)), action=(handle_svc_check(inport);) + table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:ff:02 && !is_chassis_resident("cr-public-lr0")), action=(outport = "cr-public-lr0"; output;) + table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:ff:02 && is_chassis_resident("cr-public-lr0")), action=(outport = "public-lr0"; output;) + 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=70 , match=(eth.mcast), action=(outport = "_MC_flood"; output;) + table=??(ls_in_l2_lkup ), priority=75 , match=(eth.src == {00:00:00:00:ff:02, 30:54:00:00:00:03} && (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 && arp.op == 1 && arp.tpa == 172.168.0.10 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.10 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.100 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.100 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.110 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:ff02 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:ff02 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) + +AT_CHECK([grep -e "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 + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.110), 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.168.0.120), 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_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep -e "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} && (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 && arp.op == 1 && arp.tpa == 172.168.0.110 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120 && !is_chassis_resident("cr-public-lr0")), action=(clone {outport = "cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) +} + +# Check that the lflows are as expected when public has localnet port. +check_flows_no_cr_port_for_public_lr0 + +# Remove the localnet port from public logical switch. +check ovn-nbctl --wait=sb lsp-set-type ln-public "" + +# Check that the lflows are as expected and there is no cr port +# created for "public-lr0" when public has no localnet port +# since public doesn't have the option "overlay_provider_network=true" +# set. +check_row_count Port_Binding 0 logical_port=cr-public-lr0 + +ovn-sbctl dump-flows lr0 > lr0flows +ovn-sbctl dump-flows public > publicflows + +AT_CHECK([grep -e "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 + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && inport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 30:54:00:00:00:03; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) + table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.168.0.110; next;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.110), 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.168.0.120), 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=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110), action=(drop;) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120), action=(drop;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("sw0-port1")), action=(eth.dst = eth.src; eth.src = 30:54:00:00:00:03; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 30:54:00:00:00:03; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-lr0-public")), 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_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1") && (!ct.trk || !ct.rpl)), action=(eth.src = 30:54:00:00:00:03; ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public"), action=(eth.src = 30:54:00:00:00:03; ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep -e "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} && (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 && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) + + +# Set the option "overlay_provider_network=true" for public. +check ovn-nbctl --wait=sb set logical_switch public other_config:overlay_provider_network=true + +# Check that the lflows are as expected and there is cr port created for public-lr0. +check_flows_cr_port_for_public_lr0 + +# Set the type of ln-public back to localnet +check ovn-nbctl --wait=sb lsp-set-type ln-public localnet + +# Check that the lflows are as expected when public has localnet port. +check_flows_no_cr_port_for_public_lr0 + +# Delete the localnet port +check ovn-nbctl --wait=sb lsp-del ln-public + +# Check that the lflows are as expected when public has no localnet port. +check_flows_cr_port_for_public_lr0 + +# Create multiple gateway ports. chassisresident port should not be +# created for 'public-lr0' even if there is no localnet port on 'public' +# logical switch. +check ovn-nbctl --wait=sb lrp-set-gateway-chassis lr0-sw0 gw1 +# check that there is no port binding cr-public-lr0 +check_row_count Port_Binding 0 logical_port=cr-public-lr0 + +ovn-sbctl dump-flows lr0 > lr0flows +ovn-sbctl dump-flows public > publicflows + +AT_CHECK([grep -e "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 + table=??(lr_in_admission ), priority=50 , match=(eth.dst == 30:54:00:00:00:03 && inport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(xreg0[[0..47]] = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.110), action=(eth.dst = 30:54:00:00:00:03; next;) + table=??(lr_in_arp_resolve ), priority=100 , match=(outport == "lr0-public" && reg0 == 172.168.0.120), action=(eth.dst = 00:00:00:00:ff:02; next;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.110), action=(drop;) + table=??(lr_in_arp_resolve ), priority=150 , match=(inport == "lr0-public" && outport == "lr0-public" && ip4.dst == 172.168.0.120), action=(drop;) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_dnat(10.0.0.3);) + table=??(lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat(20.0.0.3);) + table=??(lr_in_gw_redirect ), priority=100 , match=(ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(eth.src = 30:54:00:00:00:03; reg1 = 172.168.0.110; next;) + table=??(lr_in_ip_input ), priority=90 , match=(arp.op == 1 && arp.tpa == 172.168.0.110), 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.168.0.120), 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=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110), action=(drop;) + table=??(lr_in_ip_input ), priority=91 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120), action=(drop;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.110 && is_chassis_resident("sw0-port1")), action=(eth.dst = eth.src; eth.src = 30:54:00:00:00:03; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 30:54:00:00:00:03; arp.tpa <-> arp.spa; outport = inport; flags.loopback = 1; output;) + table=??(lr_in_ip_input ), priority=92 , match=(inport == "lr0-public" && arp.op == 1 && arp.tpa == 172.168.0.120 && is_chassis_resident("cr-lr0-public")), 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_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.110 && inport == "lr0-public"), action=(ct_snat;) + table=??(lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.168.0.120 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.110 && outport == "lr0-public" && is_chassis_resident("sw0-port1")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_egr_loop ), priority=100 , match=(ip4.dst == 172.168.0.120 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(clone { ct_clear; inport = outport; outport = ""; eth.dst <-> eth.src; flags = 0; flags.loopback = 1; reg0 = 0; reg1 = 0; reg2 = 0; reg3 = 0; reg4 = 0; reg5 = 0; reg6 = 0; reg7 = 0; reg8 = 0; reg9 = 0; reg9[[0]] = 1; next(pipeline=ingress, table=??); };) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("sw0-port1") && (!ct.trk || !ct.rpl)), action=(eth.src = 30:54:00:00:00:03; ct_snat(172.168.0.110);) + table=??(lr_out_snat ), priority=161 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.120);) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 10.0.0.3 && outport == "lr0-public"), action=(eth.src = 30:54:00:00:00:03; ct_dnat;) + table=??(lr_out_undnat ), priority=100 , match=(ip && ip4.src == 20.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;) +]) + +AT_CHECK([grep -e "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} && (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 && arp.op == 1 && arp.tpa == 172.168.0.110), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) + table=??(ls_in_l2_lkup ), priority=80 , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 172.168.0.120), action=(clone {outport = "public-lr0"; output; }; outport = "_MC_flood_l2"; output;) +]) + +AT_CLEANUP +]) diff --git a/tests/ovn.at b/tests/ovn.at index 9c173f6cff..62b8e629f1 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -21215,10 +21215,10 @@ ovn-nbctl lsp-add sw0 rp-sw0 -- set Logical_Switch_Port rp-sw0 \ type=router options:router-port=sw0 \ -- lsp-set-addresses rp-sw0 router -ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 \ - -- lrp-set-gateway-chassis sw1 hv2 +ovn-nbctl lrp-add lr0 lr0-sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 \ + -- lrp-set-gateway-chassis lr0-sw1 hv2 ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \ - type=router options:router-port=sw1 \ + type=router options:router-port=lr0-sw1 \ -- lsp-set-addresses rp-sw1 router ovn-nbctl lsp-add sw0 sw0-p0 \ @@ -21230,6 +21230,8 @@ ovn-nbctl lsp-add sw0 sw0-p1 \ ovn-nbctl lsp-add sw1 sw1-p0 \ -- lsp-set-addresses sw1-p0 unknown +check ovn-nbctl lsp-add sw1 ln-sw1 -- lsp-set-type ln-sw1 localnet + ovn-nbctl lr-nat-add lr0 snat 172.16.1.1 192.168.1.0/24 ovn-nbctl lr-nat-add lr0 snat 2002::1 2001::/64