From patchwork Mon Aug 10 10:53:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342788 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=jMRdYLn3; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCWH0CHGz9sPB for ; Mon, 10 Aug 2020 20:53:46 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id F315286457; Mon, 10 Aug 2020 10:53:44 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id OcfHOymIWVNl; Mon, 10 Aug 2020 10:53:43 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 9836686490; Mon, 10 Aug 2020 10:53:43 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7C2DAC013C; Mon, 10 Aug 2020 10:53:43 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 30B01C013C for ; Mon, 10 Aug 2020 10:53:41 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 1D53087C69 for ; Mon, 10 Aug 2020 10:53:41 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id W9yuW9t1eLFt for ; Mon, 10 Aug 2020 10:53:39 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) by hemlock.osuosl.org (Postfix) with ESMTPS id 82C04885D1 for ; Mon, 10 Aug 2020 10:53:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056818; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tLVrO0j3AUyLAZd/c8MZAixOcqHYzlGoRS5BdnFAeEA=; b=jMRdYLn3krZO1fLJgkc4tSCD1lu+ZgfTbemA/KflNN94HOMj7mwYOViVNld/Usliael5sq WZnYZhz4ikkSHW77jso7kHJTfemn4xLz09oTUsn3Q5YZgU5GUwDOtKvHGLb0VAfK9WehYk hzCOHn9BJzuDlGPBLD9AFkCvOEUFlR4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-392-oTaFvoaOM-ym2ZdbfYET_g-1; Mon, 10 Aug 2020 06:53:35 -0400 X-MC-Unique: oTaFvoaOM-ym2ZdbfYET_g-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 68FF01005504 for ; Mon, 10 Aug 2020 10:53:34 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id AEE9760BE2 for ; Mon, 10 Aug 2020 10:53:33 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:53:30 +0200 Message-Id: <20200810105329.25272.10504.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 1/7] ovn-northd: Document OVS register usage in logical 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" Also, use macros instead of bare references to register names. Acked-by: Han Zhou Signed-off-by: Dumitru Ceara (cherry-picked from master commit 47149a672c2b4a2439f9d70a4a5865480e3b0020) --- northd/ovn-northd.c | 161 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 48 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 5c13e25..a1448dd 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -226,8 +226,58 @@ enum ovn_stage { #define REG_ECMP_GROUP_ID "reg8[0..15]" #define REG_ECMP_MEMBER_ID "reg8[16..31]" +/* Registers used for routing. */ +#define REG_NEXT_HOP_IPV4 "reg0" +#define REG_NEXT_HOP_IPV6 "xxreg0" +#define REG_SRC_IPV4 "reg1" +#define REG_SRC_IPV6 "xxreg1" + #define FLAGBIT_NOT_VXLAN "flags[1] == 0" +/* + * OVS register usage: + * + * Logical Switch pipeline: + * +---------+-------------------------------------+ + * | R0 | REGBIT_{CONNTRACK/DHCP/DNS/HAIRPIN} | + * +---------+-------------------------------------+ + * | R1 - R9 | UNUSED | + * +---------+-------------------------------------+ + * + * Logical Router pipeline: + * +-----+--------------------------+---+---------------+ + * | R0 | REGBIT_ND_RA_OPTS_RESULT | | | + * | | (= IN_ND_RA_OPTIONS) | | | + * | | NEXT_HOP_IPV4 | X | | + * | | (>= IP_INPUT) | X | | + * +-----+--------------------------+ R | | + * | R1 | SRC_IPV4 for ARP-REQ | E | NEXT_HOP_IPV6 | + * | | (>= IP_INPUT) | G | (>= IP_INPUT) | + * +-----+--------------------------+ 0 | | + * | R2 | UNUSED | | | + * +-----+--------------------------+ | | + * | R3 | UNUSED | | | + * +-----+--------------------------+---+---------------+ + * | R4 | UNUSED | | | + * +-----+--------------------------+ X | | + * | R5 | UNUSED | X |SRC_IPV6 for NS| + * +-----+--------------------------+ R | (>= IP_INPUT) | + * | R6 | UNUSED | E | | + * +-----+--------------------------+ G | | + * | R7 | UNUSED | 1 | | + * +-----+--------------------------+---+---------------+ + * | R8 | ECMP_GROUP_ID | + * | | ECMP_MEMBER_ID | + * +-----+--------------------------+ + * | | REGBIT_{ | + * | | EGRESS_LOOPBACK/ | + * | R9 | PKT_LARGER/ | + * | | LOOKUP_NEIGHBOR_RESULT/| + * | | SKIP_LOOKUP_NEIGHBOR} | + * +-----+--------------------------+ + * + */ + /* Returns an "enum ovn_stage" built from the arguments. */ static enum ovn_stage ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline, @@ -7138,15 +7188,15 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, return; } bool is_ipv4 = strchr(rule->nexthop, '.') ? true : false; - ds_put_format(&actions, "%sreg0 = %s; " - "%sreg1 = %s; " + ds_put_format(&actions, "%s = %s; " + "%s = %s; " "eth.src = %s; " "outport = %s; " "flags.loopback = 1; " "next;", - is_ipv4 ? "" : "xx", + is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6, rule->nexthop, - is_ipv4 ? "" : "xx", + is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, out_port->lrp_networks.ea_s, out_port->json_key); @@ -7532,14 +7582,14 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, REG_ECMP_MEMBER_ID" == %"PRIu16, eg->id, er->id); ds_clear(&actions); - ds_put_format(&actions, "%sreg0 = %s; " - "%sreg1 = %s; " + ds_put_format(&actions, "%s = %s; " + "%s = %s; " "eth.src = %s; " "outport = %s; " "next;", - is_ipv4 ? "" : "xx", + is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6, route->nexthop, - is_ipv4 ? "" : "xx", + is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, out_port->lrp_networks.ea_s, out_port->json_key); @@ -7574,8 +7624,8 @@ add_route(struct hmap *lflows, const struct ovn_port *op, &match, &priority); struct ds actions = DS_EMPTY_INITIALIZER; - ds_put_format(&actions, "ip.ttl--; "REG_ECMP_GROUP_ID" = 0; %sreg0 = ", - is_ipv4 ? "" : "xx"); + ds_put_format(&actions, "ip.ttl--; "REG_ECMP_GROUP_ID" = 0; %s = ", + is_ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6); if (gateway) { ds_put_cstr(&actions, gateway); @@ -7583,12 +7633,12 @@ add_route(struct hmap *lflows, const struct ovn_port *op, ds_put_format(&actions, "ip%s.dst", is_ipv4 ? "4" : "6"); } ds_put_format(&actions, "; " - "%sreg1 = %s; " + "%s = %s; " "eth.src = %s; " "outport = %s; " "flags.loopback = 1; " "next;", - is_ipv4 ? "" : "xx", + is_ipv4 ? REG_SRC_IPV4 : REG_SRC_IPV6, lrp_addr_s, op->lrp_networks.ea_s, op->json_key); @@ -9059,7 +9109,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_put_format( &match, "outport == %s && %s == %s", od->l3dgw_port->json_key, - is_v6 ? "xxreg0" : "reg0", nat->external_ip); + is_v6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4, + nat->external_ip); ds_clear(&actions); ds_put_format( &actions, "eth.dst = %s; next;", @@ -9221,9 +9272,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * router, add flows that are specific to a NAT rule. These * flows indicate the presence of an applicable NAT rule that * can be applied in a distributed manner. - * In particulr reg1 and eth.src are set to NAT external IP and - * NAT external mac so the ARP request generated in the following - * stage is sent out with proper IP/MAC src addresses + * In particulr REG_SRC_IPV4/REG_SRC_IPV6 and eth.src are set to + * NAT external IP and NAT external mac so the ARP request + * generated in the following stage is sent out with proper IP/MAC + * src addresses. */ if (distributed) { ds_clear(&match); @@ -9233,8 +9285,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "is_chassis_resident(\"%s\")", is_v6 ? "6" : "4", nat->logical_ip, od->l3dgw_port->json_key, nat->logical_port); - ds_put_format(&actions, "eth.src = %s; %sreg1 = %s; next;", - nat->external_mac, is_v6 ? "xx" : "", + ds_put_format(&actions, "eth.src = %s; %s = %s; next;", + nat->external_mac, + is_v6 ? REG_SRC_IPV6 : REG_SRC_IPV4, nat->external_ip); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100, ds_cstr(&match), @@ -9556,14 +9609,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * * For regular routes without ECMP, table IP_ROUTING sets outport to the * correct output port, eth.src to the output port's MAC address, and - * '[xx]reg0' to the next-hop IP address (leaving 'ip[46].dst', the - * packet’s final destination, unchanged), and advances to the next table. + * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 to the next-hop IP address + * (leaving 'ip[46].dst', the packet’s final destination, unchanged), and + * advances to the next table. * * For ECMP routes, i.e. multiple routes with same policy and prefix, table * IP_ROUTING remembers ECMP group id and selects a member id, and advances - * to table IP_ROUTING_ECMP, which sets outport, eth.src and '[xx]reg0' for - * the selected ECMP member. - * */ + * to table IP_ROUTING_ECMP, which sets outport, eth.src and + * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 for the selected ECMP member. + */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbrp) { continue; @@ -9700,8 +9754,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * permitted/denied/rerouted to the address in the rule's nexthop. * This table sets outport to the correct out_port, * eth.src to the output port's MAC address, - * and '[xx]reg0' to the next-hop IP address (leaving - * 'ip[46].dst', the packet’s final destination, unchanged), and + * and REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 to the next-hop IP address + * (leaving 'ip[46].dst', the packet’s final destination, unchanged), and * advances to the next table for ARP/ND resolution. */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbr) { @@ -9738,9 +9792,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, /* Local router ingress table ARP_RESOLVE: ARP Resolution. * * Any unicast packet that reaches this table is an IP packet whose - * next-hop IP address is in reg0. (ip4.dst is the final destination.) - * This table resolves the IP address in reg0 into an output port in - * outport and an Ethernet address in eth.dst. + * next-hop IP address is in REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 + * (ip4.dst/ipv6.dst is the final destination). + * This table resolves the IP address in + * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 into an output port in outport and + * an Ethernet address in eth.dst. */ HMAP_FOR_EACH (op, key_node, ports) { if (op->nbsp && !lsp_is_enabled(op->nbsp)) { @@ -9749,17 +9805,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, if (op->nbrp) { /* This is a logical router port. If next-hop IP address in - * '[xx]reg0' matches IP address of this router port, then - * the packet is intended to eventually be sent to this - * logical port. Set the destination mac address using this - * port's mac address. + * REG_NEXT_HOP_IPV4/REG_NEXT_HOP_IPV6 matches IP address of this + * router port, then the packet is intended to eventually be sent + * to this logical port. Set the destination mac address using + * this port's mac address. * * The packet is still in peer's logical pipeline. So the match * should be on peer's outport. */ if (op->peer && op->nbrp->peer) { if (op->lrp_networks.n_ipv4_addrs) { ds_clear(&match); - ds_put_format(&match, "outport == %s && reg0 == ", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV4 "== ", op->peer->json_key); op_put_v4_networks(&match, op, false); @@ -9774,7 +9831,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, if (op->lrp_networks.n_ipv6_addrs) { ds_clear(&match); - ds_put_format(&match, "outport == %s && xxreg0 == ", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV6 " == ", op->peer->json_key); op_put_v6_networks(&match, op); @@ -9845,7 +9903,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } ds_clear(&match); - ds_put_format(&match, "outport == %s && reg0 == %s", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV4 " == %s", peer->json_key, ip_s); ds_clear(&actions); @@ -9881,7 +9940,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } ds_clear(&match); - ds_put_format(&match, "outport == %s && xxreg0 == %s", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV6 " == %s", peer->json_key, ip_s); ds_clear(&actions); @@ -9932,8 +9992,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, if (find_lrp_member_ip(peer, vip)) { ds_clear(&match); - ds_put_format(&match, "outport == %s && reg0 == %s", - peer->json_key, vip); + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV4 " == %s", + peer->json_key, vip); ds_clear(&actions); ds_put_format(&actions, @@ -9978,8 +10039,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } ds_clear(&match); - ds_put_format(&match, "outport == %s && reg0 == %s", - peer->json_key, vip); + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV4 " == %s", + peer->json_key, vip); ds_clear(&actions); ds_put_format(&actions, "eth.dst = %s; next;", ea_s); @@ -10033,7 +10095,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, if (router_port->lrp_networks.n_ipv4_addrs) { ds_clear(&match); - ds_put_format(&match, "outport == %s && reg0 == ", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV4 " == ", peer->json_key); op_put_v4_networks(&match, router_port, false); @@ -10048,7 +10111,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, if (router_port->lrp_networks.n_ipv6_addrs) { ds_clear(&match); - ds_put_format(&match, "outport == %s && xxreg0 == ", + ds_put_format(&match, "outport == %s && " + REG_NEXT_HOP_IPV6 " == ", peer->json_key); op_put_v6_networks(&match, router_port); @@ -10070,10 +10134,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip4", - "get_arp(outport, reg0); next;"); + "get_arp(outport, " REG_NEXT_HOP_IPV4 "); next;"); ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip6", - "get_nd(outport, xxreg0); next;"); + "get_nd(outport, " REG_NEXT_HOP_IPV6 "); next;"); } /* Local router ingress table CHK_PKT_LEN: Check packet length. @@ -10219,7 +10283,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, ds_clear(&match); ds_put_format(&match, "eth.dst == 00:00:00:00:00:00 && " - "ip6 && xxreg0 == %s", route->nexthop); + "ip6 && " REG_NEXT_HOP_IPV6 " == %s", + route->nexthop); struct in6_addr sn_addr; struct eth_addr eth_dst; in6_addr_solicited_node(&sn_addr, &gw_ip6); @@ -10247,15 +10312,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "eth.dst == 00:00:00:00:00:00 && ip4", "arp { " "eth.dst = ff:ff:ff:ff:ff:ff; " - "arp.spa = reg1; " - "arp.tpa = reg0; " + "arp.spa = " REG_SRC_IPV4 "; " + "arp.tpa = " REG_NEXT_HOP_IPV4 "; " "arp.op = 1; " /* ARP request */ "output; " "};"); ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, "eth.dst == 00:00:00:00:00:00 && ip6", "nd_ns { " - "nd.target = xxreg0; " + "nd.target = " REG_NEXT_HOP_IPV6 "; " "output; " "};"); ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;"); From patchwork Mon Aug 10 10:53:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342789 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.133; helo=hemlock.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=MW6A1dBP; dkim-atps=neutral Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCWm6BKSz9sPB for ; Mon, 10 Aug 2020 20:54:12 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id C32D4887DE; Mon, 10 Aug 2020 10:54:10 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id mB0aXKtGA-Cl; Mon, 10 Aug 2020 10:54:08 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by hemlock.osuosl.org (Postfix) with ESMTP id 89EE987C69; Mon, 10 Aug 2020 10:54:08 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7CF2FC013C; Mon, 10 Aug 2020 10:54:08 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id CA53EC004D for ; Mon, 10 Aug 2020 10:54:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id B51F489059 for ; Mon, 10 Aug 2020 10:54:07 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bmFjdBaxoxbn for ; Mon, 10 Aug 2020 10:54:05 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.81]) by whitealder.osuosl.org (Postfix) with ESMTPS id E1BC689040 for ; Mon, 10 Aug 2020 10:54:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056843; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LrC2eSQGdc4GP7ZoSWwL0/LvHsxCkjsZ7rZbra7PmOU=; b=MW6A1dBP7RE2pN5McnA8I408BMznBvJBP5OYJOPw2tj2H8aQNeAu+Mvp/igLSgKZcPXpl4 P7DhqSkUThcsJhrcCyn7cjqitOk9mcoKVd3rIVZ2bu+2PnDKDqjtSaKI9TUUd0kkxkHkGD p4Im5iLo7HWgGzWEEiqrAL17RwumDwQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-61-1Ax-O24VPuqqjc9LUYx9OQ-1; Mon, 10 Aug 2020 06:53:45 -0400 X-MC-Unique: 1Ax-O24VPuqqjc9LUYx9OQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 85EF6107ACCA for ; Mon, 10 Aug 2020 10:53:44 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9DC7A10013C2 for ; Mon, 10 Aug 2020 10:53:43 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:53:40 +0200 Message-Id: <20200810105339.25272.5409.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 2/7] ovn-northd: Store ETH address of router inport in xreg0. 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" This helps simplifying logical flows that need to use the port's configured ETH address: - ARP responders for owned IPs - NS responders for owned IPs Acked-by: Han Zhou Signed-off-by: Dumitru Ceara (cherry-picked from master commit fa079165e49cb6694717bfc67456167076e19b66) --- northd/ovn-northd.8.xml | 22 ++++--- northd/ovn-northd.c | 153 +++++++++++++++++++++++++---------------------- tests/ovn-northd.at | 140 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 80 deletions(-) diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index 081536a..16f586d 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1459,7 +1459,9 @@ output; For each enabled router port P with Ethernet address E, a priority-50 flow that matches inport == P && (eth.mcast || eth.dst == - E), with action next;. + E), stores the router port ethernet address + and advances to next table, with action + xreg0[0..47]=E; next;.

@@ -1479,7 +1481,7 @@ output; a priority-50 flow that matches inport == GW && eth.dst == E, where GW is the logical router gateway port, with action - next;. + xreg0[0..47]=E; next;.

@@ -1742,10 +1744,10 @@ next;

 eth.dst = eth.src;
-eth.src = E;
+eth.src = xreg0[0..47];
 arp.op = 2; /* ARP reply. */
 arp.tha = arp.sha;
-arp.sha = E;
+arp.sha = xreg0[0..47];
 arp.tpa = arp.spa;
 arp.spa = A;
 outport = P;
@@ -1794,10 +1796,10 @@ output;
 
         
 nd_na_router {
-    eth.src = E;
+    eth.src = xreg0[0..47];
     ip6.src = A;
     nd.target = A;
-    nd.tll = E;
+    nd.tll = xreg0[0..47];
     outport = inport;
     flags.loopback = 1;
     output;
@@ -1834,10 +1836,10 @@ nd_na_router {
 
         
 eth.dst = eth.src;
-eth.src = E;
+eth.src = xreg0[0..47];
 arp.op = 2; /* ARP reply. */
 arp.tha = arp.sha;
-arp.sha = E;
+arp.sha = xreg0[0..47];
 arp.tpa = arp.spa;
 arp.spa = A;
 outport = P;
@@ -1866,8 +1868,8 @@ output;
         
 eth.dst = eth.src;
 nd_na {
-    eth.src = E;
-    nd.tll = E;
+    eth.src = xreg0[0..47];
+    nd.tll = xreg0[0..47];
     ip6.src = A;
     nd.target = A;
     outport = P;
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index a1448dd..6a28864 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -222,6 +222,11 @@ enum ovn_stage {
 #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]"
 #define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[3]"
 
+/* Register to store the eth address associated to a router port for packets
+ * received in S_ROUTER_IN_ADMISSION.
+ */
+#define REG_INPORT_ETH_ADDR "xreg0[0..47]"
+
 /* Register for ECMP bucket selection. */
 #define REG_ECMP_GROUP_ID       "reg8[0..15]"
 #define REG_ECMP_MEMBER_ID      "reg8[16..31]"
@@ -245,36 +250,42 @@ enum ovn_stage {
  * +---------+-------------------------------------+
  *
  * Logical Router pipeline:
- * +-----+--------------------------+---+---------------+
- * | R0  | REGBIT_ND_RA_OPTS_RESULT |   |               |
- * |     |   (= IN_ND_RA_OPTIONS)   |   |               |
- * |     |      NEXT_HOP_IPV4       | X |               |
- * |     |      (>= IP_INPUT)       | X |               |
- * +-----+--------------------------+ R |               |
- * | R1  |   SRC_IPV4 for ARP-REQ   | E | NEXT_HOP_IPV6 |
- * |     |      (>= IP_INPUT)       | G | (>= IP_INPUT) |
- * +-----+--------------------------+ 0 |               |
- * | R2  |        UNUSED            |   |               |
- * +-----+--------------------------+   |               |
- * | R3  |        UNUSED            |   |               |
- * +-----+--------------------------+---+---------------+
- * | R4  |        UNUSED            |   |               |
- * +-----+--------------------------+ X |               |
- * | R5  |        UNUSED            | X |SRC_IPV6 for NS|
- * +-----+--------------------------+ R | (>= IP_INPUT) |
- * | R6  |        UNUSED            | E |               |
- * +-----+--------------------------+ G |               |
- * | R7  |        UNUSED            | 1 |               |
- * +-----+--------------------------+---+---------------+
- * | R8  |     ECMP_GROUP_ID        |
- * |     |     ECMP_MEMBER_ID       |
- * +-----+--------------------------+
- * |     | REGBIT_{                 |
- * |     |   EGRESS_LOOPBACK/       |
- * | R9  |   PKT_LARGER/            |
- * |     |   LOOKUP_NEIGHBOR_RESULT/|
- * |     |   SKIP_LOOKUP_NEIGHBOR}  |
- * +-----+--------------------------+
+ * +-----+--------------------------+---+-----------------+---+---------------+
+ * | R0  | REGBIT_ND_RA_OPTS_RESULT |   |                 |   |               |
+ * |     |   (= IN_ND_RA_OPTIONS)   | X |                 |   |               |
+ * |     |      NEXT_HOP_IPV4       | R |                 |   |               |
+ * |     |      (>= IP_INPUT)       | E | INPORT_ETH_ADDR | X |               |
+ * +-----+--------------------------+ G |   (< IP_INPUT)  | X |               |
+ * | R1  |   SRC_IPV4 for ARP-REQ   | 0 |                 | R |               |
+ * |     |      (>= IP_INPUT)       |   |                 | E | NEXT_HOP_IPV6 |
+ * +-----+--------------------------+---+-----------------+ G | (>= IP_INPUT) |
+ * | R2  |        UNUSED            | X |                 | 0 |               |
+ * |     |                          | R |                 |   |               |
+ * +-----+--------------------------+ E |     UNUSED      |   |               |
+ * | R3  |        UNUSED            | G |                 |   |               |
+ * |     |                          | 1 |                 |   |               |
+ * +-----+--------------------------+---+-----------------+---+---------------+
+ * | R4  |        UNUSED            | X |                 |   |               |
+ * |     |                          | R |                 |   |               |
+ * +-----+--------------------------+ E |     UNUSED      | X |               |
+ * | R5  |        UNUSED            | G |                 | X |               |
+ * |     |                          | 2 |                 | R |SRC_IPV6 for NS|
+ * +-----+--------------------------+---+-----------------+ E | (>= IP_INPUT) |
+ * | R6  |        UNUSED            | X |                 | G |               |
+ * |     |                          | R |                 | 1 |               |
+ * +-----+--------------------------+ E |     UNUSED      |   |               |
+ * | R7  |        UNUSED            | G |                 |   |               |
+ * |     |                          | 3 |                 |   |               |
+ * +-----+--------------------------+---+-----------------+---+---------------+
+ * | R8  |     ECMP_GROUP_ID        |   |                 |
+ * |     |     ECMP_MEMBER_ID       | X |                 |
+ * +-----+--------------------------+ R |                 |
+ * |     | REGBIT_{                 | E |                 |
+ * |     |   EGRESS_LOOPBACK/       | G |     UNUSED      |
+ * | R9  |   PKT_LARGER/            | 4 |                 |
+ * |     |   LOOKUP_NEIGHBOR_RESULT/|   |                 |
+ * |     |   SKIP_LOOKUP_NEIGHBOR}  |   |                 |
+ * +-----+--------------------------+---+-----------------+
  *
  */
 
@@ -8005,10 +8016,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
+        /* Store the ethernet address of the port receiving the packet.
+         * This will save us from having to match on inport further down in
+         * the pipeline.
+         */
+        ds_clear(&actions);
+        ds_put_format(&actions, REG_INPORT_ETH_ADDR " = %s; next;",
+                      op->lrp_networks.ea_s);
+
         ds_clear(&match);
         ds_put_format(&match, "eth.mcast && inport == %s", op->json_key);
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                                ds_cstr(&match), "next;", &op->nbrp->header_);
+                                ds_cstr(&match), ds_cstr(&actions),
+                                &op->nbrp->header_);
 
         ds_clear(&match);
         ds_put_format(&match, "eth.dst == %s && inport == %s",
@@ -8021,7 +8041,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                           op->od->l3redirect_port->json_key);
         }
         ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                                ds_cstr(&match), "next;", &op->nbrp->header_);
+                                ds_cstr(&match),  ds_cstr(&actions),
+                                &op->nbrp->header_);
     }
 
     /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
