|
@@ -771,6 +771,28 @@ static __inline__ int neigh_max_probes(struct neighbour *n)
|
|
|
p->ucast_probes + p->app_probes + p->mcast_probes);
|
|
|
}
|
|
|
|
|
|
+static void neigh_invalidate(struct neighbour *neigh)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
|
|
|
+ NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
|
|
|
+ neigh->updated = jiffies;
|
|
|
+
|
|
|
+ /* It is very thin place. report_unreachable is very complicated
|
|
|
+ routine. Particularly, it can hit the same neighbour entry!
|
|
|
+
|
|
|
+ So that, we try to be accurate and avoid dead loop. --ANK
|
|
|
+ */
|
|
|
+ while (neigh->nud_state == NUD_FAILED &&
|
|
|
+ (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
|
|
|
+ write_unlock(&neigh->lock);
|
|
|
+ neigh->ops->error_report(neigh, skb);
|
|
|
+ write_lock(&neigh->lock);
|
|
|
+ }
|
|
|
+ skb_queue_purge(&neigh->arp_queue);
|
|
|
+}
|
|
|
+
|
|
|
/* Called when a timer expires for a neighbour entry. */
|
|
|
|
|
|
static void neigh_timer_handler(unsigned long arg)
|
|
@@ -835,26 +857,9 @@ static void neigh_timer_handler(unsigned long arg)
|
|
|
|
|
|
if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) &&
|
|
|
atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
|
|
|
- struct sk_buff *skb;
|
|
|
-
|
|
|
neigh->nud_state = NUD_FAILED;
|
|
|
- neigh->updated = jiffies;
|
|
|
notify = 1;
|
|
|
- NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
|
|
|
- NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
|
|
|
-
|
|
|
- /* It is very thin place. report_unreachable is very complicated
|
|
|
- routine. Particularly, it can hit the same neighbour entry!
|
|
|
-
|
|
|
- So that, we try to be accurate and avoid dead loop. --ANK
|
|
|
- */
|
|
|
- while (neigh->nud_state == NUD_FAILED &&
|
|
|
- (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
|
|
|
- write_unlock(&neigh->lock);
|
|
|
- neigh->ops->error_report(neigh, skb);
|
|
|
- write_lock(&neigh->lock);
|
|
|
- }
|
|
|
- skb_queue_purge(&neigh->arp_queue);
|
|
|
+ neigh_invalidate(neigh);
|
|
|
}
|
|
|
|
|
|
if (neigh->nud_state & NUD_IN_TIMER) {
|
|
@@ -1001,6 +1006,11 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
|
|
|
neigh->nud_state = new;
|
|
|
err = 0;
|
|
|
notify = old & NUD_VALID;
|
|
|
+ if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
|
|
|
+ (new & NUD_FAILED)) {
|
|
|
+ neigh_invalidate(neigh);
|
|
|
+ notify = 1;
|
|
|
+ }
|
|
|
goto out;
|
|
|
}
|
|
|
|