aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorHe Chunhui <hchunhui@mail.ustc.edu.cn>2016-07-26 02:16:52 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-26 17:25:20 -0400
commitd1c2b5010d07e967d7cbcc232a86b2308d824ca3 (patch)
tree9deefa7e8dcb6a4756e6c7faadb0ca2ab1128e1a /net/core
parentee591f46a5a0ca0caaeb72b79ec5e012c6e3c9ef (diff)
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>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/neighbour.c7
1 files changed, 1 insertions, 6 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 5cdc62a8eb84..cf26e04c4046 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1060,8 +1060,6 @@ static void neigh_update_hhs(struct neighbour *neigh)
1060 NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 1060 NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
1061 lladdr instead of overriding it 1061 lladdr instead of overriding it
1062 if it is different. 1062 if it is different.
1063 It also allows to retain current state
1064 if lladdr is unchanged.
1065 NEIGH_UPDATE_F_ADMIN means that the change is administrative. 1063 NEIGH_UPDATE_F_ADMIN means that the change is administrative.
1066 1064
1067 NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 1065 NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing
@@ -1150,10 +1148,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
1150 } else 1148 } else
1151 goto out; 1149 goto out;
1152 } else { 1150 } else {
1153 if (lladdr == neigh->ha && new == NUD_STALE && 1151 if (lladdr == neigh->ha && new == NUD_STALE)
1154 ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) ||
1155 (old & NUD_CONNECTED))
1156 )
1157 new = old; 1152 new = old;
1158 } 1153 }
1159 } 1154 }