@@ -8288,17 +8309,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_clear(&actions);
             ds_put_format(&actions,
                 "eth.dst = eth.src; "
-                "eth.src = %s; "
+                "eth.src = " REG_INPORT_ETH_ADDR "; "
                 "arp.op = 2; /* ARP reply */ "
                 "arp.tha = arp.sha; "
-                "arp.sha = %s; "
+                "arp.sha = " REG_INPORT_ETH_ADDR "; "
                 "arp.tpa = arp.spa; "
                 "arp.spa = %s; "
                 "outport = %s; "
                 "flags.loopback = 1; "
                 "output;",
-                op->lrp_networks.ea_s,
-                op->lrp_networks.ea_s,
                 op->lrp_networks.ipv4_addrs[i].addr_s,
                 op->json_key);
             ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
@@ -8325,17 +8344,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_clear(&actions);
             ds_put_format(&actions,
                           "eth.dst = eth.src; "
-                          "eth.src = %s; "
+                          "eth.src = " REG_INPORT_ETH_ADDR "; "
                           "arp.op = 2; /* ARP reply */ "
                           "arp.tha = arp.sha; "
-                          "arp.sha = %s; "
+                          "arp.sha = " REG_INPORT_ETH_ADDR "; "
                           "arp.tpa = arp.spa; "
                           "arp.spa = %s; "
                           "outport = %s; "
                           "flags.loopback = 1; "
                           "output;",
-                          op->lrp_networks.ea_s,
-                          op->lrp_networks.ea_s,
                           ip_address,
                           op->json_key);
 
@@ -8356,18 +8373,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_clear(&actions);
             ds_put_format(&actions,
                           "nd_na { "
-                          "eth.src = %s; "
+                          "eth.src = " REG_INPORT_ETH_ADDR "; "
                           "ip6.src = %s; "
                           "nd.target = %s; "
-                          "nd.tll = %s; "
+                          "nd.tll = " REG_INPORT_ETH_ADDR "; "
                           "outport = inport; "
                           "flags.loopback = 1; "
                           "output; "
                           "};",
-                          op->lrp_networks.ea_s,
                           ip_address,
-                          ip_address,
-                          op->lrp_networks.ea_s);
+                          ip_address);
 
             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
                           ds_cstr(&match), ds_cstr(&actions));
@@ -8490,18 +8505,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                   nat->logical_port);
                 } else {
                     if (is_v6) {
-                        ds_put_format(&actions,
-                            "eth.src = %s; "
-                            "nd.tll = %s; ",
-                            op->lrp_networks.ea_s,
-                            op->lrp_networks.ea_s);
+                        ds_put_cstr(&actions,
+                                    "eth.src = " REG_INPORT_ETH_ADDR "; "
+                                    "nd.tll = " REG_INPORT_ETH_ADDR "; ");
 
                     } else {
-                        ds_put_format(&actions,
-                            "eth.src = %s; "
-                            "arp.sha = %s; ",
-                            op->lrp_networks.ea_s,
-                            op->lrp_networks.ea_s);
+                        ds_put_cstr(&actions,
+                                    "eth.src = "REG_INPORT_ETH_ADDR "; "
+                                    "arp.sha = " REG_INPORT_ETH_ADDR "; ");
                     }
                     /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
                      * should only be sent from the "redirect-chassis", so that
@@ -8515,17 +8526,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                 }
             } else {
                 if (is_v6) {
-                    ds_put_format(&actions,
-                        "eth.src = %s; "
-                        "nd.tll = %s; ",
-                        op->lrp_networks.ea_s,
-                        op->lrp_networks.ea_s);
+                    ds_put_cstr(&actions,
+                                "eth.src = " REG_INPORT_ETH_ADDR "; "
+                                "nd.tll = " REG_INPORT_ETH_ADDR "; ");
                 } else {
                     ds_put_format(&actions,
-                        "eth.src = %s; "
-                        "arp.sha = %s; ",
-                        op->lrp_networks.ea_s,
-                        op->lrp_networks.ea_s);
+                                  "eth.src = " REG_INPORT_ETH_ADDR "; "
+                                  "arp.sha = " REG_INPORT_ETH_ADDR "; ");
                 }
             }
             if (is_v6) {
@@ -8743,18 +8750,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             ds_clear(&actions);
             ds_put_format(&actions,
                           "nd_na_router { "
-                          "eth.src = %s; "
+                          "eth.src = " REG_INPORT_ETH_ADDR "; "
                           "ip6.src = %s; "
                           "nd.target = %s; "
-                          "nd.tll = %s; "
+                          "nd.tll = " REG_INPORT_ETH_ADDR "; "
                           "outport = inport; "
                           "flags.loopback = 1; "
                           "output; "
                           "};",
-                          op->lrp_networks.ea_s,
-                          op->lrp_networks.ipv6_addrs[i].addr_s,
                           op->lrp_networks.ipv6_addrs[i].addr_s,
-                          op->lrp_networks.ea_s);
+                          op->lrp_networks.ipv6_addrs[i].addr_s);
             ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
                                     ds_cstr(&match), ds_cstr(&actions),
                                     &op->nbrp->header_);
@@ -9256,6 +9261,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
              * on the l3dgw_port instance where nat->logical_port is
              * resident. */
             if (distributed) {
+                /* Store the ethernet address of the port receiving the packet.
+                 * This will save us from having to match on inport further
+                 * down in the pipeline.
+                 */
+                ds_clear(&actions);
+                ds_put_format(&actions, REG_INPORT_ETH_ADDR " = %s; next;",
+                              od->l3dgw_port->lrp_networks.ea_s);
+
                 ds_clear(&match);
                 ds_put_format(&match,
                               "eth.dst == "ETH_ADDR_FMT" && inport == %s"
@@ -9264,7 +9277,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                               od->l3dgw_port->json_key,
                               nat->logical_port);
                 ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ADMISSION, 50,
-                                        ds_cstr(&match), "next;",
+                                        ds_cstr(&match), ds_cstr(&actions),
                                         &nat->header_);
             }
 
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 87eb42e..a03dbf4 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1624,3 +1624,143 @@ AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_pre_lb.*priority=100" | grep reg0
 ])
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- check router ARP/NS responder])
+ovn_start
+
+ovn-sbctl chassis-add ch geneve 127.0.0.1
+
+ovn-nbctl lr-add lr
+ovn-nbctl lrp-add lr lrp-public 00:00:00:00:01:00 43.43.43.1/24
+ovn-nbctl lrp-add lr lrp 00:00:00:00:00:01 42.42.42.1/24
+
+ovn-nbctl ls-add ls
+ovn-nbctl lsp-add ls ls-rp
+ovn-nbctl lsp-set-type ls-rp router
+ovn-nbctl lsp-set-addresses ls-rp router
+ovn-nbctl lsp-set-options ls-rp router-port=lrp
+ovn-nbctl lsp-add ls ls-vm
+
+ovn-nbctl set logical_router lr options:chassis=ch
+ovn-nbctl lr-nat-add lr dnat_and_snat 43.43.43.2 42.42.42.2
+ovn-nbctl lr-nat-add lr dnat 43.43.43.3 42.42.42.3
+ovn-nbctl lr-nat-add lr dnat_and_snat 43.43.43.4 42.42.42.4 ls-vm 00:00:00:00:00:02
+
+ovn-nbctl --wait=sb sync
+
+# Ingress router port ETH address is stored in lr_in_admission.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_admission.*xreg0\[[0..47\]]" | sort], [0], [dnl
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.dst == 00:00:00:00:01:00 && inport == "lrp-public"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.mcast && inport == "lrp"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.mcast && inport == "lrp-public"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
+])
+
+# Ingress router port ETH address is used for ARP reply/NA in lr_in_ip_input.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), dnl
+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; arp.spa = 42.42.42.1; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd.target == fe80::200:ff:fe00:1), dnl
+action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), dnl
+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; arp.spa = 43.43.43.1; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd.target == fe80::200:ff:fe00:100), dnl
+action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
+])
+
+# 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.
+ovn-nbctl remove logical_router lr options chassis
+ovn-nbctl lrp-set-gateway-chassis lrp-public ch
+ovn-nbctl --wait=sb sync
+
+# Ingress router port ETH address is stored in lr_in_admission.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_admission.*xreg0\[[0..47\]]" | sort], [0], [dnl
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.dst == 00:00:00:00:00:02 && inport == "lrp-public" && is_chassis_resident("ls-vm")), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.dst == 00:00:00:00:01:00 && inport == "lrp-public" && is_chassis_resident("cr-lrp-public")), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.mcast && inport == "lrp"), dnl
+action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+  table=0 (lr_in_admission    ), priority=50   , dnl
+match=(eth.mcast && inport == "lrp-public"), dnl
+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.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), dnl
+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; arp.spa = 42.42.42.1; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd.target == fe80::200:ff:fe00:1), dnl
+action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), dnl
+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; arp.spa = 43.43.43.1; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1 && is_chassis_resident("ls-vm")), dnl
+action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = 00:00:00:00:00:02; arp.sha = 00:00:00:00:00:02; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;)
+  table=3 (lr_in_ip_input     ), priority=90   , dnl
+match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl
+action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };)
+])
+
+# 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

From patchwork Mon Aug 10 10:53:51 2020
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Dumitru Ceara 
X-Patchwork-Id: 1342791
Return-Path: 
X-Original-To: incoming@patchwork.ozlabs.org
Delivered-To: patchwork-incoming@bilbo.ozlabs.org
Authentication-Results: ozlabs.org;
 spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org
 (client-ip=140.211.166.138; helo=whitealder.osuosl.org;
 envelope-from=ovs-dev-bounces@openvswitch.org; receiver=)
Authentication-Results: ozlabs.org;
 dmarc=fail (p=none dis=none) header.from=redhat.com
Authentication-Results: ozlabs.org;
	dkim=fail reason="signature verification failed" (1024-bit key;
 unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256
 header.s=mimecast20190719 header.b=ZSEBgOdQ;
	dkim-atps=neutral
Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(No client certificate requested)
	by ozlabs.org (Postfix) with ESMTPS id 4BQCXD30s6z9sPB
	for ; Mon, 10 Aug 2020 20:54:36 +1000 (AEST)
Received: from localhost (localhost [127.0.0.1])
	by whitealder.osuosl.org (Postfix) with ESMTP id E79358908B;
	Mon, 10 Aug 2020 10:54:34 +0000 (UTC)
X-Virus-Scanned: amavisd-new at osuosl.org
Received: from whitealder.osuosl.org ([127.0.0.1])
	by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)
	with ESMTP id zP5HJ68qjm1r; Mon, 10 Aug 2020 10:54:19 +0000 (UTC)
Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])
	by whitealder.osuosl.org (Postfix) with ESMTP id 62EA98903E;
	Mon, 10 Aug 2020 10:54:19 +0000 (UTC)
Received: from lf-lists.osuosl.org (localhost [127.0.0.1])
	by lists.linuxfoundation.org (Postfix) with ESMTP id 47387C0891;
	Mon, 10 Aug 2020 10:54:19 +0000 (UTC)
X-Original-To: dev@openvswitch.org
Delivered-To: ovs-dev@lists.linuxfoundation.org
Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133])
 by lists.linuxfoundation.org (Postfix) with ESMTP id A2CC7C004D
 for ; Mon, 10 Aug 2020 10:54:17 +0000 (UTC)
Received: from localhost (localhost [127.0.0.1])
 by hemlock.osuosl.org (Postfix) with ESMTP id 913E2885AD
 for ; Mon, 10 Aug 2020 10:54:17 +0000 (UTC)
X-Virus-Scanned: amavisd-new at osuosl.org
Received: from hemlock.osuosl.org ([127.0.0.1])
 by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024)
 with ESMTP id jlTCedAEzKqw for ;
 Mon, 10 Aug 2020 10:54:15 +0000 (UTC)
X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6
Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com
 [205.139.110.120])
 by hemlock.osuosl.org (Postfix) with ESMTPS id 1E3E888C3D
 for ; Mon, 10 Aug 2020 10:54:15 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;
 s=mimecast20190719; t=1597056854;
 h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
 to:to:cc:mime-version:mime-version:content-type:content-type:
 content-transfer-encoding:content-transfer-encoding:
 in-reply-to:in-reply-to:references:references;
 bh=pSzZrWOunu6lrJSBdj/vexHC4PjwHtCYWZtJUy9X6jc=;
 b=ZSEBgOdQWgiKk7R70wtz5fB2f+XBixG+k+X/EJrOcqjvBR/X9j026FvizRWya5jYYv8A63
 vsPAtRhE55UJRTgH205MA785Bu+n6hkdoFv7DfTDisbhrtU7cAYNTQjXv34q+9+nABwRFQ
 dBmJovai9qoaoa+kRrjsA2DgvHexDwc=
Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com
 [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id
 us-mta-464-3-azfomDMnS3ku6PaW7LZQ-1; Mon, 10 Aug 2020 06:53:55 -0400
X-MC-Unique: 3-azfomDMnS3ku6PaW7LZQ-1
Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com
 [10.5.11.13])
 (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))
 (No client certificate requested)
 by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 206FC1030C21
 for ; Mon, 10 Aug 2020 10:53:55 +0000 (UTC)
Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com
 [10.36.112.228])
 by smtp.corp.redhat.com (Postfix) with ESMTP id 3564F7C0E5
 for ; Mon, 10 Aug 2020 10:53:54 +0000 (UTC)
