Skip to content

Commit

Permalink
net: Exempt multicast addresses from five-second neighbor lifetime
Browse files Browse the repository at this point in the history
Commit 5895631 ("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: 5895631 ("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>
  • Loading branch information
Jeff Dike authored and Jakub Kicinski committed Nov 13, 2020
1 parent 2b56687 commit 8cf8821
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/net/neighbour.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions net/core/neighbour.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions net/ipv4/arp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions net/ipv6/ndisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -1706,6 +1708,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);
Expand Down

0 comments on commit 8cf8821

Please sign in to comment.