Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: neigh: disallow transition to NUD_STALE if lladdr is unchanged in neigh_update()

NUD_STALE is used when the caller(e.g. arp_process()) can't guarantee
neighbour reachability. If the entry was NUD_VALID and lladdr is unchanged,
the entry state should not be changed.

Currently the code puts an extra "NUD_CONNECTED" condition. So if old state
was NUD_DELAY or NUD_PROBE (they are NUD_VALID but not NUD_CONNECTED), the
state can be changed to NUD_STALE.

This may cause problem. Because NUD_STALE lladdr doesn't guarantee
reachability, when we send traffic, the state will be changed to
NUD_DELAY. In normal case, if we get no confirmation (by dst_confirm()),
we will change the state to NUD_PROBE and send probe traffic. But now the
state may be reset to NUD_STALE again(e.g. by broadcast ARP packets),
so the probe traffic will not be sent. This situation may happen again and
again, and packets will be sent to an non-reachable lladdr forever.

The fix is to remove the "NUD_CONNECTED" condition. After that the
"NEIGH_UPDATE_F_WEAK_OVERRIDE" condition (used by IPv6) in that branch will
be redundant, so remove it.

This change may increase probe traffic, but it's essential since NUD_STALE
lladdr is unreliable. To ensure correctness, we prefer to resolve lladdr,
when we can't get confirmation, even while remote packets try to set
NUD_STALE state.

Signed-off-by: Chunhui He <hchunhui@mail.ustc.edu.cn>
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Reviewed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

He Chunhui and committed by
David S. Miller
d1c2b501 ee591f46

+1 -6
+1 -6
net/core/neighbour.c
··· 1060 1060 NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 1061 1061 lladdr instead of overriding it 1062 1062 if it is different. 1063 - It also allows to retain current state 1064 - if lladdr is unchanged. 1065 1063 NEIGH_UPDATE_F_ADMIN means that the change is administrative. 1066 1064 1067 1065 NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing ··· 1148 1150 } else 1149 1151 goto out; 1150 1152 } else { 1151 - if (lladdr == neigh->ha && new == NUD_STALE && 1152 - ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) || 1153 - (old & NUD_CONNECTED)) 1154 - ) 1153 + if (lladdr == neigh->ha && new == NUD_STALE) 1155 1154 new = old; 1156 1155 } 1157 1156 }