From: Dumitru Ceara 
To: dev@openvswitch.org
Date: Mon, 10 Aug 2020 12:53:51 +0200
Message-Id: <20200810105349.25272.43406.stgit@dceara.remote.csb>
In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb>
References: <20200810105317.25272.51568.stgit@dceara.remote.csb>
User-Agent: StGit/0.17.1-dirty
MIME-Version: 1.0
X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13
Authentication-Results: relay.mimecast.com;
 auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com
X-Mimecast-Spam-Score: 0
X-Mimecast-Originator: redhat.com
Subject: [ovs-dev] [PATCH ovn branch-20.06 3/7] ovn-northd: Refactor ARP/NS
	responder in router pipeline.
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" 

Add functions to build the ARP/NS responder flows for table
S_ROUTER_IN_IP_INPUT and use them in all places where responder
flows are created.

Acked-by: Han Zhou 
Signed-off-by: Dumitru Ceara 

(cherry-picked from master commit d4ce368f1d3757fafafc7a7acd768e0c0ee3b30a)
---
 northd/ovn-northd.8.xml |    8 +
 northd/ovn-northd.c     |  315 +++++++++++++++++++++--------------------------
 tests/ovn-northd.at     |   72 +++++------
 3 files changed, 182 insertions(+), 213 deletions(-)

diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 16f586d..b4f45ab 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1750,7 +1750,7 @@ arp.tha = arp.sha;
 arp.sha = xreg0[0..47];
 arp.tpa = arp.spa;
 arp.spa = A;
-outport = P;
+outport = inport;
 flags.loopback = 1;
 output;
         
