|
@@ -1304,16 +1304,42 @@ static void rt_del(unsigned hash, struct rtable *rt)
|
|
|
spin_unlock_bh(rt_hash_lock_addr(hash));
|
|
|
}
|
|
|
|
|
|
+static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
|
|
|
+{
|
|
|
+ struct rtable *rt = (struct rtable *) dst;
|
|
|
+ __be32 orig_gw = rt->rt_gateway;
|
|
|
+ struct neighbour *n, *old_n;
|
|
|
+
|
|
|
+ dst_confirm(&rt->dst);
|
|
|
+
|
|
|
+ rt->rt_gateway = peer->redirect_learned.a4;
|
|
|
+
|
|
|
+ n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway);
|
|
|
+ if (IS_ERR(n))
|
|
|
+ return PTR_ERR(n);
|
|
|
+ old_n = xchg(&rt->dst._neighbour, n);
|
|
|
+ if (old_n)
|
|
|
+ neigh_release(old_n);
|
|
|
+ if (!n || !(n->nud_state & NUD_VALID)) {
|
|
|
+ if (n)
|
|
|
+ neigh_event_send(n, NULL);
|
|
|
+ rt->rt_gateway = orig_gw;
|
|
|
+ return -EAGAIN;
|
|
|
+ } else {
|
|
|
+ rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
+ call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* called in rcu_read_lock() section */
|
|
|
void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|
|
|
__be32 saddr, struct net_device *dev)
|
|
|
{
|
|
|
int s, i;
|
|
|
struct in_device *in_dev = __in_dev_get_rcu(dev);
|
|
|
- struct rtable *rt;
|
|
|
__be32 skeys[2] = { saddr, 0 };
|
|
|
int ikeys[2] = { dev->ifindex, 0 };
|
|
|
- struct flowi4 fl4;
|
|
|
struct inet_peer *peer;
|
|
|
struct net *net;
|
|
|
|
|
@@ -1336,33 +1362,42 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|
|
|
goto reject_redirect;
|
|
|
}
|
|
|
|
|
|
- memset(&fl4, 0, sizeof(fl4));
|
|
|
- fl4.daddr = daddr;
|
|
|
for (s = 0; s < 2; s++) {
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
- fl4.flowi4_oif = ikeys[i];
|
|
|
- fl4.saddr = skeys[s];
|
|
|
- rt = __ip_route_output_key(net, &fl4);
|
|
|
- if (IS_ERR(rt))
|
|
|
- continue;
|
|
|
-
|
|
|
- if (rt->dst.error || rt->dst.dev != dev ||
|
|
|
- rt->rt_gateway != old_gw) {
|
|
|
- ip_rt_put(rt);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ unsigned int hash;
|
|
|
+ struct rtable __rcu **rthp;
|
|
|
+ struct rtable *rt;
|
|
|
+
|
|
|
+ hash = rt_hash(daddr, skeys[s], ikeys[i], rt_genid(net));
|
|
|
+
|
|
|
+ rthp = &rt_hash_table[hash].chain;
|
|
|
+
|
|
|
+ while ((rt = rcu_dereference(*rthp)) != NULL) {
|
|
|
+ rthp = &rt->dst.rt_next;
|
|
|
+
|
|
|
+ if (rt->rt_key_dst != daddr ||
|
|
|
+ rt->rt_key_src != skeys[s] ||
|
|
|
+ rt->rt_oif != ikeys[i] ||
|
|
|
+ rt_is_input_route(rt) ||
|
|
|
+ rt_is_expired(rt) ||
|
|
|
+ !net_eq(dev_net(rt->dst.dev), net) ||
|
|
|
+ rt->dst.error ||
|
|
|
+ rt->dst.dev != dev ||
|
|
|
+ rt->rt_gateway != old_gw)
|
|
|
+ continue;
|
|
|
|
|
|
- if (!rt->peer)
|
|
|
- rt_bind_peer(rt, rt->rt_dst, 1);
|
|
|
+ if (!rt->peer)
|
|
|
+ rt_bind_peer(rt, rt->rt_dst, 1);
|
|
|
|
|
|
- peer = rt->peer;
|
|
|
- if (peer) {
|
|
|
- peer->redirect_learned.a4 = new_gw;
|
|
|
- atomic_inc(&__rt_peer_genid);
|
|
|
+ peer = rt->peer;
|
|
|
+ if (peer) {
|
|
|
+ if (peer->redirect_learned.a4 != new_gw) {
|
|
|
+ peer->redirect_learned.a4 = new_gw;
|
|
|
+ atomic_inc(&__rt_peer_genid);
|
|
|
+ }
|
|
|
+ check_peer_redir(&rt->dst, peer);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- ip_rt_put(rt);
|
|
|
- return;
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
@@ -1649,33 +1684,6 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
|
|
|
-{
|
|
|
- struct rtable *rt = (struct rtable *) dst;
|
|
|
- __be32 orig_gw = rt->rt_gateway;
|
|
|
- struct neighbour *n, *old_n;
|
|
|
-
|
|
|
- dst_confirm(&rt->dst);
|
|
|
-
|
|
|
- rt->rt_gateway = peer->redirect_learned.a4;
|
|
|
-
|
|
|
- n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway);
|
|
|
- if (IS_ERR(n))
|
|
|
- return PTR_ERR(n);
|
|
|
- old_n = xchg(&rt->dst._neighbour, n);
|
|
|
- if (old_n)
|
|
|
- neigh_release(old_n);
|
|
|
- if (!n || !(n->nud_state & NUD_VALID)) {
|
|
|
- if (n)
|
|
|
- neigh_event_send(n, NULL);
|
|
|
- rt->rt_gateway = orig_gw;
|
|
|
- return -EAGAIN;
|
|
|
- } else {
|
|
|
- rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
- call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
|
|
|
static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
{
|