forked from mirrors/linux
		
	ipv4: Cache learned redirect information in inetpeer.
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>
This commit is contained in:
		
							parent
							
								
									2c8cec5c10
								
							
						
					
					
						commit
						f39925dbde
					
				
					 1 changed files with 42 additions and 94 deletions
				
			
		
							
								
								
									
										136
									
								
								net/ipv4/route.c
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								net/ipv4/route.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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));
 | 
			
		||||
	peer = inet_getpeer_v4(daddr, 1);
 | 
			
		||||
	if (peer) {
 | 
			
		||||
		peer->redirect_learned.a4 = new_gw;
 | 
			
		||||
 | 
			
		||||
			rthp = &rt_hash_table[hash].chain;
 | 
			
		||||
		inet_putpeer(peer);
 | 
			
		||||
 | 
			
		||||
			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;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				netevent.old = &rth->dst;
 | 
			
		||||
				netevent.new = &rt->dst;
 | 
			
		||||
				call_netevent_notifiers(NETEVENT_REDIRECT,
 | 
			
		||||
							&netevent);
 | 
			
		||||
 | 
			
		||||
				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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue