]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
Exempt multicast addresses from five-second neighbor lifetime
authorJeff Dike <jdike@akamai.com>
Fri, 13 Nov 2020 01:58:15 +0000 (20:58 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Nov 2020 12:28:56 +0000 (13:28 +0100)
[ Upstream commit 8cf8821e15cd553339a5b48ee555a0439c2b2742 ]

Commit 58956317c8de ("neighbor: Improve garbage collection")
guarantees neighbour table entries a five-second lifetime.  Processes
which make heavy use of multicast can fill the neighour table with
multicast addresses in five seconds.  At that point, neighbour entries
can't be GC-ed because they aren't five seconds old yet, the kernel
log starts to fill up with "neighbor table overflow!" messages, and
sends start to fail.

This patch allows multicast addresses to be thrown out before they've
lived out their five seconds.  This makes room for non-multicast
addresses and makes messages to all addresses more reliable in these
circumstances.

Fixes: 58956317c8de ("neighbor: Improve garbage collection")
Signed-off-by: Jeff Dike <jdike@akamai.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://lore.kernel.org/r/20201113015815.31397-1-jdike@akamai.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/neighbour.h
net/core/neighbour.c
net/ipv4/arp.c
net/ipv6/ndisc.c

index 8ec77bfdc1a413d0a45edc978075aedab817e62e..2be8d6b0dfb69563158d180bf0ecc01eba06a55e 100644 (file)
@@ -204,6 +204,7 @@ struct neigh_table {
        int                     (*pconstructor)(struct pneigh_entry *);
        void                    (*pdestructor)(struct pneigh_entry *);
        void                    (*proxy_redo)(struct sk_buff *skb);
+       int                     (*is_multicast)(const void *pkey);
        bool                    (*allow_add)(const struct net_device *dev,
                                             struct netlink_ext_ack *extack);
        char                    *id;
index 04953e5f2530282213b5d5cbb8148d57bb18e534..6c270fce200f4f0cc7b60f7a6d248df0148b8ebb 100644 (file)
@@ -235,6 +235,8 @@ static int neigh_forced_gc(struct neigh_table *tbl)
 
                        write_lock(&n->lock);
                        if ((n->nud_state == NUD_FAILED) ||
+                           (tbl->is_multicast &&
+                            tbl->is_multicast(n->primary_key)) ||
                            time_after(tref, n->updated))
                                remove = true;
                        write_unlock(&n->lock);
index 05eb42f347e8f59e0bd737a7f6d8d2921300caad..7b951992c372bf8010ce32abebca2b621255a9bc 100644 (file)
@@ -125,6 +125,7 @@ static int arp_constructor(struct neighbour *neigh);
 static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb);
 static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb);
 static void parp_redo(struct sk_buff *skb);
+static int arp_is_multicast(const void *pkey);
 
 static const struct neigh_ops arp_generic_ops = {
        .family =               AF_INET,
@@ -156,6 +157,7 @@ struct neigh_table arp_tbl = {
        .key_eq         = arp_key_eq,
        .constructor    = arp_constructor,
        .proxy_redo     = parp_redo,
+       .is_multicast   = arp_is_multicast,
        .id             = "arp_cache",
        .parms          = {
                .tbl                    = &arp_tbl,
@@ -928,6 +930,10 @@ static void parp_redo(struct sk_buff *skb)
        arp_process(dev_net(skb->dev), NULL, skb);
 }
 
+static int arp_is_multicast(const void *pkey)
+{
+       return ipv4_is_multicast(*((__be32 *)pkey));
+}
 
 /*
  *     Receive an arp request from the device layer.
index 53caf59c591e303c78cc70a88f77b9c2ec1403ba..118e19cabb72bd3cd1e267ca4f4da3c276e2cb83 100644 (file)
@@ -81,6 +81,7 @@ static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
 static int pndisc_constructor(struct pneigh_entry *n);
 static void pndisc_destructor(struct pneigh_entry *n);
 static void pndisc_redo(struct sk_buff *skb);
+static int ndisc_is_multicast(const void *pkey);
 
 static const struct neigh_ops ndisc_generic_ops = {
        .family =               AF_INET6,
@@ -115,6 +116,7 @@ struct neigh_table nd_tbl = {
        .pconstructor = pndisc_constructor,
        .pdestructor =  pndisc_destructor,
        .proxy_redo =   pndisc_redo,
+       .is_multicast = ndisc_is_multicast,
        .allow_add  =   ndisc_allow_add,
        .id =           "ndisc_cache",
        .parms = {
@@ -1705,6 +1707,11 @@ static void pndisc_redo(struct sk_buff *skb)
        kfree_skb(skb);
 }
 
+static int ndisc_is_multicast(const void *pkey)
+{
+       return ipv6_addr_is_multicast((struct in6_addr *)pkey);
+}
+
 static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)
 {
        struct inet6_dev *idev = __in6_dev_get(skb->dev);