diff options
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r-- | net/ipv4/route.c | 136 |
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) | |||
1294 | void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, | 1294 | void 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 | ||
1593 | static 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 | |||
1681 | static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) | 1618 | static 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; |