From patchwork Tue Nov 15 22:09:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Zeitlhofer X-Patchwork-Id: 20606 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:6687:0:0:0:0:0 with SMTP id l7csp2966930wru; Tue, 15 Nov 2022 14:16:50 -0800 (PST) X-Google-Smtp-Source: AA0mqf70Ofe1SVU5yBZYJSBzHQsqN1gipnfpuzifqiJfhbFtROms6Qm+FuvWgYOXXXOGNyWDY/9M X-Received: by 2002:a17:906:c00c:b0:7ae:e6ac:2427 with SMTP id e12-20020a170906c00c00b007aee6ac2427mr11649406ejz.345.1668550610236; Tue, 15 Nov 2022 14:16:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1668550610; cv=none; d=google.com; s=arc-20160816; b=rLeLmyw9Mi0qJL4eXF6NYLiFb/4RAPQEN6BtglHRy7kxkph43RIe5oke+z4fp9Q+5b xJhNNKOu8VLh0AYaebNvnwyFeRPFvxksoQy2+Z5tHVe3RzSvYkvEblQVzwdREH1d8eYF YDmpUUsCf0O5xHUb6YPzH42ZUT7mWzfAaZdXtCA8brjq+s0kFTAAwXj0RUERNnLuMHUz v+iErZ6dkiEhGQgRN/drY+CHSOwlrG0mIw84FxPHL49AlA9ewkM1PvJvzr+a+r+Mp66G 2i03GmOJA2Q44dkFWRHBi4CiXUW+nlttVgdW8gdf3s7SeywU8gaKwpKJG/wCG0SEFeeL WShw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date; bh=/egqI5TEdoc1L4NIjDau9t259JU+p91GgWBDsYSV9HM=; b=D0jl4ll2Xo+xaQPdXl9C7MZA33Wfe34C+e5Ou0Hb3Mj+fG9y/YmD/2TigZEJ/RzAyy 034WYgtSSj+MYwk6DxyjVQ+Ei4+pOMW12IIRd9f+skvHT1CEYLnWUVF6JTmRgNj2zY+j dAFzJnOESVeS6pobUZLh8T/3bUjOYFevFqPVo7kFi0QmfsPTZyysSo17bmcyz8MkRQbK 6zdPdn9UtN7bzcj4iyKahPMzQgoPCt8m3MIMhJs4bzo5+ozav8I5rtZiiu45kSKMHcSX +d2h3wO0fYfzStQTBv5iB80uG7/khDWlZS5XdXO6mxBbFWhjtejQmsET2y1lRnVFoj1a v+iw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id sa28-20020a1709076d1c00b0078e1d1d6005si12080048ejc.23.2022.11.15.14.16.26; Tue, 15 Nov 2022 14:16:50 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238427AbiKOWJ7 (ORCPT + 99 others); Tue, 15 Nov 2022 17:09:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49982 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232997AbiKOWJu (ORCPT ); Tue, 15 Nov 2022 17:09:50 -0500 Received: from vie01a-dmta-at03-1.mx.upcmail.net (vie01a-dmta-at03-1.mx.upcmail.net [62.179.121.151]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2337E275CD for ; Tue, 15 Nov 2022 14:09:48 -0800 (PST) Received: from [172.31.216.235] (helo=vie01a-pemc-psmtp-pe12.mail.upcmail.net) by vie01a-dmta-at03.mx.upcmail.net with esmtp (Exim 4.92) (envelope-from ) id 1ov47l-00AT0Z-PS for linux-kernel@vger.kernel.org; Tue, 15 Nov 2022 23:09:45 +0100 Received: from ren-mail-psmtp-mg02. ([80.109.253.241]) by vie01a-pemc-psmtp-pe12.mail.upcmail.net with ESMTP id v47loejvM8s8Uv47lom72W; Tue, 15 Nov 2022 23:09:45 +0100 Received: from mr1 ([80.108.14.125]) by ren-mail-psmtp-mg02. with ESMTP id v47ko7xqObZLDv47kos7Gy; Tue, 15 Nov 2022 23:09:44 +0100 X-Env-Mailfrom: thomas.zeitlhofer+lkml@ze-it.at X-Env-Rcptto: linux-kernel@vger.kernel.org X-SourceIP: 80.108.14.125 X-CNFS-Analysis: v=2.4 cv=Ufwy9IeN c=1 sm=1 tr=0 ts=63740e29 a=NZF4o3NPETkZAaMtA+lBEA==:117 a=NZF4o3NPETkZAaMtA+lBEA==:17 a=kj9zAlcOel0A:10 a=VG87k5yWxwQ-MwjZPvcA:9 a=CjuIK1q_8ugA:10 Date: Tue, 15 Nov 2022 23:09:41 +0100 From: Thomas Zeitlhofer To: Paolo Abeni Cc: "David S. Miller" , Alexander Mikhalitsyn , "Denis V. Lunev" , Eric Dumazet , Jakub Kicinski , David Ahern , Daniel Borkmann , Yang Yingliang , Muchun Song , Vasily Averin , Yuwei Wang , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2] net: neigh: decrement the family specific qlen Message-ID: References: <205d812ab74d721f4345eabcf3e5a86a710b40da.camel@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <205d812ab74d721f4345eabcf3e5a86a710b40da.camel@redhat.com> X-CMAE-Envelope: MS4wfHw0bTZVdEuAVSMqBwCcMeuCnME9toMJrnXQ50trBd+EAC0uMt1AkmZVhwUZAFBA47NdpVwbGSI5cf6GQ6bYrU9LvWC18FN+RO67yVgTmFUAErMqjgXm eBIuATnCyvwW/1IRvEadbUqNwxLIYcctVwpQ3yvYEwpgqhGlptFwz5972j0WEEzIlBkZIifwkU+yHEx7hr95JlLYKOK4+ef/6bo= X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_LOW, SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1749291868793266718?= X-GMAIL-MSGID: =?utf-8?q?1749602124691834988?= Commit 0ff4eb3d5ebb ("neighbour: make proxy_queue.qlen limit per-device") introduced the length counter qlen in struct neigh_parms. There are separate neigh_parms instances for IPv4/ARP and IPv6/ND, and while the family specific qlen is incremented in pneigh_enqueue(), the mentioned commit decrements always the IPv4/ARP specific qlen, regardless of the currently processed family, in pneigh_queue_purge() and neigh_proxy_process(). As a result, with IPv6/ND, the family specific qlen is only incremented (and never decremented) until it exceeds PROXY_QLEN, and then, according to the check in pneigh_enqueue(), neighbor solicitations are not answered anymore. As an example, this is noted when using the subnet-router anycast address to access a Linux router. After a certain amount of time (in the observed case, qlen exceeded PROXY_QLEN after two days), the Linux router stops answering neighbor solicitations for its subnet-router anycast address and effectively becomes unreachable. Another result with IPv6/ND is that the IPv4/ARP specific qlen is decremented more often than incremented. This leads to negative qlen values, as a signed integer has been used for the length counter qlen, and potentially to an integer overflow. Fix this by introducing the helper function neigh_parms_qlen_dec(), which decrements the family specific qlen. Thereby, make use of the existing helper function neigh_get_dev_parms_rcu(), whose definition therefore needs to be placed earlier in neighbour.c. Take the family member from struct neigh_table to determine the currently processed family and appropriately call neigh_parms_qlen_dec() from pneigh_queue_purge() and neigh_proxy_process(). Additionally, use an unsigned integer for the length counter qlen. Fixes: 0ff4eb3d5ebb ("neighbour: make proxy_queue.qlen limit per-device") Signed-off-by: Thomas Zeitlhofer --- Notes: v2: implement review comment from Paolo Abeni (thanks for the feedback): use u32 instead of __u32 in qlen declaration include/net/neighbour.h | 2 +- net/core/neighbour.c | 58 +++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 20745cf7ae1a..2f2a6023fb0e 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -83,7 +83,7 @@ struct neigh_parms { struct rcu_head rcu_head; int reachable_time; - int qlen; + u32 qlen; int data[NEIGH_VAR_DATA_MAX]; DECLARE_BITMAP(data_state, NEIGH_VAR_DATA_MAX); }; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index a77a85e357e0..952a54763358 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -307,7 +307,31 @@ static int neigh_del_timer(struct neighbour *n) return 0; } -static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net) +static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, + int family) +{ + switch (family) { + case AF_INET: + return __in_dev_arp_parms_get_rcu(dev); + case AF_INET6: + return __in6_dev_nd_parms_get_rcu(dev); + } + return NULL; +} + +static void neigh_parms_qlen_dec(struct net_device *dev, int family) +{ + struct neigh_parms *p; + + rcu_read_lock(); + p = neigh_get_dev_parms_rcu(dev, family); + if (p) + p->qlen--; + rcu_read_unlock(); +} + +static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net, + int family) { struct sk_buff_head tmp; unsigned long flags; @@ -321,13 +345,7 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net) struct net_device *dev = skb->dev; if (net == NULL || net_eq(dev_net(dev), net)) { - struct in_device *in_dev; - - rcu_read_lock(); - in_dev = __in_dev_get_rcu(dev); - if (in_dev) - in_dev->arp_parms->qlen--; - rcu_read_unlock(); + neigh_parms_qlen_dec(dev, family); __skb_unlink(skb, list); __skb_queue_tail(&tmp, skb); } @@ -409,7 +427,8 @@ static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, write_lock_bh(&tbl->lock); neigh_flush_dev(tbl, dev, skip_perm); pneigh_ifdown_and_unlock(tbl, dev); - pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL); + pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL, + tbl->family); if (skb_queue_empty_lockless(&tbl->proxy_queue)) del_timer_sync(&tbl->proxy_timer); return 0; @@ -1621,13 +1640,8 @@ static void neigh_proxy_process(struct timer_list *t) if (tdif <= 0) { struct net_device *dev = skb->dev; - struct in_device *in_dev; - rcu_read_lock(); - in_dev = __in_dev_get_rcu(dev); - if (in_dev) - in_dev->arp_parms->qlen--; - rcu_read_unlock(); + neigh_parms_qlen_dec(dev, tbl->family); __skb_unlink(skb, &tbl->proxy_queue); if (tbl->proxy_redo && netif_running(dev)) { @@ -1821,7 +1835,7 @@ int neigh_table_clear(int index, struct neigh_table *tbl) cancel_delayed_work_sync(&tbl->managed_work); cancel_delayed_work_sync(&tbl->gc_work); del_timer_sync(&tbl->proxy_timer); - pneigh_queue_purge(&tbl->proxy_queue, NULL); + pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family); neigh_ifdown(tbl, NULL); if (atomic_read(&tbl->entries)) pr_crit("neighbour leakage\n"); @@ -3539,18 +3553,6 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write, return ret; } -static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, - int family) -{ - switch (family) { - case AF_INET: - return __in_dev_arp_parms_get_rcu(dev); - case AF_INET6: - return __in6_dev_nd_parms_get_rcu(dev); - } - return NULL; -} - static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, int index) {