From patchwork Thu Aug 15 16:52:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1972854 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WlB4r5stCz1yXZ for ; Fri, 16 Aug 2024 02:52:43 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 8A10181F21; Thu, 15 Aug 2024 16:52:40 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id hLoPBJJIRRpZ; Thu, 15 Aug 2024 16:52:39 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org CF2F881F08 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTPS id CF2F881F08; Thu, 15 Aug 2024 16:52:38 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9CBEEC000E; Thu, 15 Aug 2024 16:52:38 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 390F2C000D for ; Thu, 15 Aug 2024 16:52:36 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 10F9881F0E for ; Thu, 15 Aug 2024 16:52:36 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id KxICCYLkCYCZ for ; Thu, 15 Aug 2024 16:52:34 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.183.196; helo=relay4-d.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 1992381F08 Authentication-Results: smtp1.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 1992381F08 Received: from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net [217.70.183.196]) by smtp1.osuosl.org (Postfix) with ESMTPS id 1992381F08 for ; Thu, 15 Aug 2024 16:52:33 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 6E2BDE0004; Thu, 15 Aug 2024 16:52:30 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Thu, 15 Aug 2024 12:52:22 -0400 Message-ID: <20240815165222.1144250-1-numans@ovn.org> X-Mailer: git-send-email 2.45.2 MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v3] Reply only for the multicast ND solicitations. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique IPv6 ND Solicitation (NS) responder logical flows match on ip6.dst field. These flows when translated to datapath flows also match on ip6.dst, which means a separate datapath flow per destination IP address. This may cause significant performance issues in some setups (particularly ovs-dpdk telco deployments). This patch addresses this issue by matching on eth.mcast6 so that datapath flows for normal IPv6 traffic doesn't have to match on ip6.dst. IPv6 NS packets are generally multicast. A new logical match "nd_ns_mcast" is added for this purpose. After this patch, We no longer respond to IPv6 NS unicast packets. Let the target reply to it, so that the sender has the ability to monitor the targe liveness via the unicast ND solicitations. This behavior now matches the IPv4 ARP responder flows. Note that after the commit [1] which was recently added we now only respond to IPv4 ARP broadcast packets. A recent patch [2] from Ilya partially addressed the same datapath flow explosion issue by matching on eth.mcast6 for MLD packets. With this patch, we now address the datapath flow explosion issue for IPv6 traffic provided 2 conditions are met: a. All the logical ports of a logical switch are not configured with port security. b. The logical switch port of type router if configured with "arp_proxy" option doesn't include any IPv6 address(es). [1] - c48ed1736a58 ("Do not reply on unicast arps for IPv4 targets.") [2] - 43c34f2e6676 ("logical-fields: Add missing multicast matches for MLD and IGMP.") Note: Documentation for 'eth.mcastv6' and 'ip6.mcast' predicates were missing from ovn-sb.xml and this patch adds it. Reported-at: https://issues.redhat.com/browse/FDP-728 Reported-by: Mike Pattrick Signed-off-by: Numan Siddique Acked-by: Dumitru Ceara Acked-by: Ilya Maximets --- v2 -> v3 ------- - Removed the system test as it was flaky and intead added the test in ovn.at and used ofproto/trace as suggested by Ilya to check the megaflows. v1 -> v2 ------- - Addressed review comments from Ilya. lib/logical-fields.c | 3 + northd/northd.c | 21 ++++--- ovn-sb.xml | 3 + tests/ovn-northd.at | 4 +- tests/ovn.at | 144 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 163 insertions(+), 12 deletions(-) diff --git a/lib/logical-fields.c b/lib/logical-fields.c index 2c9d3c61bf..5a8b53f2b6 100644 --- a/lib/logical-fields.c +++ b/lib/logical-fields.c @@ -293,6 +293,9 @@ ovn_init_symtab(struct shash *symtab) "icmp6.type == {135, 136} && icmp6.code == 0 && ip.ttl == 255"); expr_symtab_add_predicate(symtab, "nd_ns", "icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255"); + expr_symtab_add_predicate(symtab, "nd_ns_mcast", + "ip6.mcast && icmp6.type == 135 && icmp6.code == 0 && " + "ip.ttl == 255"); expr_symtab_add_predicate(symtab, "nd_na", "icmp6.type == 136 && icmp6.code == 0 && ip.ttl == 255"); expr_symtab_add_predicate(symtab, "nd_rs", diff --git a/northd/northd.c b/northd/northd.c index c4824aae3b..c9ae1e958f 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -9633,16 +9633,21 @@ build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op, op->lflow_ref); } - /* 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. */ + /* For ND solicitations: + * - Reply only for the all-nodes multicast address(es) of the + * logical port IPv6 address(es). + * + * - Do not reply for unicast ND solicitations. Let the target + * reply it, so that the sender has the ability to monitor + * the targe liveness via the unicast ND solicitations. + */ for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) { ds_clear(match); - ds_put_format(match, - "nd_ns && ip6.dst == {%s, %s} && nd.target == %s", - op->lsp_addrs[i].ipv6_addrs[j].addr_s, - op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s, - op->lsp_addrs[i].ipv6_addrs[j].addr_s); + ds_put_format( + match, + "nd_ns_mcast && ip6.dst == %s && nd.target == %s", + op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s, + op->lsp_addrs[i].ipv6_addrs[j].addr_s); ds_clear(actions); ds_put_format(actions, diff --git a/ovn-sb.xml b/ovn-sb.xml index de0bd636fd..c11296d7c4 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -1146,6 +1146,7 @@
  • eth.bcast expands to eth.dst == ff:ff:ff:ff:ff:ff
  • eth.mcast expands to eth.dst[40]
  • +
  • eth.mcastv6 expands to eth.dst[32..47] == 0x3333
  • vlan.present expands to vlan.tci[12]
  • ip4 expands to eth.type == 0x800
  • ip4.src_mcast expands to @@ -1161,8 +1162,10 @@
  • ip.first_frag expands to ip.is_frag && !ip.later_frag
  • arp expands to eth.type == 0x806
  • rarp expands to eth.type == 0x8035
  • +
  • ip6.mcast expands to eth.mcastv6 && ip6.dst[120..127] == 0xff
  • nd expands to icmp6.type == {135, 136} && icmp6.code == 0 && ip.ttl == 255
  • nd_ns expands to icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255
  • +
  • nd_ns_mcast expands to ip6.mcast && icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255
  • nd_na expands to icmp6.type == 136 && icmp6.code == 0 && ip.ttl == 255
  • nd_rs expands to icmp6.type == 133 && icmp6.code == 0 && ip.ttl == 255
  • diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 3c49c0d43b..93ccbce6b0 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -9503,9 +9503,9 @@ AT_CAPTURE_FILE([S1flows]) AT_CHECK([grep -e "ls_in_arp_rsp" S1flows | ovn_strip_lflows], [0], [dnl table=??(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;) table=??(ls_in_arp_rsp ), priority=100 , match=(arp.tpa == 192.168.0.10 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff && inport == "S1-vm1"), action=(next;) - table=??(ls_in_arp_rsp ), priority=100 , match=(nd_ns && ip6.dst == {fd00::10, ff02::1:ff00:10} && nd.target == fd00::10 && inport == "S1-vm1"), action=(next;) + table=??(ls_in_arp_rsp ), priority=100 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:10 && nd.target == fd00::10 && inport == "S1-vm1"), action=(next;) table=??(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == 192.168.0.10 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = 50:54:00:00:00:10; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 50:54:00:00:00:10; arp.tpa = arp.spa; arp.spa = 192.168.0.10; outport = inport; flags.loopback = 1; output;) - table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns && ip6.dst == {fd00::10, ff02::1:ff00:10} && nd.target == fd00::10), action=(nd_na { eth.src = 50:54:00:00:00:10; ip6.src = fd00::10; nd.target = fd00::10; nd.tll = 50:54:00:00:00:10; outport = inport; flags.loopback = 1; output; };) + table=??(ls_in_arp_rsp ), priority=50 , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:10 && nd.target == fd00::10), action=(nd_na { eth.src = 50:54:00:00:00:10; ip6.src = fd00::10; nd.target = fd00::10; nd.tll = 50:54:00:00:00:10; outport = inport; flags.loopback = 1; output; };) ]) #Set the disable_arp_nd_rsp option and verify the flow diff --git a/tests/ovn.at b/tests/ovn.at index a1d689e849..b8cb831b80 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -8617,7 +8617,7 @@ done # Complete Neighbor Solicitation packet and Neighbor Advertisement packet # vif1 -> NS -> vif2. vif1 <- NA <- ovn-controller. # vif2 will not receive NS packet, since ovn-controller will reply for it. -ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598fd81ce49a9480000f8163efffea1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598 +ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598ff0200000000000000000001ffa1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598 na_packet=fa163e940598fa163ea1f9ae86dd6000000000203afffd81ce49a9480000f8163efffea1f9aefd81ce49a9480000f8163efffe9405988800e9ed60000000fd81ce49a9480000f8163efffea1f9ae0201fa163ea1f9ae as hv1 ovs-appctl netdev-dummy/receive vif1 $ns_packet @@ -14417,7 +14417,7 @@ test_ns_na() { local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 packet=$(fmt_pkt " - Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}') / + Ether(dst='33:33:ff:ff:ff:ff', src='${src_mac}') / IPv6(src='${src_ip}', dst='ff02::1:ff00:2') / ICMPv6ND_NS(tgt='${dst_ip}') ") @@ -38756,3 +38756,143 @@ OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP ]) + +dnl This test checks that the megaflows translated by ovs-vswitchd +dnl don't match on IPv6 source and destination addresses for +dnl simple switching. +OVN_FOR_EACH_NORTHD([ +AT_SETUP([IPv6 switching - megaflow check for IPv6 src/dst matches]) +ovn_start + +check ovn-nbctl ls-add sw0 + +check ovn-nbctl lsp-add sw0 vm0 +check ovn-nbctl lsp-set-addresses vm0 "f0:00:0f:01:02:03 10.0.0.3 1000::3" + +check ovn-nbctl lsp-add sw0 vm1 +check ovn-nbctl lsp-set-addresses vm1 "f0:00:0f:01:02:04 10.0.0.4 1000::4" + +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-sw0 fa:16:3e:00:00:01 10.0.0.1 1000::1/64 +check ovn-nbctl lsp-add sw0 sw0-lr0 +check ovn-nbctl lsp-set-type sw0-lr0 router +check ovn-nbctl lsp-set-addresses sw0-lr0 router +check ovn-nbctl --wait=hv lsp-set-options sw0-lr0 router-port=lr0-sw0 + +net_add n1 +sim_add hv +as hv +check ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +check ovs-vsctl add-port br-int vif1 -- set Interface vif1 \ +external-ids:iface-id=vm0 options:tx_pcap=hv/vif1-tx.pcap \ +options:rxq_pcap=hv/vif1-rx.pcap ofport-request=1 +check ovs-vsctl add-port br-int vif2 -- set Interface vif2 \ +external-ids:iface-id=vm1 options:tx_pcap=hv/vif2-tx.pcap \ +options:rxq_pcap=hv/vif2-rx.pcap ofport-request=2 + +wait_for_ports_up vm0 vm1 + +packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:04', src='f0:00:0f:01:02:03')/ \ + IPv6(dst='1000::4', src='1000::3')/ \ + UDP(sport=53, dport=4369)") + +as hv +ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif1 $packet + +AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl +0 +]) + +dnl Make sure that the packet was received by vm1. This ensures that the +dnl packet was delivered as expected and the megaflow didn't have any matches +dnl on IPv6 src or dst. + +echo $packet > expected +OVN_CHECK_PACKETS([hv/vif2-tx.pcap], [expected]) + +packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:03', src='f0:00:0f:01:02:04')/ \ + IPv6(dst='1000::3', src='1000::4')/ \ + UDP(sport=53, dport=4369)") + +as hv +ovs-appctl ofproto/trace br-int in_port=2 $packet > vm1_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif2 $packet + +AT_CAPTURE_FILE([vm1_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl +0 +]) + +dnl Make sure that the packet was received by vm0. This ensures that the +dnl packet was delivered as expected and the megaflow didn't have any matches +dnl on IPv6 src or dst. +echo $packet > expected +OVN_CHECK_PACKETS([hv/vif1-tx.pcap], [expected]) + +dnl Add port security to vm0. The megaflow should now match on ipv6 src/dst. +check ovn-nbctl lsp-set-port-security vm0 "f0:00:0f:01:02:03 10.0.0.3 1000::3" +check ovn-nbctl --wait=hv sync + +packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:04', src='f0:00:0f:01:02:03')/ \ + IPv6(dst='1000::4', src='1000::3')/ \ + UDP(sport=53, dport=4369)") + +as hv +ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif1 $packet + +AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl +1 +]) + +dnl Clear port security. +check ovn-nbctl lsp-set-port-security vm0 "" +check ovn-nbctl --wait=hv sync + +as hv +ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif1 $packet + +AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl +0 +]) + +dnl Configure proxy arp/nd on the router port. The megaflow should now match +dnl on ipv6 src/dst. +check ovn-nbctl --wait=hv lsp-set-options sw0-lr0 router-port=lr0-sw0 arp_proxy="2000::1/64" + +as hv +ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif1 $packet + +AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl +1 +]) + +packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:03', src='f0:00:0f:01:02:04')/ \ + IPv6(dst='1000::3', src='1000::4')/ \ + UDP(sport=53, dport=4369)") + +as hv +ovs-appctl ofproto/trace br-int in_port=2 $packet > vm1_ip6_ofproto_trace.txt +ovs-appctl netdev-dummy/receive vif2 $packet + +AT_CAPTURE_FILE([vm1_ip6_ofproto_trace.txt]) + +AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl +1 +]) + +AT_CLEANUP +])