]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ipv4: Cache learned redirect information in inetpeer.
authorDavid S. Miller <davem@davemloft.net>
Thu, 10 Feb 2011 06:00:16 +0000 (22:00 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 15 Feb 2011 05:33:27 +0000 (21:33 -0800)
Note that we do not generate the redirect netevent any longer,
because we don't create a new cached route.

Instead, once the new neighbour is bound to the cached route,
we emit a neigh update event instead.

Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/route.c

index 11faf14c743021f3c2c6688ea97686fea124a09f..756f5443b5f7e0aa5b4ee559310824031c7fdcc9 100644 (file)
@@ -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;