aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Benc <jbenc@redhat.com>2013-12-11 07:48:20 -0500
committerDavid S. Miller <davem@davemloft.net>2013-12-11 16:02:58 -0500
commit7e9805696428113e34625a65a30dbc62cb78acc5 (patch)
tree3d5c13ae988049a6b4e53ff586826ee7779627a6
parente47726683803616c773bc75c88cfb3eb54a8172b (diff)
ipv6: router reachability probing
RFC 4191 states in 3.5: When a host avoids using any non-reachable router X and instead sends a data packet to another router Y, and the host would have used router X if router X were reachable, then the host SHOULD probe each such router X's reachability by sending a single Neighbor Solicitation to that router's address. A host MUST NOT probe a router's reachability in the absence of useful traffic that the host would have sent to the router if it were reachable. In any case, these probes MUST be rate-limited to no more than one per minute per router. Currently, when the neighbour corresponding to a router falls into NUD_FAILED, it's never considered again. Introduce a new rt6_nud_state value, RT6_NUD_FAIL_PROBE, which suggests the route should not be used but should be probed with a single NS. The probe is ratelimited by the existing code. To better distinguish meanings of the failure values, rename RT6_NUD_FAIL_SOFT to RT6_NUD_FAIL_DO_RR. Signed-off-by: Jiri Benc <jbenc@redhat.com> Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/neighbour.h1
-rw-r--r--net/core/neighbour.c15
-rw-r--r--net/ipv6/route.c16
3 files changed, 26 insertions, 6 deletions
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 41b1ce6c96a8..4c09bd23b832 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -252,6 +252,7 @@ static inline struct neighbour *neigh_create(struct neigh_table *tbl,
252void neigh_destroy(struct neighbour *neigh); 252void neigh_destroy(struct neighbour *neigh);
253int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); 253int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
254int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags); 254int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags);
255void __neigh_set_probe_once(struct neighbour *neigh);
255void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); 256void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);
256int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); 257int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
257int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); 258int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 8be82a7f0e0f..bf6f404c04aa 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1238,6 +1238,21 @@ out:
1238} 1238}
1239EXPORT_SYMBOL(neigh_update); 1239EXPORT_SYMBOL(neigh_update);
1240 1240
1241/* Update the neigh to listen temporarily for probe responses, even if it is
1242 * in a NUD_FAILED state. The caller has to hold neigh->lock for writing.
1243 */
1244void __neigh_set_probe_once(struct neighbour *neigh)
1245{
1246 neigh->updated = jiffies;
1247 if (!(neigh->nud_state & NUD_FAILED))
1248 return;
1249 neigh->nud_state = NUD_PROBE;
1250 atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES));
1251 neigh_add_timer(neigh,
1252 jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME));
1253}
1254EXPORT_SYMBOL(__neigh_set_probe_once);
1255
1241struct neighbour *neigh_event_ns(struct neigh_table *tbl, 1256struct neighbour *neigh_event_ns(struct neigh_table *tbl,
1242 u8 *lladdr, void *saddr, 1257 u8 *lladdr, void *saddr,
1243 struct net_device *dev) 1258 struct net_device *dev)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index ddb9d41c8eea..a1a57523b158 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -66,8 +66,9 @@
66#endif 66#endif
67 67
68enum rt6_nud_state { 68enum rt6_nud_state {
69 RT6_NUD_FAIL_HARD = -2, 69 RT6_NUD_FAIL_HARD = -3,
70 RT6_NUD_FAIL_SOFT = -1, 70 RT6_NUD_FAIL_PROBE = -2,
71 RT6_NUD_FAIL_DO_RR = -1,
71 RT6_NUD_SUCCEED = 1 72 RT6_NUD_SUCCEED = 1
72}; 73};
73 74
@@ -521,7 +522,7 @@ static void rt6_probe(struct rt6_info *rt)
521 work = kmalloc(sizeof(*work), GFP_ATOMIC); 522 work = kmalloc(sizeof(*work), GFP_ATOMIC);
522 523
523 if (neigh && work) 524 if (neigh && work)
524 neigh->updated = jiffies; 525 __neigh_set_probe_once(neigh);
525 526
526 if (neigh) 527 if (neigh)
527 write_unlock(&neigh->lock); 528 write_unlock(&neigh->lock);
@@ -577,11 +578,13 @@ static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
577#ifdef CONFIG_IPV6_ROUTER_PREF 578#ifdef CONFIG_IPV6_ROUTER_PREF
578 else if (!(neigh->nud_state & NUD_FAILED)) 579 else if (!(neigh->nud_state & NUD_FAILED))
579 ret = RT6_NUD_SUCCEED; 580 ret = RT6_NUD_SUCCEED;
581 else
582 ret = RT6_NUD_FAIL_PROBE;
580#endif 583#endif
581 read_unlock(&neigh->lock); 584 read_unlock(&neigh->lock);
582 } else { 585 } else {
583 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 586 ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
584 RT6_NUD_SUCCEED : RT6_NUD_FAIL_SOFT; 587 RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
585 } 588 }
586 rcu_read_unlock_bh(); 589 rcu_read_unlock_bh();
587 590
@@ -618,16 +621,17 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
618 goto out; 621 goto out;
619 622
620 m = rt6_score_route(rt, oif, strict); 623 m = rt6_score_route(rt, oif, strict);
621 if (m == RT6_NUD_FAIL_SOFT) { 624 if (m == RT6_NUD_FAIL_DO_RR) {
622 match_do_rr = true; 625 match_do_rr = true;
623 m = 0; /* lowest valid score */ 626 m = 0; /* lowest valid score */
624 } else if (m < 0) { 627 } else if (m == RT6_NUD_FAIL_HARD) {
625 goto out; 628 goto out;
626 } 629 }
627 630
628 if (strict & RT6_LOOKUP_F_REACHABLE) 631 if (strict & RT6_LOOKUP_F_REACHABLE)
629 rt6_probe(rt); 632 rt6_probe(rt);
630 633
634 /* note that m can be RT6_NUD_FAIL_PROBE at this point */
631 if (m > *mpri) { 635 if (m > *mpri) {
632 *do_rr = match_do_rr; 636 *do_rr = match_do_rr;
633 *mpri = m; 637 *mpri = m;