aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/route.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-02-10 01:00:16 -0500
committerDavid S. Miller <davem@davemloft.net>2011-02-15 00:33:27 -0500
commitf39925dbde7788cfb96419c0f092b086aa325c0f (patch)
tree4e236c5287558f9803bea163e00d919c05ba5cc7 /net/ipv4/route.c
parent2c8cec5c10bced2408082a6656170e74ac17231c (diff)
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>
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r--net/ipv4/route.c136
1 files changed, 42 insertions, 94 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 11faf14c7430..756f5443b5f7 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1294,13 +1294,8 @@ static void rt_del(unsigned hash, struct rtable *rt)
1294void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, 1294void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
1295 __be32 saddr, struct net_device *dev) 1295 __be32 saddr, struct net_device *dev)
1296{ 1296{
1297 int i, k;
1298 struct in_device *in_dev = __in_dev_get_rcu(dev); 1297 struct in_device *in_dev = __in_dev_get_rcu(dev);
1299 struct rtable *rth; 1298 struct inet_peer *peer;
1300 struct rtable __rcu **rthp;
1301 __be32 skeys[2] = { saddr, 0 };
1302 int ikeys[2] = { dev->ifindex, 0 };
1303 struct netevent_redirect netevent;
1304 struct net *net; 1299 struct net *net;
1305 1300
1306 if (!in_dev) 1301 if (!in_dev)
@@ -1312,9 +1307,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
1312 ipv4_is_zeronet(new_gw)) 1307 ipv4_is_zeronet(new_gw))
1313 goto reject_redirect; 1308 goto reject_redirect;
1314 1309
1315 if (!rt_caching(net))
1316 goto reject_redirect;
1317
1318 if (!IN_DEV_SHARED_MEDIA(in_dev)) { 1310 if (!IN_DEV_SHARED_MEDIA(in_dev)) {
1319 if (!inet_addr_onlink(in_dev, new_gw, old_gw)) 1311 if (!inet_addr_onlink(in_dev, new_gw, old_gw))
1320 goto reject_redirect; 1312 goto reject_redirect;
@@ -1325,93 +1317,13 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
1325 goto reject_redirect; 1317 goto reject_redirect;
1326 } 1318 }
1327 1319
1328 for (i = 0; i < 2; i++) { 1320 peer = inet_getpeer_v4(daddr, 1);
1329 for (k = 0; k < 2; k++) { 1321 if (peer) {
1330 unsigned hash = rt_hash(daddr, skeys[i], ikeys[k], 1322 peer->redirect_learned.a4 = new_gw;
1331 rt_genid(net));
1332
1333 rthp = &rt_hash_table[hash].chain;
1334
1335 while ((rth = rcu_dereference(*rthp)) != NULL) {
1336 struct rtable *rt;
1337
1338 if (rth->fl.fl4_dst != daddr ||
1339 rth->fl.fl4_src != skeys[i] ||
1340 rth->fl.oif != ikeys[k] ||
1341 rt_is_input_route(rth) ||
1342 rt_is_expired(rth) ||
1343 !net_eq(dev_net(rth->dst.dev), net)) {
1344 rthp = &rth->dst.rt_next;
1345 continue;
1346 }
1347
1348 if (rth->rt_dst != daddr ||
1349 rth->rt_src != saddr ||
1350 rth->dst.error ||
1351 rth->rt_gateway != old_gw ||
1352 rth->dst.dev != dev)
1353 break;
1354
1355 dst_hold(&rth->dst);
1356
1357 rt = dst_alloc(&ipv4_dst_ops);
1358 if (rt == NULL) {
1359 ip_rt_put(rth);
1360 return;
1361 }
1362
1363 /* Copy all the information. */
1364 *rt = *rth;
1365 rt->dst.__use = 1;
1366 atomic_set(&rt->dst.__refcnt, 1);
1367 rt->dst.child = NULL;
1368 if (rt->dst.dev)
1369 dev_hold(rt->dst.dev);
1370 rt->dst.obsolete = -1;
1371 rt->dst.lastuse = jiffies;
1372 rt->dst.path = &rt->dst;
1373 rt->dst.neighbour = NULL;
1374 rt->dst.hh = NULL;
1375#ifdef CONFIG_XFRM
1376 rt->dst.xfrm = NULL;
1377#endif
1378 rt->rt_genid = rt_genid(net);
1379 rt->rt_flags |= RTCF_REDIRECTED;
1380
1381 /* Gateway is different ... */
1382 rt->rt_gateway = new_gw;
1383
1384 /* Redirect received -> path was valid */
1385 dst_confirm(&rth->dst);
1386
1387 if (rt->peer)
1388 atomic_inc(&rt->peer->refcnt);
1389 if (rt->fi)
1390 atomic_inc(&rt->fi->fib_clntref);
1391
1392 if (arp_bind_neighbour(&rt->dst) ||
1393 !(rt->dst.neighbour->nud_state &
1394 NUD_VALID)) {
1395 if (rt->dst.neighbour)
1396 neigh_event_send(rt->dst.neighbour, NULL);
1397 ip_rt_put(rth);
1398 rt_drop(rt);
1399 goto do_next;
1400 }
1401 1323
1402 netevent.old = &rth->dst; 1324 inet_putpeer(peer);
1403 netevent.new = &rt->dst;
1404 call_netevent_notifiers(NETEVENT_REDIRECT,
1405 &netevent);
1406 1325
1407 rt_del(hash, rth); 1326 atomic_inc(&__rt_peer_genid);
1408 if (!rt_intern_hash(hash, rt, &rt, NULL, rt->fl.oif))
1409 ip_rt_put(rt);
1410 goto do_next;
1411 }
1412 do_next:
1413 ;
1414 }
1415 } 1327 }
1416 return; 1328 return;
1417 1329
@@ -1678,6 +1590,31 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
1678 } 1590 }
1679} 1591}
1680 1592
1593static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
1594{
1595 struct rtable *rt = (struct rtable *) dst;
1596 __be32 orig_gw = rt->rt_gateway;
1597
1598 dst_confirm(&rt->dst);
1599
1600 neigh_release(rt->dst.neighbour);
1601 rt->dst.neighbour = NULL;
1602
1603 rt->rt_gateway = peer->redirect_learned.a4;
1604 if (arp_bind_neighbour(&rt->dst) ||
1605 !(rt->dst.neighbour->nud_state & NUD_VALID)) {
1606 if (rt->dst.neighbour)
1607 neigh_event_send(rt->dst.neighbour, NULL);
1608 rt->rt_gateway = orig_gw;
1609 return -EAGAIN;
1610 } else {
1611 rt->rt_flags |= RTCF_REDIRECTED;
1612 call_netevent_notifiers(NETEVENT_NEIGH_UPDATE,
1613 rt->dst.neighbour);
1614 }
1615 return 0;
1616}
1617
1681static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) 1618static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
1682{ 1619{
1683 struct rtable *rt = (struct rtable *) dst; 1620 struct rtable *rt = (struct rtable *) dst;
@@ -1694,6 +1631,12 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
1694 if (peer && peer->pmtu_expires) 1631 if (peer && peer->pmtu_expires)
1695 check_peer_pmtu(dst, peer); 1632 check_peer_pmtu(dst, peer);
1696 1633
1634 if (peer && peer->redirect_learned.a4 &&
1635 peer->redirect_learned.a4 != rt->rt_gateway) {
1636 if (check_peer_redir(dst, peer))
1637 return NULL;
1638 }
1639
1697 rt->rt_peer_genid = rt_peer_genid(); 1640 rt->rt_peer_genid = rt_peer_genid();
1698 } 1641 }
1699 return dst; 1642 return dst;
@@ -1830,6 +1773,11 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi)
1830 1773
1831 if (peer->pmtu_expires) 1774 if (peer->pmtu_expires)
1832 check_peer_pmtu(&rt->dst, peer); 1775 check_peer_pmtu(&rt->dst, peer);
1776 if (peer->redirect_learned.a4 &&
1777 peer->redirect_learned.a4 != rt->rt_gateway) {
1778 rt->rt_gateway = peer->redirect_learned.a4;
1779 rt->rt_flags |= RTCF_REDIRECTED;
1780 }
1833 } else { 1781 } else {
1834 if (fi->fib_metrics != (u32 *) dst_default_metrics) { 1782 if (fi->fib_metrics != (u32 *) dst_default_metrics) {
1835 rt->fi = fi; 1783 rt->fi = fi;