@@ -1842,7 +1842,7 @@ arp.tha = arp.sha; arp.sha = xreg0[0..47]; arp.tpa = arp.spa; arp.spa = A; -outport = P; +outport = inport; flags.loopback = 1; output;
@@ -1872,7 +1872,7 @@ nd_na { nd.tll = xreg0[0..47]; ip6.src = A; nd.target = A; - outport = P; + outport = inport; flags.loopback = 1; output; } @@ -2542,7 +2542,7 @@ reg8[0..15] = 0; xxreg0 = G; xxreg1 = A; eth.src = E; -outport = P; +outport = inport; flags.loopback = 1; next;
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 6a28864..88538e2 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -7973,6 +7973,106 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat) return false; } +/* Builds the logical flow that replies to ARP requests for an 'ip_address' + * owned by the router. The flow is inserted in table S_ROUTER_IN_IP_INPUT + * with the given priority. + */ +static void +build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op, + const char *ip_address, const char *eth_addr, + struct ds *extra_match, uint16_t priority, + const struct ovsdb_idl_row *hint, + struct hmap *lflows) +{ + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; + + if (op) { + ds_put_format(&match, "inport == %s && ", op->json_key); + } + + ds_put_format(&match, "arp.op == 1 && arp.tpa == %s", ip_address); + + if (extra_match && ds_last(extra_match) != EOF) { + ds_put_format(&match, " && %s", ds_cstr(extra_match)); + } + ds_put_format(&actions, + "eth.dst = eth.src; " + "eth.src = %s; " + "arp.op = 2; /* ARP reply */ " + "arp.tha = arp.sha; " + "arp.sha = %s; " + "arp.tpa = arp.spa; " + "arp.spa = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output;", + eth_addr, + eth_addr, + ip_address); + + ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, + ds_cstr(&match), ds_cstr(&actions), hint); + + ds_destroy(&match); + ds_destroy(&actions); +} + +/* Builds the logical flow that replies to NS requests for an 'ip_address' + * owned by the router. The flow is inserted in table S_ROUTER_IN_IP_INPUT + * with the given priority. If 'sn_ip_address' is non-NULL, requests are + * restricted only to packets with IP destination 'ip_address' or + * 'sn_ip_address'. + */ +static void +build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, + const char *action, const char *ip_address, + const char *sn_ip_address, const char *eth_addr, + struct ds *extra_match, uint16_t priority, + const struct ovsdb_idl_row *hint, + struct hmap *lflows) +{ + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; + + if (op) { + ds_put_format(&match, "inport == %s && ", op->json_key); + } + + if (sn_ip_address) { + ds_put_format(&match, "ip6.dst == {%s, %s} && ", + ip_address, sn_ip_address); + } + + ds_put_format(&match, "nd_ns && nd.target == %s", ip_address); + + if (extra_match && ds_last(extra_match) != EOF) { + ds_put_format(&match, " && %s", ds_cstr(extra_match)); + } + + ds_put_format(&actions, + "%s { " + "eth.src = %s; " + "ip6.src = %s; " + "nd.target = %s; " + "nd.tll = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output; " + "};", + action, + eth_addr, + ip_address, + ip_address, + eth_addr); + + ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, + ds_cstr(&match), ds_cstr(&actions), hint); + + ds_destroy(&match); + ds_destroy(&actions); +} + static void build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, struct hmap *lflows, struct shash *meter_groups, @@ -8268,13 +8368,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * IP address. */ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { ds_clear(&match); - ds_put_format(&match, - "inport == %s && arp.spa == %s/%u && arp.tpa == %s" - " && arp.op == 1", - op->json_key, + ds_put_format(&match, "arp.spa == %s/%u", op->lrp_networks.ipv4_addrs[i].network_s, - op->lrp_networks.ipv4_addrs[i].plen, - op->lrp_networks.ipv4_addrs[i].addr_s); + op->lrp_networks.ipv4_addrs[i].plen); if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer && op->peer->od->n_localnet_ports) { @@ -8306,23 +8402,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } } - ds_clear(&actions); - ds_put_format(&actions, - "eth.dst = eth.src; " - "eth.src = " REG_INPORT_ETH_ADDR "; " - "arp.op = 2; /* ARP reply */ " - "arp.tha = arp.sha; " - "arp.sha = " REG_INPORT_ETH_ADDR "; " - "arp.tpa = arp.spa; " - "arp.spa = %s; " - "outport = %s; " - "flags.loopback = 1; " - "output;", - op->lrp_networks.ipv4_addrs[i].addr_s, - op->json_key); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, - ds_cstr(&match), ds_cstr(&actions), - &op->nbrp->header_); + build_lrouter_arp_flow(op->od, op, + op->lrp_networks.ipv4_addrs[i].addr_s, + REG_INPORT_ETH_ADDR, &match, 90, + &op->nbrp->header_, lflows); } /* A set to hold all load-balancer vips that need ARP responses. */ @@ -8333,59 +8416,26 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, const char *ip_address; SSET_FOR_EACH (ip_address, &all_ips_v4) { ds_clear(&match); - ds_put_format(&match, - "inport == %s && arp.tpa == %s && arp.op == 1", - op->json_key, ip_address); - if (op == op->od->l3dgw_port) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(&match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } - ds_clear(&actions); - ds_put_format(&actions, - "eth.dst = eth.src; " - "eth.src = " REG_INPORT_ETH_ADDR "; " - "arp.op = 2; /* ARP reply */ " - "arp.tha = arp.sha; " - "arp.sha = " REG_INPORT_ETH_ADDR "; " - "arp.tpa = arp.spa; " - "arp.spa = %s; " - "outport = %s; " - "flags.loopback = 1; " - "output;", - ip_address, - op->json_key); - ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, - ds_cstr(&match), ds_cstr(&actions)); + build_lrouter_arp_flow(op->od, op, + ip_address, REG_INPORT_ETH_ADDR, + &match, 90, NULL, lflows); } SSET_FOR_EACH (ip_address, &all_ips_v6) { ds_clear(&match); - ds_put_format(&match, - "inport == %s && nd_ns && nd.target == %s", - op->json_key, ip_address); - if (op == op->od->l3dgw_port) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(&match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } - ds_clear(&actions); - ds_put_format(&actions, - "nd_na { " - "eth.src = " REG_INPORT_ETH_ADDR "; " - "ip6.src = %s; " - "nd.target = %s; " - "nd.tll = " REG_INPORT_ETH_ADDR "; " - "outport = inport; " - "flags.loopback = 1; " - "output; " - "};", - ip_address, - ip_address); - ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, - ds_cstr(&match), ds_cstr(&actions)); + build_lrouter_nd_flow(op->od, op, "nd_na", + ip_address, NULL, REG_INPORT_ETH_ADDR, + &match, 90, NULL, lflows); } sset_destroy(&all_ips_v4); @@ -8441,123 +8491,60 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, continue; } + /* Mac address to use when replying to ARP/NS. */ + const char *mac_s = REG_INPORT_ETH_ADDR; + /* ARP / ND handling for external IP addresses. * * DNAT IP addresses are external IP addresses that need ARP * handling. */ - char addr_s[INET6_ADDRSTRLEN + 1]; ds_clear(&match); - ds_clear(&actions); - if (is_v6) { - /* For ND solicitations, we need to listen for both the - * unicast IPv6 address and its all-nodes multicast address, - * but always respond with the unicast IPv6 address. */ - char sn_addr_s[INET6_ADDRSTRLEN + 1]; - struct in6_addr sn_addr; - in6_addr_solicited_node(&sn_addr, &ipv6); - ipv6_string_mapped(sn_addr_s, &sn_addr); - ipv6_string_mapped(addr_s, &ipv6); - - ds_put_format(&match, "inport == %s && " - "nd_ns && ip6.dst == {%s, %s} && nd.target == %s", - op->json_key, addr_s, sn_addr_s, addr_s); - ds_put_format(&actions, - "eth.dst = eth.src; " - "nd_na { "); - } else { - ds_put_format(&match, - "inport == %s " - "&& arp.tpa == "IP_FMT" && arp.op == 1", - op->json_key, IP_ARGS(ip)); - - ds_put_format(&actions, - "eth.dst = eth.src; " - "arp.op = 2; /* ARP reply */ " - "arp.tha = arp.sha; "); - } if (op->od->l3dgw_port && op == op->od->l3dgw_port) { struct eth_addr mac; if (nat->external_mac && eth_addr_from_string(nat->external_mac, &mac) && nat->logical_port) { /* distributed NAT case, use nat->external_mac */ - if (is_v6) { - ds_put_format(&actions, - "eth.src = "ETH_ADDR_FMT"; " - "nd.tll = "ETH_ADDR_FMT"; ", - ETH_ADDR_ARGS(mac), - ETH_ADDR_ARGS(mac)); - - } else { - ds_put_format(&actions, - "eth.src = "ETH_ADDR_FMT"; " - "arp.sha = "ETH_ADDR_FMT"; ", - ETH_ADDR_ARGS(mac), - ETH_ADDR_ARGS(mac)); - } + mac_s = nat->external_mac; /* Traffic with eth.src = nat->external_mac should only be * sent from the chassis where nat->logical_port is * resident, so that upstream MAC learning points to the * correct chassis. Also need to avoid generation of * multiple ARP responses from different chassis. */ - ds_put_format(&match, " && is_chassis_resident(\"%s\")", + ds_put_format(&match, "is_chassis_resident(\"%s\")", nat->logical_port); } else { - if (is_v6) { - ds_put_cstr(&actions, - "eth.src = " REG_INPORT_ETH_ADDR "; " - "nd.tll = " REG_INPORT_ETH_ADDR "; "); - - } else { - ds_put_cstr(&actions, - "eth.src = "REG_INPORT_ETH_ADDR "; " - "arp.sha = " REG_INPORT_ETH_ADDR "; "); - } + mac_s = REG_INPORT_ETH_ADDR; /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s * should only be sent from the "redirect-chassis", so that * upstream MAC learning points to the "redirect-chassis". * Also need to avoid generation of multiple ARP responses * from different chassis. */ if (op->od->l3redirect_port) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(&match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } } - } else { - if (is_v6) { - ds_put_cstr(&actions, - "eth.src = " REG_INPORT_ETH_ADDR "; " - "nd.tll = " REG_INPORT_ETH_ADDR "; "); - } else { - ds_put_format(&actions, - "eth.src = " REG_INPORT_ETH_ADDR "; " - "arp.sha = " REG_INPORT_ETH_ADDR "; "); - } } if (is_v6) { - ds_put_format(&actions, - "ip6.src = %s; " - "nd.target = %s; " - "outport = %s; " - "flags.loopback = 1; " - "output; " - "};", - addr_s, addr_s, op->json_key); + /* For ND solicitations, we need to listen for both the + * unicast IPv6 address and its all-nodes multicast address, + * but always respond with the unicast IPv6 address. */ + char sn_addr_s[INET6_ADDRSTRLEN + 1]; + struct in6_addr sn_addr; + in6_addr_solicited_node(&sn_addr, &ipv6); + ipv6_string_mapped(sn_addr_s, &sn_addr); + + build_lrouter_nd_flow(op->od, op, "nd_na", + nat->external_ip, sn_addr_s, + mac_s, &match, 90, + &nat->header_, lflows); } else { - ds_put_format(&actions, - "arp.tpa = arp.spa; " - "arp.spa = "IP_FMT"; " - "outport = %s; " - "flags.loopback = 1; " - "output;", - IP_ARGS(ip), - op->json_key); + build_lrouter_arp_flow(op->od, op, + nat->external_ip, mac_s, &match, 90, + &nat->header_, lflows); } - - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, - ds_cstr(&match), ds_cstr(&actions), - &nat->header_); } if (!smap_get(&op->od->nbr->options, "chassis") @@ -8729,13 +8716,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * router's own IP address. */ for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { ds_clear(&match); - ds_put_format(&match, - "inport == %s && nd_ns && ip6.dst == {%s, %s} " - "&& nd.target == %s", - op->json_key, - op->lrp_networks.ipv6_addrs[i].addr_s, - op->lrp_networks.ipv6_addrs[i].sn_addr_s, - op->lrp_networks.ipv6_addrs[i].addr_s); if (op->od->l3dgw_port && op == op->od->l3dgw_port && op->od->l3redirect_port) { /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s @@ -8743,26 +8723,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * upstream MAC learning points to the "redirect-chassis". * Also need to avoid generation of multiple ND replies * from different chassis. */ - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(&match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } - ds_clear(&actions); - ds_put_format(&actions, - "nd_na_router { " - "eth.src = " REG_INPORT_ETH_ADDR "; " - "ip6.src = %s; " - "nd.target = %s; " - "nd.tll = " REG_INPORT_ETH_ADDR "; " - "outport = inport; " - "flags.loopback = 1; " - "output; " - "};", - op->lrp_networks.ipv6_addrs[i].addr_s, - op->lrp_networks.ipv6_addrs[i].addr_s); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, - ds_cstr(&match), ds_cstr(&actions), - &op->nbrp->header_); + build_lrouter_nd_flow(op->od, op, "nd_na_router", + op->lrp_networks.ipv6_addrs[i].addr_s, + op->lrp_networks.ipv6_addrs[i].sn_addr_s, + REG_INPORT_ETH_ADDR, &match, 90, + &op->nbrp->header_, lflows); } /* UDP/TCP port unreachable */ diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index a03dbf4..a8a2b37 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1667,34 +1667,34 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) # Ingress router port ETH address is used for ARP reply/NA in lr_in_ip_input. AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), dnl -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; arp.spa = 42.42.42.1; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl +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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd.target == fe80::200:ff:fe00:1), dnl +match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), dnl -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; arp.spa = 43.43.43.1; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl +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; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd.target == fe80::200:ff:fe00:100), dnl +match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) ]) @@ -1729,34 +1729,34 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) # xxreg0[0..47] is used unless external_mac is set. AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.spa == 42.42.42.0/24 && arp.tpa == 42.42.42.1 && arp.op == 1), dnl -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; arp.spa = 42.42.42.1; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl +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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.2 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.3 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.tpa == 43.43.43.4 && arp.op == 1), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp"; flags.loopback = 1; output;) +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && nd_ns && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd.target == fe80::200:ff:fe00:1), dnl +match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.spa == 43.43.43.0/24 && arp.tpa == 43.43.43.1 && arp.op == 1), dnl -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; arp.spa = 43.43.43.1; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl +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; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.2 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.2; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl +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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.3 && arp.op == 1 && is_chassis_resident("cr-lrp-public")), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = xreg0[[0..47]]; arp.sha = xreg0[[0..47]]; arp.tpa = arp.spa; arp.spa = 43.43.43.3; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident("cr-lrp-public")), dnl +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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.tpa == 43.43.43.4 && arp.op == 1 && is_chassis_resident("ls-vm")), dnl -action=(eth.dst = eth.src; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; eth.src = 00:00:00:00:00:02; arp.sha = 00:00:00:00:00:02; arp.tpa = arp.spa; arp.spa = 43.43.43.4; outport = "lrp-public"; flags.loopback = 1; output;) +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4 && is_chassis_resident("ls-vm")), dnl +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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && nd_ns && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl +match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) ]) From patchwork Mon Aug 10 10:54:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342790 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=whitealder.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=IKYnYSiQ; dkim-atps=neutral Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCWz27p6z9sPB for ; Mon, 10 Aug 2020 20:54:23 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 96AB1890C3; Mon, 10 Aug 2020 10:54:21 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UYvlfRiBGj4o; Mon, 10 Aug 2020 10:54:16 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by whitealder.osuosl.org (Postfix) with ESMTP id B0FEA8905B; Mon, 10 Aug 2020 10:54:10 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9777AC013C; Mon, 10 Aug 2020 10:54:10 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id ACCBDC013C for ; Mon, 10 Aug 2020 10:54:09 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id A786786670 for ; Mon, 10 Aug 2020 10:54:09 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 7HUR7VfZQr6U for ; Mon, 10 Aug 2020 10:54:08 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) by fraxinus.osuosl.org (Postfix) with ESMTPS id C1C4386493 for ; Mon, 10 Aug 2020 10:54:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056847; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NvUvdyG1H2qEBgLwNAUlkj4dAv08p8m1Slu7CykC9Po=; b=IKYnYSiQEdlcG008g2n3U3gLwHR2QB/mpHpFl4KQRJRfihZFX5fr02uGhh8bZtYmSXAvHJ 8ZV4TAvFlqq5Hy7KRtOZbM/msy6wl33+Zsg0zvwsXcWL8tKWK459523UIA6VB+xjXuOsMq ThmBghlwOFd7I7ydijYhu9xaIFcPVBE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-313-aGagrLc5NImOp6i2RZQDpA-1; Mon, 10 Aug 2020 06:54:05 -0400 X-MC-Unique: aGagrLc5NImOp6i2RZQDpA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 12EB81005504 for ; Mon, 10 Aug 2020 10:54:05 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id 813AC90E84 for ; Mon, 10 Aug 2020 10:54:04 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:54:01 +0200 Message-Id: <20200810105400.25272.73073.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 4/7] ovn-northd: Refactor NAT address parsing. 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" Store NAT entries pointers in ovn_datapath and pre-parse the external IP addresses. This simplifies the code and makes it easier to reuse the parsed external IP and solicited-node address without reparsing. Acked-by: Han Zhou Signed-off-by: Dumitru Ceara (cherry-picked from master commit 1f01c59f6d5a9c38a45a32f871f4b81246466146) --- northd/ovn-northd.c | 119 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 88538e2..1bb75b0 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -606,6 +606,9 @@ struct ovn_datapath { * the "redirect-chassis". */ struct ovn_port *l3redirect_port; + /* NAT entries configured on the router. */ + struct ovn_nat *nat_entries; + struct ovn_port **localnet_ports; size_t n_localnet_ports; @@ -618,6 +621,69 @@ struct ovn_datapath { struct hmap nb_pgs; }; +/* Contains a NAT entry with the external addresses pre-parsed. */ +struct ovn_nat { + const struct nbrec_nat *nb; + struct lport_addresses ext_addrs; +}; + +/* Returns true if a 'nat_entry' is valid, i.e.: + * - parsing was successful. + * - the string yielded exactly one IPv4 address or exactly one IPv6 address. + */ +static bool +nat_entry_is_valid(const struct ovn_nat *nat_entry) +{ + const struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; + + return (ext_addrs->n_ipv4_addrs == 1 && ext_addrs->n_ipv6_addrs == 0) || + (ext_addrs->n_ipv4_addrs == 0 && ext_addrs->n_ipv6_addrs == 1); +} + +static bool +nat_entry_is_v6(const struct ovn_nat *nat_entry) +{ + return nat_entry->ext_addrs.n_ipv6_addrs > 0; +} + +static void +init_nat_entries(struct ovn_datapath *od) +{ + if (!od->nbr || od->nbr->n_nat == 0) { + return; + } + + od->nat_entries = xmalloc(od->nbr->n_nat * sizeof *od->nat_entries); + + for (size_t i = 0; i < od->nbr->n_nat; i++) { + const struct nbrec_nat *nat = od->nbr->nat[i]; + struct ovn_nat *nat_entry = &od->nat_entries[i]; + + nat_entry->nb = nat; + if (!extract_ip_addresses(nat->external_ip, + &nat_entry->ext_addrs) || + !nat_entry_is_valid(nat_entry)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + + VLOG_WARN_RL(&rl, + "Bad ip address %s in nat configuration " + "for router %s", nat->external_ip, od->nbr->name); + } + } +} + +static void +destroy_nat_entries(struct ovn_datapath *od) +{ + if (!od->nbr) { + return; + } + + for (size_t i = 0; i < od->nbr->n_nat; i++) { + destroy_lport_addresses(&od->nat_entries[i].ext_addrs); + } +} + /* A group of logical router datapaths which are connected - either * directly or indirectly. * Each logical router can belong to only one group. */ @@ -675,6 +741,8 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od) ovn_destroy_tnlids(&od->port_tnlids); bitmap_free(od->ipam_info.allocated_ipv4s); free(od->router_ports); + destroy_nat_entries(od); + free(od->nat_entries); free(od->localnet_ports); ovn_ls_port_group_destroy(&od->nb_pgs); destroy_mcast_info_for_datapath(od); @@ -1103,6 +1171,7 @@ join_datapaths(struct northd_context *ctx, struct hmap *datapaths, ovs_list_push_back(nb_only, &od->list); } init_mcast_info_for_datapath(od); + init_nat_entries(od); ovs_list_push_back(lr_list, &od->lr_list); } } @@ -8461,30 +8530,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, snat_ips[n_snat_ips++] = snat_ip; } - for (int i = 0; i < op->od->nbr->n_nat; i++) { - const struct nbrec_nat *nat; - - nat = op->od->nbr->nat[i]; + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { + struct ovn_nat *nat_entry = &op->od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; - ovs_be32 ip; - struct in6_addr ipv6; - bool is_v6 = false; - if (!ip_parse(nat->external_ip, &ip) || !ip) { - if (!ipv6_parse(nat->external_ip, &ipv6)) { - static struct vlog_rate_limit rl = - VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration " - "for router %s", nat->external_ip, op->key); - continue; - } - is_v6 = true; + /* Skip entries we failed to parse. */ + if (!nat_entry_is_valid(nat_entry)) { + continue; } if (!strcmp(nat->type, "snat")) { - if (is_v6) { + if (nat_entry_is_v6(nat_entry)) { + struct in6_addr *ipv6 = + &nat_entry->ext_addrs.ipv6_addrs[0].addr; + snat_ips[n_snat_ips].family = AF_INET6; - snat_ips[n_snat_ips++].ipv6 = ipv6; + snat_ips[n_snat_ips++].ipv6 = *ipv6; } else { + ovs_be32 ip = nat_entry->ext_addrs.ipv4_addrs[0].addr; snat_ips[n_snat_ips].family = AF_INET; snat_ips[n_snat_ips++].ipv4 = ip; } @@ -8527,22 +8590,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } } } - if (is_v6) { - /* For ND solicitations, we need to listen for both the - * unicast IPv6 address and its all-nodes multicast address, - * but always respond with the unicast IPv6 address. */ - char sn_addr_s[INET6_ADDRSTRLEN + 1]; - struct in6_addr sn_addr; - in6_addr_solicited_node(&sn_addr, &ipv6); - ipv6_string_mapped(sn_addr_s, &sn_addr); + struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; + if (nat_entry_is_v6(nat_entry)) { build_lrouter_nd_flow(op->od, op, "nd_na", - nat->external_ip, sn_addr_s, - mac_s, &match, 90, - &nat->header_, lflows); + ext_addrs->ipv6_addrs[0].addr_s, + ext_addrs->ipv6_addrs[0].sn_addr_s, + mac_s, &match, 90, + &nat->header_, lflows); } else { build_lrouter_arp_flow(op->od, op, - nat->external_ip, mac_s, &match, 90, + ext_addrs->ipv4_addrs[0].addr_s, + mac_s, &match, 90, &nat->header_, lflows); } } From patchwork Mon Aug 10 10:54:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342792 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=silver.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=KlVd+Y5h; dkim-atps=neutral Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCZZ6sQXz9sPB for ; Mon, 10 Aug 2020 20:56:38 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id EF78D264B4; Mon, 10 Aug 2020 10:56:36 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id W8697kYR2Cfl; Mon, 10 Aug 2020 10:55:49 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by silver.osuosl.org (Postfix) with ESMTP id 6D9CF26673; Mon, 10 Aug 2020 10:54:30 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 53CBFC004D; Mon, 10 Aug 2020 10:54:30 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 28726C013C for ; Mon, 10 Aug 2020 10:54:29 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 110D8876A3 for ; Mon, 10 Aug 2020 10:54:29 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id IZslliTnE5DO for ; Mon, 10 Aug 2020 10:54:23 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by fraxinus.osuosl.org (Postfix) with ESMTPS id D7E0687692 for ; Mon, 10 Aug 2020 10:54:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056860; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DUAd4Kclj63Po2C+zZlX4GLgdaVrnmq9I4EHPnUBFPA=; b=KlVd+Y5h6qtSoU47KCBrt1Si48fNoMDxb239DVlUzmqg8RMTJ9wT8e5+NWYVF7Q1yXqMgf Gn5HJ/CG2ckSHR3zuUKcllikoMDVzg/MAjp1us2UyusyRTwc7cfkSdSHSG1H77gjvDyx3R mvjryrtvLSVdHwOHIgi1IzVJXDGUhsU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-344-uVfYupu0MCa9C0fnJAmldA-1; Mon, 10 Aug 2020 06:54:16 -0400 X-MC-Unique: uVfYupu0MCa9C0fnJAmldA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 9C790100CCC0 for ; Mon, 10 Aug 2020 10:54:15 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id B15CB5C662 for ; Mon, 10 Aug 2020 10:54:14 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:54:12 +0200 Message-Id: <20200810105410.25272.86105.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 5/7] ovn-northd: Minimize number of ARP/NS responder flows for DNAT. 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" Most ARP/NS responder flows can be configured per datapath instead of per router port. The only exception is with distributed gateway router ports which need special treatment. This patch changes the ARP/NS responder behavior and adds: - Priority 92 flows to reply to ARP requests on distributed gateway router ports, on the chassis where the DNAT entry is bound. - Priority 91 flows to drop ARP requests on distributed gateway router ports, on chassis where the DNAT entry is not bound. - Priority 90 flows to reply to ARP requests on all other router ports. This last type of flows is programmed exactly once per logical router limiting the total number of required logical flows. Suggested-by: Han Zhou Reported-by: Girish Moodalbail Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-June/050186.html Acked-by: Numan Siddique Signed-off-by: Dumitru Ceara (cherry-picked from master commit 59340bab9402c3d78568363d11acea373045a260) --- northd/ovn-northd.8.xml | 16 ++ northd/ovn-northd.c | 338 +++++++++++++++++++++++++++++------------------ tests/ovn-northd.at | 65 +++++---- tests/ovn.at | 8 + 4 files changed, 258 insertions(+), 169 deletions(-) diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index b4f45ab..1543406 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1829,9 +1829,8 @@ nd_na_router { IPv4: For a configured DNAT IP address or a load balancer IPv4 VIP A, for each router port P with Ethernet address E, a priority-90 flow matches - inport == P && arp.op == 1 && - arp.tpa == A (ARP request) - with the following actions: + arp.op == 1 && arp.tpa == A + (ARP request) with the following actions:

@@ -1848,6 +1847,11 @@ output;
         

+ IPv4: For a configured load balancer IPv4 VIP, a similar flow is + added with the additional match inport == P. +

+ +

If the router port P is a distributed gateway router port, then the is_chassis_resident(P) is also added in the match condition for the load balancer IPv4 @@ -1894,9 +1898,11 @@ nd_na {

  • If the corresponding NAT rule cannot be handled in a - distributed manner, then this flow is only programmed on + distributed manner, then a priority-92 flow is programmed on the gateway port instance on the - redirect-chassis. This behavior avoids + redirect-chassis. A priority-91 drop flow is + programmed on the other chassis when ARP requests/NS packets + are received on the gateway port. This behavior avoids generation of multiple ARP responses from different chassis, and allows upstream MAC learning to point to the redirect-chassis. diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 1bb75b0..285e2af 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -8049,7 +8049,7 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat) static void build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op, const char *ip_address, const char *eth_addr, - struct ds *extra_match, uint16_t priority, + struct ds *extra_match, bool drop, uint16_t priority, const struct ovsdb_idl_row *hint, struct hmap *lflows) { @@ -8065,20 +8065,24 @@ build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op, if (extra_match && ds_last(extra_match) != EOF) { ds_put_format(&match, " && %s", ds_cstr(extra_match)); } - ds_put_format(&actions, - "eth.dst = eth.src; " - "eth.src = %s; " - "arp.op = 2; /* ARP reply */ " - "arp.tha = arp.sha; " - "arp.sha = %s; " - "arp.tpa = arp.spa; " - "arp.spa = %s; " - "outport = inport; " - "flags.loopback = 1; " - "output;", - eth_addr, - eth_addr, - ip_address); + if (drop) { + ds_put_format(&actions, "drop;"); + } else { + ds_put_format(&actions, + "eth.dst = eth.src; " + "eth.src = %s; " + "arp.op = 2; /* ARP reply */ " + "arp.tha = arp.sha; " + "arp.sha = %s; " + "arp.tpa = arp.spa; " + "arp.spa = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output;", + eth_addr, + eth_addr, + ip_address); + } ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, ds_cstr(&match), ds_cstr(&actions), hint); @@ -8097,7 +8101,7 @@ static void build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, const char *action, const char *ip_address, const char *sn_ip_address, const char *eth_addr, - struct ds *extra_match, uint16_t priority, + struct ds *extra_match, bool drop, uint16_t priority, const struct ovsdb_idl_row *hint, struct hmap *lflows) { @@ -8119,21 +8123,25 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, ds_put_format(&match, " && %s", ds_cstr(extra_match)); } - ds_put_format(&actions, - "%s { " - "eth.src = %s; " - "ip6.src = %s; " - "nd.target = %s; " - "nd.tll = %s; " - "outport = inport; " - "flags.loopback = 1; " - "output; " - "};", - action, - eth_addr, - ip_address, - ip_address, - eth_addr); + if (drop) { + ds_put_format(&actions, "drop;"); + } else { + ds_put_format(&actions, + "%s { " + "eth.src = %s; " + "ip6.src = %s; " + "nd.target = %s; " + "nd.tll = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output; " + "};", + action, + eth_addr, + ip_address, + ip_address, + eth_addr); + } ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, ds_cstr(&match), ds_cstr(&actions), hint); @@ -8323,7 +8331,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "ip4.dst == 0.0.0.0/8", "drop;"); - /* Priority-90 flows reply to ARP requests and ND packets. */ + /* Priority-90-92 flows handle ARP requests and ND packets. Most are + * per logical port but DNAT addresses can be handled per datapath + * for non gateway router ports. + */ + for (int i = 0; i < od->nbr->n_nat; i++) { + struct ovn_nat *nat_entry = &od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; + + /* Skip entries we failed to parse. */ + if (!nat_entry_is_valid(nat_entry)) { + continue; + } + + if (!strcmp(nat->type, "snat")) { + continue; + } + + /* Priority 91 and 92 flows are added for each gateway router + * port to handle the special cases. In case we get the packet + * on a regular port, just reply with the port's ETH address. + */ + struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; + if (nat_entry_is_v6(nat_entry)) { + build_lrouter_nd_flow(od, NULL, "nd_na", + ext_addrs->ipv6_addrs[0].addr_s, + ext_addrs->ipv6_addrs[0].sn_addr_s, + REG_INPORT_ETH_ADDR, NULL, false, 90, + &nat->header_, lflows); + } else { + build_lrouter_arp_flow(od, NULL, + ext_addrs->ipv4_addrs[0].addr_s, + REG_INPORT_ETH_ADDR, NULL, false, 90, + &nat->header_, lflows); + } + } /* Drop ARP packets (priority 85). ARP request packets for router's own * IPs are handled with priority-90 flows. @@ -8473,7 +8515,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, build_lrouter_arp_flow(op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s, - REG_INPORT_ETH_ADDR, &match, 90, + REG_INPORT_ETH_ADDR, &match, false, 90, &op->nbrp->header_, lflows); } @@ -8492,7 +8534,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, build_lrouter_arp_flow(op->od, op, ip_address, REG_INPORT_ETH_ADDR, - &match, 90, NULL, lflows); + &match, false, 90, NULL, lflows); } SSET_FOR_EACH (ip_address, &all_ips_v6) { @@ -8504,108 +8546,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, build_lrouter_nd_flow(op->od, op, "nd_na", ip_address, NULL, REG_INPORT_ETH_ADDR, - &match, 90, NULL, lflows); + &match, false, 90, NULL, lflows); } sset_destroy(&all_ips_v4); sset_destroy(&all_ips_v6); - /* A gateway router can have 2 SNAT IP addresses to force DNATed and - * LBed traffic respectively to be SNATed. In addition, there can be - * a number of SNAT rules in the NAT table. */ - struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips - * (op->od->nbr->n_nat + 2)); - size_t n_snat_ips = 0; - - struct v46_ip snat_ip; - const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat", - &snat_ip); - if (dnat_force_snat_ip) { - snat_ips[n_snat_ips++] = snat_ip; - } - - const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb", - &snat_ip); - if (lb_force_snat_ip) { - snat_ips[n_snat_ips++] = snat_ip; - } - - for (size_t i = 0; i < op->od->nbr->n_nat; i++) { - struct ovn_nat *nat_entry = &op->od->nat_entries[i]; - const struct nbrec_nat *nat = nat_entry->nb; - - /* Skip entries we failed to parse. */ - if (!nat_entry_is_valid(nat_entry)) { - continue; - } - - if (!strcmp(nat->type, "snat")) { - if (nat_entry_is_v6(nat_entry)) { - struct in6_addr *ipv6 = - &nat_entry->ext_addrs.ipv6_addrs[0].addr; - - snat_ips[n_snat_ips].family = AF_INET6; - snat_ips[n_snat_ips++].ipv6 = *ipv6; - } else { - ovs_be32 ip = nat_entry->ext_addrs.ipv4_addrs[0].addr; - snat_ips[n_snat_ips].family = AF_INET; - snat_ips[n_snat_ips++].ipv4 = ip; - } - continue; - } - - /* Mac address to use when replying to ARP/NS. */ - const char *mac_s = REG_INPORT_ETH_ADDR; - - /* ARP / ND handling for external IP addresses. - * - * DNAT IP addresses are external IP addresses that need ARP - * handling. */ - ds_clear(&match); - - if (op->od->l3dgw_port && op == op->od->l3dgw_port) { - struct eth_addr mac; - if (nat->external_mac && - eth_addr_from_string(nat->external_mac, &mac) - && nat->logical_port) { - /* distributed NAT case, use nat->external_mac */ - mac_s = nat->external_mac; - /* Traffic with eth.src = nat->external_mac should only be - * sent from the chassis where nat->logical_port is - * resident, so that upstream MAC learning points to the - * correct chassis. Also need to avoid generation of - * multiple ARP responses from different chassis. */ - ds_put_format(&match, "is_chassis_resident(\"%s\")", - nat->logical_port); - } else { - mac_s = REG_INPORT_ETH_ADDR; - /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s - * should only be sent from the "redirect-chassis", so that - * upstream MAC learning points to the "redirect-chassis". - * Also need to avoid generation of multiple ARP responses - * from different chassis. */ - if (op->od->l3redirect_port) { - ds_put_format(&match, "is_chassis_resident(%s)", - op->od->l3redirect_port->json_key); - } - } - } - - struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; - if (nat_entry_is_v6(nat_entry)) { - build_lrouter_nd_flow(op->od, op, "nd_na", - ext_addrs->ipv6_addrs[0].addr_s, - ext_addrs->ipv6_addrs[0].sn_addr_s, - mac_s, &match, 90, - &nat->header_, lflows); - } else { - build_lrouter_arp_flow(op->od, op, - ext_addrs->ipv4_addrs[0].addr_s, - mac_s, &match, 90, - &nat->header_, lflows); - } - } - if (!smap_get(&op->od->nbr->options, "chassis") && !op->od->l3dgw_port) { /* UDP/TCP port unreachable. */ @@ -8654,6 +8600,50 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } } + /* A gateway router can have 2 SNAT IP addresses to force DNATed and + * LBed traffic respectively to be SNATed. In addition, there can be + * a number of SNAT rules in the NAT table. */ + struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips + * (op->od->nbr->n_nat + 2)); + size_t n_snat_ips = 0; + + struct v46_ip snat_ip; + const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat", + &snat_ip); + if (dnat_force_snat_ip) { + snat_ips[n_snat_ips++] = snat_ip; + } + + const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb", + &snat_ip); + if (lb_force_snat_ip) { + snat_ips[n_snat_ips++] = snat_ip; + } + + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { + struct ovn_nat *nat_entry = &op->od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; + + /* Skip entries we failed to parse. */ + if (!nat_entry_is_valid(nat_entry)) { + continue; + } + + if (!strcmp(nat->type, "snat")) { + if (nat_entry_is_v6(nat_entry)) { + struct in6_addr *ipv6 = + &nat_entry->ext_addrs.ipv6_addrs[0].addr; + + snat_ips[n_snat_ips].family = AF_INET6; + snat_ips[n_snat_ips++].ipv6 = *ipv6; + } else { + ovs_be32 ip = nat_entry->ext_addrs.ipv4_addrs[0].addr; + snat_ips[n_snat_ips].family = AF_INET; + snat_ips[n_snat_ips++].ipv4 = ip; + } + } + } + ds_clear(&match); ds_put_cstr(&match, "ip4.dst == {"); bool has_drop_ips = false; @@ -8715,6 +8705,90 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, } free(snat_ips); + + /* ARP/NS packets are taken care of per router. The only exception + * is on the l3dgw_port where we might need to use a different + * ETH address. + */ + if (op != op->od->l3dgw_port) { + continue; + } + + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { + struct ovn_nat *nat_entry = &op->od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; + + /* Skip entries we failed to parse. */ + if (!nat_entry_is_valid(nat_entry)) { + continue; + } + + if (!strcmp(nat->type, "snat")) { + continue; + } + + /* Mac address to use when replying to ARP/NS. */ + const char *mac_s = REG_INPORT_ETH_ADDR; + + /* ARP / ND handling for external IP addresses. + * + * DNAT IP addresses are external IP addresses that need ARP + * handling. */ + + struct eth_addr mac; + + ds_clear(&match); + if (nat->external_mac && + eth_addr_from_string(nat->external_mac, &mac) + && nat->logical_port) { + /* distributed NAT case, use nat->external_mac */ + mac_s = nat->external_mac; + /* Traffic with eth.src = nat->external_mac should only be + * sent from the chassis where nat->logical_port is + * resident, so that upstream MAC learning points to the + * correct chassis. Also need to avoid generation of + * multiple ARP responses from different chassis. */ + ds_put_format(&match, "is_chassis_resident(\"%s\")", + nat->logical_port); + } else { + mac_s = REG_INPORT_ETH_ADDR; + /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s + * should only be sent from the "redirect-chassis", so that + * upstream MAC learning points to the "redirect-chassis". + * Also need to avoid generation of multiple ARP responses + * from different chassis. */ + if (op->od->l3redirect_port) { + ds_put_format(&match, "is_chassis_resident(\"%s\")", + op->od->l3redirect_port->json_key); + } + } + + /* Respond to ARP/NS requests on the chassis that binds the gw + * port. Drop the ARP/NS requests on other chassis. + */ + struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; + if (nat_entry_is_v6(nat_entry)) { + build_lrouter_nd_flow(op->od, op, "nd_na", + ext_addrs->ipv6_addrs[0].addr_s, + ext_addrs->ipv6_addrs[0].sn_addr_s, + mac_s, &match, false, 92, + &nat->header_, lflows); + build_lrouter_nd_flow(op->od, op, "nd_na", + ext_addrs->ipv6_addrs[0].addr_s, + ext_addrs->ipv6_addrs[0].sn_addr_s, + mac_s, NULL, true, 91, + &nat->header_, lflows); + } else { + build_lrouter_arp_flow(op->od, op, + ext_addrs->ipv4_addrs[0].addr_s, + mac_s, &match, false, 92, + &nat->header_, lflows); + build_lrouter_arp_flow(op->od, op, + ext_addrs->ipv4_addrs[0].addr_s, + mac_s, NULL, true, 91, + &nat->header_, lflows); + } + } } /* DHCPv6 reply handling */ @@ -8789,7 +8863,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, build_lrouter_nd_flow(op->od, op, "nd_na_router", op->lrp_networks.ipv6_addrs[i].addr_s, op->lrp_networks.ipv6_addrs[i].sn_addr_s, - REG_INPORT_ETH_ADDR, &match, 90, + REG_INPORT_ETH_ADDR, &match, false, 90, &op->nbrp->header_, lflows); } diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index a8a2b37..df980c7 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1667,33 +1667,24 @@ action=(xreg0[[0..47]] = 00:00:00:00:01:00; next;) # Ingress router port ETH address is used for ARP reply/NA in lr_in_ip_input. AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl -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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.2), dnl 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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.3), dnl 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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.4), dnl 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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl +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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) table=3 (lr_in_ip_input ), priority=90 , dnl match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl 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; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl -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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl -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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl -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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) ]) @@ -1727,37 +1718,55 @@ 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. +# Priority 90 flows (per router). AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=90" | grep "arp\|nd" | sort], [0], [dnl table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl -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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.2), dnl 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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.3), dnl 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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +match=(arp.op == 1 && arp.tpa == 43.43.43.4), dnl 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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl +match=(inport == "lrp" && arp.op == 1 && arp.tpa == 42.42.42.1 && arp.spa == 42.42.42.0/24), dnl +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; arp.spa = 42.42.42.1; outport = inport; flags.loopback = 1; output;) + table=3 (lr_in_ip_input ), priority=90 , dnl match=(inport == "lrp" && ip6.dst == {fe80::200:ff:fe00:1, ff02::1:ff00:1} && nd_ns && nd.target == fe80::200:ff:fe00:1), dnl action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:1; nd.target = fe80::200:ff:fe00:1; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) table=3 (lr_in_ip_input ), priority=90 , dnl match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.1 && arp.spa == 43.43.43.0/24), dnl 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; arp.spa = 43.43.43.1; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl +match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl +action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) +]) + +# Priority 91 drop flows (per distributed gw port), if port is not resident. +AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=91" | grep "arp\|nd" | sort], [0], [dnl + table=3 (lr_in_ip_input ), priority=91 , dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2), dnl +action=(drop;) + table=3 (lr_in_ip_input ), priority=91 , dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3), dnl +action=(drop;) + table=3 (lr_in_ip_input ), priority=91 , dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4), dnl +action=(drop;) +]) + +# Priority 92 ARP/NS responders (per distributed gw port), if port is resident. +AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=92" | grep "arp\|nd" | sort], [0], [dnl + table=3 (lr_in_ip_input ), priority=92 , dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident(""cr-lrp-public"")), dnl 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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident("cr-lrp-public")), dnl + table=3 (lr_in_ip_input ), priority=92 , dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident(""cr-lrp-public"")), dnl 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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl + table=3 (lr_in_ip_input ), priority=92 , dnl match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4 && is_chassis_resident("ls-vm")), dnl 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; arp.spa = 43.43.43.4; outport = inport; flags.loopback = 1; output;) - table=3 (lr_in_ip_input ), priority=90 , dnl -match=(inport == "lrp-public" && ip6.dst == {fe80::200:ff:fe00:100, ff02::1:ff00:100} && nd_ns && nd.target == fe80::200:ff:fe00:100 && is_chassis_resident("cr-lrp-public")), dnl -action=(nd_na_router { eth.src = xreg0[[0..47]]; ip6.src = fe80::200:ff:fe00:100; nd.target = fe80::200:ff:fe00:100; nd.tll = xreg0[[0..47]]; outport = inport; flags.loopback = 1; output; };) ]) # xreg0[0..47] isn't used anywhere else. diff --git a/tests/ovn.at b/tests/ovn.at index a2e0f7e..c99675f 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -19162,7 +19162,7 @@ OVS_WAIT_UNTIL([ send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121) # Verify that the ARP request is replied to from hv1 and not hv2. -match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1" +match_arp_req="priority=92.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1" as hv1 OVS_WAIT_UNTIL([ @@ -19182,7 +19182,7 @@ OVS_WAIT_UNTIL([ send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 122) # Verify that the ARP request is replied to from hv2 and not hv1. -match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1" +match_arp_req="priority=92.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1" as hv2 OVS_WAIT_UNTIL([ @@ -19226,7 +19226,7 @@ dst_ipv6=00100000000000000000000000000121 send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72dd # Verify that the ND_NS is replied to from hv1 and not hv2. -match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121" +match_nd_ns="priority=92.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121" as hv1 OVS_WAIT_UNTIL([ @@ -19248,7 +19248,7 @@ dst_ipv6=00100000000000000000000000000122 send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72db # Verify that the ND_NS is replied to from hv2 and not hv1. -match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122" +match_nd_ns="priority=92.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122" as hv2 OVS_WAIT_UNTIL([ From patchwork Mon Aug 10 10:54:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342793 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=silver.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=jEY+2rAc; dkim-atps=neutral Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCbN6Bsvz9sPB for ; Mon, 10 Aug 2020 20:57:20 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id E061C26742; Mon, 10 Aug 2020 10:57:18 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aUmY7BHPrQ7J; Mon, 10 Aug 2020 10:57:05 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by silver.osuosl.org (Postfix) with ESMTP id A58BB2673B; Mon, 10 Aug 2020 10:54:37 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9809DC004D; Mon, 10 Aug 2020 10:54:37 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 4F70DC013C for ; Mon, 10 Aug 2020 10:54:36 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 3A5E8876B0 for ; Mon, 10 Aug 2020 10:54:36 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xLFd28e48ggk for ; Mon, 10 Aug 2020 10:54:34 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) by fraxinus.osuosl.org (Postfix) with ESMTPS id 8A5C4876C3 for ; Mon, 10 Aug 2020 10:54:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056872; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AA1KLui1WxC5WKPAiE5z/eqSUEchMqgG/r/Cb+T75nk=; b=jEY+2rAcw4/d0/Heu/zPqzDEX9FES9sUuzY/WmSl3Ta7o/1P3fd4aUA7wEmDDaiintRFiO VV7vNL0l5+bjTGQjUvcexaGeKt4RLn0WKeNRLFuy07dhWwmIWZ8t5SVxyIeDCGmcYfRmwV WcEe4yxkJNR1TgsmIXFmgrKVSF8Cnlc= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-177-TfomWVGtNMyATogbz1_3_w-1; Mon, 10 Aug 2020 06:54:30 -0400 X-MC-Unique: TfomWVGtNMyATogbz1_3_w-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id EEE181DE2 for ; Mon, 10 Aug 2020 10:54:29 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id 682537C0E5 for ; Mon, 10 Aug 2020 10:54:29 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:54:26 +0200 Message-Id: <20200810105420.25272.19607.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 6/7] ovn-northd: Fix is_chassis_resident() match for DNAT. 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" While reducing the number of logical flows, a bug was introduced when building the match for flows that need "is_chassis_resident(redirect_port)". This commit removes the wrong additional quotes. Fixes: 59340bab9402 ("ovn-northd: Minimize number of ARP/NS responder flows for DNAT.") Signed-off-by: Dumitru Ceara (cherry-picked from master commit c4ff9b83cab44a88f74d19a866eea31b565108da) --- northd/ovn-northd.c | 2 +- tests/ovn-northd.at | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 285e2af..f8401d7 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -8758,7 +8758,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * Also need to avoid generation of multiple ARP responses * from different chassis. */ if (op->od->l3redirect_port) { - ds_put_format(&match, "is_chassis_resident(\"%s\")", + ds_put_format(&match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } } diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index df980c7..aac3fcf 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1759,10 +1759,10 @@ action=(drop;) # Priority 92 ARP/NS responders (per distributed gw port), if port is resident. AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_input.*priority=92" | grep "arp\|nd" | sort], [0], [dnl table=3 (lr_in_ip_input ), priority=92 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident(""cr-lrp-public"")), dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.2 && is_chassis_resident("cr-lrp-public")), dnl 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; arp.spa = 43.43.43.2; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=92 , dnl -match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident(""cr-lrp-public"")), dnl +match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.3 && is_chassis_resident("cr-lrp-public")), dnl 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; arp.spa = 43.43.43.3; outport = inport; flags.loopback = 1; output;) table=3 (lr_in_ip_input ), priority=92 , dnl match=(inport == "lrp-public" && arp.op == 1 && arp.tpa == 43.43.43.4 && is_chassis_resident("ls-vm")), dnl From patchwork Mon Aug 10 10:54:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1342794 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.133; helo=hemlock.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=d9zYg4TN; dkim-atps=neutral Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BQCcB0BhYz9sTb for ; Mon, 10 Aug 2020 20:58:02 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 7121A8890E; Mon, 10 Aug 2020 10:58:00 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Wc-pjK+DToQI; Mon, 10 Aug 2020 10:57:58 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by hemlock.osuosl.org (Postfix) with ESMTP id E1C0788456; Mon, 10 Aug 2020 10:57:58 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id C4843C013C; Mon, 10 Aug 2020 10:57:58 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id DBBA0C004D for ; Mon, 10 Aug 2020 10:57:57 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id C5C622049F for ; Mon, 10 Aug 2020 10:57:57 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id I22skrIjyfff for ; Mon, 10 Aug 2020 10:57:39 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) by silver.osuosl.org (Postfix) with ESMTPS id DF4802680A for ; Mon, 10 Aug 2020 10:54:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1597056886; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4AEguBRpY1qFfGKlBkWlYzKMOdjUdOaGeWopnpcLvjQ=; b=d9zYg4TNYosQrP3YTvcAuOha0zqNVEskKmFniIuyNDYW9GIMI7y6cm0rVnMpQtFQIIgVWi UWLxakSj6lCAd5kuLEYF2xVnQOTy4ZQ4BZCz3IMpInwjlTqtv15/W8T4LrrQ1idhKunfLH FSDWj1KP0q8rKRF1HFgzMfcwcDd5xpc= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-424-8sneoPDENNKFABnwHsc0Gg-1; Mon, 10 Aug 2020 06:54:42 -0400 X-MC-Unique: 8sneoPDENNKFABnwHsc0Gg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 28CBF1005504 for ; Mon, 10 Aug 2020 10:54:41 +0000 (UTC) Received: from dceara.remote.csb (ovpn-112-228.ams2.redhat.com [10.36.112.228]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6AB1B5C662 for ; Mon, 10 Aug 2020 10:54:40 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Mon, 10 Aug 2020 12:54:37 +0200 Message-Id: <20200810105435.25272.92714.stgit@dceara.remote.csb> In-Reply-To: <20200810105317.25272.51568.stgit@dceara.remote.csb> References: <20200810105317.25272.51568.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn branch-20.06 7/7] ovn-northd: Fix logical flows to limit ARP/NS broadcast domain. 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" Logical flows that limit the ARP/NS broadcast domain on a logical switch should only match on ARP requests/NS for IPs that can actually be replied to on the connected router port (i.e., an IP on the same network is configured on the router port). Reported-by: Girish Moodalbail Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-June/050287.html Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.") Acked-by: Numan Siddique Signed-off-by: Dumitru Ceara (cherry-picked from master commit 1e07781310d8155997672bdce01a2ff4f5a93e83) --- northd/ovn-northd.c | 162 ++++++++++++++++++++++++++++++++++++++++++--------- tests/ovn.at | 74 +++++++++++++++++++++++ 2 files changed, 208 insertions(+), 28 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index f8401d7..1134528 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -6090,6 +6090,42 @@ build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list) } } +/* Returns 'true' if the IPv4 'addr' is on the same subnet with one of the + * IPs configured on the router port. + */ +static bool +lrouter_port_ipv4_reachable(const struct ovn_port *op, ovs_be32 addr) +{ + for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + struct ipv4_netaddr *op_addr = &op->lrp_networks.ipv4_addrs[i]; + + if ((addr & op_addr->mask) == op_addr->network) { + return true; + } + } + return false; +} + +/* Returns 'true' if the IPv6 'addr' is on the same subnet with one of the + * IPs configured on the router port. + */ +static bool +lrouter_port_ipv6_reachable(const struct ovn_port *op, + const struct in6_addr *addr) +{ + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { + struct ipv6_netaddr *op_addr = &op->lrp_networks.ipv6_addrs[i]; + + struct in6_addr nat_addr6_masked = + ipv6_addr_bitand(addr, &op_addr->mask); + + if (ipv6_addr_equals(&nat_addr6_masked, &op_addr->network)) { + return true; + } + } + return false; +} + /* * Ingress table 19: Flows that flood self originated ARP/ND packets in the * switching domain. @@ -6100,8 +6136,47 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, struct ovn_datapath *od, struct hmap *lflows) { - struct ds match = DS_EMPTY_INITIALIZER; + struct sset all_eth_addrs = SSET_INITIALIZER(&all_eth_addrs); struct ds eth_src = DS_EMPTY_INITIALIZER; + struct ds match = DS_EMPTY_INITIALIZER; + + sset_add(&all_eth_addrs, op->lrp_networks.ea_s); + + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { + struct ovn_nat *nat_entry = &op->od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; + + if (!nat_entry_is_valid(nat_entry)) { + continue; + } + + if (!strcmp(nat->type, "snat")) { + continue; + } + + if (!nat->external_mac) { + continue; + } + + /* Check if the ovn port has a network configured on which we could + * expect ARP requests/NS for the DNAT external_ip. + */ + if (nat_entry_is_v6(nat_entry)) { + struct in6_addr *addr = &nat_entry->ext_addrs.ipv6_addrs[0].addr; + + if (!lrouter_port_ipv6_reachable(op, addr)) { + continue; + } + } else { + ovs_be32 addr = nat_entry->ext_addrs.ipv4_addrs[0].addr; + + if (!lrouter_port_ipv4_reachable(op, addr)) { + continue; + } + } + sset_add(&all_eth_addrs, nat->external_mac); + } + /* Self originated (G)ARP requests/ND need to be flooded as usual. * Determine that packets are self originated by also matching on @@ -6109,15 +6184,11 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, * is a VLAN-backed network. * Priority: 80. */ - ds_put_format(ð_src, "{ %s, ", op->lrp_networks.ea_s); - for (size_t i = 0; i < op->od->nbr->n_nat; i++) { - const struct nbrec_nat *nat = op->od->nbr->nat[i]; - - if (!nat->external_mac) { - continue; - } + const char *eth_addr; - ds_put_format(ð_src, "%s, ", nat->external_mac); + ds_put_cstr(ð_src, "{"); + SSET_FOR_EACH (eth_addr, &all_eth_addrs) { + ds_put_format(ð_src, "%s, ", eth_addr); } ds_chomp(ð_src, ' '); ds_chomp(ð_src, ','); @@ -6129,8 +6200,9 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, ds_cstr(&match), "outport = \""MC_FLOOD"\"; output;"); - ds_destroy(&match); + sset_destroy(&all_eth_addrs); ds_destroy(ð_src); + ds_destroy(&match); } /* @@ -6221,38 +6293,72 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op, struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); - for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { - sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); - } - for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { - sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); + get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + + const char *ip_addr; + const char *ip_addr_next; + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v4) { + ovs_be32 ipv4_addr; + + /* Check if the ovn port has a network configured on which we could + * expect ARP requests for the LB VIP. + */ + if (ip_parse(ip_addr, &ipv4_addr) && + lrouter_port_ipv4_reachable(op, ipv4_addr)) { + continue; + } + + sset_delete(&all_ips_v4, SSET_NODE_FROM_NAME(ip_addr)); } + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v6) { + struct in6_addr ipv6_addr; - get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + /* Check if the ovn port has a network configured on which we could + * expect NS requests for the LB VIP. + */ + if (ipv6_parse(ip_addr, &ipv6_addr) && + lrouter_port_ipv6_reachable(op, &ipv6_addr)) { + continue; + } + + sset_delete(&all_ips_v6, SSET_NODE_FROM_NAME(ip_addr)); + } for (size_t i = 0; i < op->od->nbr->n_nat; i++) { - const struct nbrec_nat *nat = op->od->nbr->nat[i]; + struct ovn_nat *nat_entry = &op->od->nat_entries[i]; + const struct nbrec_nat *nat = nat_entry->nb; + + if (!nat_entry_is_valid(nat_entry)) { + continue; + } if (!strcmp(nat->type, "snat")) { continue; } - ovs_be32 ip; - ovs_be32 mask; - struct in6_addr ipv6; - struct in6_addr mask_v6; + /* Check if the ovn port has a network configured on which we could + * expect ARP requests/NS for the DNAT external_ip. + */ + if (nat_entry_is_v6(nat_entry)) { + struct in6_addr *addr = &nat_entry->ext_addrs.ipv6_addrs[0].addr; - char *error = ip_parse_masked(nat->external_ip, &ip, &mask); - if (error) { - free(error); - error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6); - if (!error) { + if (lrouter_port_ipv6_reachable(op, addr)) { sset_add(&all_ips_v6, nat->external_ip); } } else { - sset_add(&all_ips_v4, nat->external_ip); + ovs_be32 addr = nat_entry->ext_addrs.ipv4_addrs[0].addr; + + if (lrouter_port_ipv4_reachable(op, addr)) { + sset_add(&all_ips_v4, nat->external_ip); + } } - free(error); + } + + for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { + sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); + } + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { + sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); } if (!sset_is_empty(&all_ips_v4)) { diff --git a/tests/ovn.at b/tests/ovn.at index c99675f..9fa433c 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -19015,12 +19015,14 @@ ovn-nbctl --wait=hv sync sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg) sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg) +sw1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw1) r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1) r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1) r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2) match_sw_metadata="metadata=0x${sw_dp_key}" +match_sw1_metadata="metadata=0x${sw1_dp_key}" match_r1_metadata="metadata=0x${r1_dp_key}" # Inject ARP request for first router owned IP address. @@ -19136,6 +19138,78 @@ ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.122 20.0.0.12 sw1-p2 00:00:00:02: ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::122 20::12 sw1-p2 00:00:00:02:00:00 ovn-nbctl --wait=hv sync +# Check that broadcast domain limiting flows match only on IPs that are +# directly reachable via the router port. +# For sw-rtr1: +# - 10.0.0.1, 10::1, fe80::200:ff:fe00:100 - interface IPs. +# - 10.0.0.11, 10::11 - LB VIPs. +# - 10.0.0.111, 10.0.0.121, 10.0.0.122, 10::111, 10::121, 10::122 - DNAT IPs. +# For sw-rtr2: +# - 10.0.0.2, 10::2, fe80::200:ff:fe00:200 - interface IPs. +# - 10.0.0.22, 10::22 - LB VIPs. +# - 10.0.0.222, 10::222 - DNAT IPs. +as hv1 +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl +arp_tpa=10.0.0.1 +arp_tpa=10.0.0.11 +arp_tpa=10.0.0.111 +arp_tpa=10.0.0.121 +arp_tpa=10.0.0.122 +arp_tpa=10.0.0.2 +arp_tpa=10.0.0.22 +arp_tpa=10.0.0.222 +]) +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl +nd_target=10::1 +nd_target=10::11 +nd_target=10::111 +nd_target=10::121 +nd_target=10::122 +nd_target=10::2 +nd_target=10::22 +nd_target=10::222 +nd_target=fe80::200:ff:fe00:100 +nd_target=fe80::200:ff:fe00:200 +]) + +# For sw1-rtr1: +# - 20.0.0.1, 20::1, fe80::200:1ff:fe00:0 - interface IPs. +as hv1 +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw1_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl +arp_tpa=20.0.0.1 +]) +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw1_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl +nd_target=20::1 +nd_target=fe80::200:1ff:fe00:0 +]) + +# Self originated ARP/NS with SMACs owned by rtr1-sw and rtr2-sw should be +# flooded: +# - 00:00:00:00:01:00 - interface MAC (rtr1-sw). +# - 00:00:00:00:02:00 - interface MAC (rtr2-sw). +# - 00:00:00:01:00:00 - dnat_and_snat external MAC. +# - 00:00:00:02:00:00 - dnat_and_snat external MAC. +as hv1 +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl +dl_src=00:00:00:00:01:00 +dl_src=00:00:00:00:02:00 +dl_src=00:00:00:01:00:00 +dl_src=00:00:00:02:00:00 +]) +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}.*icmp_type=135" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl +dl_src=00:00:00:00:01:00 +dl_src=00:00:00:00:02:00 +dl_src=00:00:00:01:00:00 +dl_src=00:00:00:02:00:00 +]) + +# Self originated ARP/NS with SMACs owned by rtr1-sw1 should be flooded: +# - 00:00:01:00:00:00 - interface MAC. +as hv1 +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw1_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl +dl_src=00:00:01:00:00:00 +]) + # Inject ARP request for first router owned NAT address. send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)