|
@@ -1294,13 +1294,8 @@ static void rt_del(unsigned hash, struct rtable *rt)
|
|
|
void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|
|
|
__be32 saddr, struct net_device *dev)
|
|
|
{
|
|
|
- int i, k;
|
|
|
struct in_device *in_dev = __in_dev_get_rcu(dev);
|
|
|
- struct rtable *rth;
|
|
|
- struct rtable __rcu **rthp;
|
|
|
- __be32 skeys[2] = { saddr, 0 };
|
|
|
- int ikeys[2] = { dev->ifindex, 0 };
|
|
|
- struct netevent_redirect netevent;
|
|
|
+ struct inet_peer *peer;
|
|
|
struct net *net;
|
|
|
|
|
|
if (!in_dev)
|
|
@@ -1312,9 +1307,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|
|
|
ipv4_is_zeronet(new_gw))
|
|
|
goto reject_redirect;
|
|
|
|
|
|
- if (!rt_caching(net))
|
|
|
- goto reject_redirect;
|
|
|
-
|
|
|
if (!IN_DEV_SHARED_MEDIA(in_dev)) {
|
|
|
if (!inet_addr_onlink(in_dev, new_gw, old_gw))
|
|
|
goto reject_redirect;
|
|
@@ -1325,93 +1317,13 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|
|
|
goto reject_redirect;
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
- for (k = 0; k < 2; k++) {
|
|
|
- unsigned hash = rt_hash(daddr, skeys[i], ikeys[k],
|
|
|
- rt_genid(net));
|
|
|
-
|
|
|
- rthp = &rt_hash_table[hash].chain;
|
|
|
-
|
|
|
- while ((rth = rcu_dereference(*rthp)) != NULL) {
|
|
|
- struct rtable *rt;
|
|
|
-
|
|
|
- if (rth->fl.fl4_dst != daddr ||
|
|
|
- rth->fl.fl4_src != skeys[i] ||
|
|
|
- rth->fl.oif != ikeys[k] ||
|
|
|
- rt_is_input_route(rth) ||
|
|
|
- rt_is_expired(rth) ||
|
|
|
- !net_eq(dev_net(rth->dst.dev), net)) {
|
|
|
- rthp = &rth->dst.rt_next;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (rth->rt_dst != daddr ||
|
|
|
- rth->rt_src != saddr ||
|
|
|
- rth->dst.error ||
|
|
|
- rth->rt_gateway != old_gw ||
|
|
|
- rth->dst.dev != dev)
|
|
|
- break;
|
|
|
-
|
|
|
- dst_hold(&rth->dst);
|
|
|
-
|
|
|
- rt = dst_alloc(&ipv4_dst_ops);
|
|
|
- if (rt == NULL) {
|
|
|
- ip_rt_put(rth);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* Copy all the information. */
|
|
|
- *rt = *rth;
|
|
|
- rt->dst.__use = 1;
|
|
|
- atomic_set(&rt->dst.__refcnt, 1);
|
|
|
- rt->dst.child = NULL;
|
|
|
- if (rt->dst.dev)
|
|
|
- dev_hold(rt->dst.dev);
|
|
|
- rt->dst.obsolete = -1;
|
|
|
- rt->dst.lastuse = jiffies;
|
|
|
- rt->dst.path = &rt->dst;
|
|
|
- rt->dst.neighbour = NULL;
|
|
|
- rt->dst.hh = NULL;
|
|
|
-#ifdef CONFIG_XFRM
|
|
|
- rt->dst.xfrm = NULL;
|
|
|
-#endif
|
|
|
- rt->rt_genid = rt_genid(net);
|
|
|
- rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
-
|
|
|
- /* Gateway is different ... */
|
|
|
- rt->rt_gateway = new_gw;
|
|
|
-
|
|
|
- /* Redirect received -> path was valid */
|
|
|
- dst_confirm(&rth->dst);
|
|
|
-
|
|
|
- if (rt->peer)
|
|
|
- atomic_inc(&rt->peer->refcnt);
|
|
|
- if (rt->fi)
|
|
|
- atomic_inc(&rt->fi->fib_clntref);
|
|
|
-
|
|
|
- if (arp_bind_neighbour(&rt->dst) ||
|
|
|
- !(rt->dst.neighbour->nud_state &
|
|
|
- NUD_VALID)) {
|
|
|
- if (rt->dst.neighbour)
|
|
|
- neigh_event_send(rt->dst.neighbour, NULL);
|
|
|
- ip_rt_put(rth);
|
|
|
- rt_drop(rt);
|
|
|
- goto do_next;
|
|
|
- }
|
|
|
+ peer = inet_getpeer_v4(daddr, 1);
|
|
|
+ if (peer) {
|
|
|
+ peer->redirect_learned.a4 = new_gw;
|
|
|
|
|
|
- netevent.old = &rth->dst;
|
|
|
- netevent.new = &rt->dst;
|
|
|
- call_netevent_notifiers(NETEVENT_REDIRECT,
|
|
|
- &netevent);
|
|
|
+ inet_putpeer(peer);
|
|
|
|
|
|
- rt_del(hash, rth);
|
|
|
- if (!rt_intern_hash(hash, rt, &rt, NULL, rt->fl.oif))
|
|
|
- ip_rt_put(rt);
|
|
|
- goto do_next;
|
|
|
- }
|
|
|
- do_next:
|
|
|
- ;
|
|
|
- }
|
|
|
+ atomic_inc(&__rt_peer_genid);
|
|
|
}
|
|
|
return;
|
|
|
|
|
@@ -1678,6 +1590,31 @@ 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;
|
|
|
+
|
|
|
+ dst_confirm(&rt->dst);
|
|
|
+
|
|
|
+ neigh_release(rt->dst.neighbour);
|
|
|
+ rt->dst.neighbour = NULL;
|
|
|
+
|
|
|
+ rt->rt_gateway = peer->redirect_learned.a4;
|
|
|
+ if (arp_bind_neighbour(&rt->dst) ||
|
|
|
+ !(rt->dst.neighbour->nud_state & NUD_VALID)) {
|
|
|
+ if (rt->dst.neighbour)
|
|
|
+ neigh_event_send(rt->dst.neighbour, NULL);
|
|
|
+ rt->rt_gateway = orig_gw;
|
|
|
+ return -EAGAIN;
|
|
|
+ } else {
|
|
|
+ rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
+ call_netevent_notifiers(NETEVENT_NEIGH_UPDATE,
|
|
|
+ rt->dst.neighbour);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
{
|
|
|
struct rtable *rt = (struct rtable *) dst;
|
|
@@ -1694,6 +1631,12 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
if (peer && peer->pmtu_expires)
|
|
|
check_peer_pmtu(dst, peer);
|
|
|
|
|
|
+ if (peer && peer->redirect_learned.a4 &&
|
|
|
+ peer->redirect_learned.a4 != rt->rt_gateway) {
|
|
|
+ if (check_peer_redir(dst, peer))
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
rt->rt_peer_genid = rt_peer_genid();
|
|
|
}
|
|
|
return dst;
|
|
@@ -1830,6 +1773,11 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi)
|
|
|
|
|
|
if (peer->pmtu_expires)
|
|
|
check_peer_pmtu(&rt->dst, peer);
|
|
|
+ if (peer->redirect_learned.a4 &&
|
|
|
+ peer->redirect_learned.a4 != rt->rt_gateway) {
|
|
|
+ rt->rt_gateway = peer->redirect_learned.a4;
|
|
|
+ rt->rt_flags |= RTCF_REDIRECTED;
|
|
|
+ }
|
|
|
} else {
|
|
|
if (fi->fib_metrics != (u32 *) dst_default_metrics) {
|
|
|
rt->fi = fi;
|