diff options
-rw-r--r-- | net/ipv4/route.c | 110 |
1 files changed, 59 insertions, 51 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 511f4a75149c..0c74da8a0473 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1304,16 +1304,42 @@ static void rt_del(unsigned hash, struct rtable *rt) | |||
1304 | spin_unlock_bh(rt_hash_lock_addr(hash)); | 1304 | spin_unlock_bh(rt_hash_lock_addr(hash)); |
1305 | } | 1305 | } |
1306 | 1306 | ||
1307 | static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) | ||
1308 | { | ||
1309 | struct rtable *rt = (struct rtable *) dst; | ||
1310 | __be32 orig_gw = rt->rt_gateway; | ||
1311 | struct neighbour *n, *old_n; | ||
1312 | |||
1313 | dst_confirm(&rt->dst); | ||
1314 | |||
1315 | rt->rt_gateway = peer->redirect_learned.a4; | ||
1316 | |||
1317 | n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway); | ||
1318 | if (IS_ERR(n)) | ||
1319 | return PTR_ERR(n); | ||
1320 | old_n = xchg(&rt->dst._neighbour, n); | ||
1321 | if (old_n) | ||
1322 | neigh_release(old_n); | ||
1323 | if (!n || !(n->nud_state & NUD_VALID)) { | ||
1324 | if (n) | ||
1325 | neigh_event_send(n, NULL); | ||
1326 | rt->rt_gateway = orig_gw; | ||
1327 | return -EAGAIN; | ||
1328 | } else { | ||
1329 | rt->rt_flags |= RTCF_REDIRECTED; | ||
1330 | call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); | ||
1331 | } | ||
1332 | return 0; | ||
1333 | } | ||
1334 | |||
1307 | /* called in rcu_read_lock() section */ | 1335 | /* called in rcu_read_lock() section */ |
1308 | void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, | 1336 | void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, |
1309 | __be32 saddr, struct net_device *dev) | 1337 | __be32 saddr, struct net_device *dev) |
1310 | { | 1338 | { |
1311 | int s, i; | 1339 | int s, i; |
1312 | struct in_device *in_dev = __in_dev_get_rcu(dev); | 1340 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
1313 | struct rtable *rt; | ||
1314 | __be32 skeys[2] = { saddr, 0 }; | 1341 | __be32 skeys[2] = { saddr, 0 }; |
1315 | int ikeys[2] = { dev->ifindex, 0 }; | 1342 | int ikeys[2] = { dev->ifindex, 0 }; |
1316 | struct flowi4 fl4; | ||
1317 | struct inet_peer *peer; | 1343 | struct inet_peer *peer; |
1318 | struct net *net; | 1344 | struct net *net; |
1319 | 1345 | ||
@@ -1336,33 +1362,42 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, | |||
1336 | goto reject_redirect; | 1362 | goto reject_redirect; |
1337 | } | 1363 | } |
1338 | 1364 | ||
1339 | memset(&fl4, 0, sizeof(fl4)); | ||
1340 | fl4.daddr = daddr; | ||
1341 | for (s = 0; s < 2; s++) { | 1365 | for (s = 0; s < 2; s++) { |
1342 | for (i = 0; i < 2; i++) { | 1366 | for (i = 0; i < 2; i++) { |
1343 | fl4.flowi4_oif = ikeys[i]; | 1367 | unsigned int hash; |
1344 | fl4.saddr = skeys[s]; | 1368 | struct rtable __rcu **rthp; |
1345 | rt = __ip_route_output_key(net, &fl4); | 1369 | struct rtable *rt; |
1346 | if (IS_ERR(rt)) | 1370 | |
1347 | continue; | 1371 | hash = rt_hash(daddr, skeys[s], ikeys[i], rt_genid(net)); |
1348 | 1372 | ||
1349 | if (rt->dst.error || rt->dst.dev != dev || | 1373 | rthp = &rt_hash_table[hash].chain; |
1350 | rt->rt_gateway != old_gw) { | 1374 | |
1351 | ip_rt_put(rt); | 1375 | while ((rt = rcu_dereference(*rthp)) != NULL) { |
1352 | continue; | 1376 | rthp = &rt->dst.rt_next; |
1353 | } | 1377 | |
1378 | if (rt->rt_key_dst != daddr || | ||
1379 | rt->rt_key_src != skeys[s] || | ||
1380 | rt->rt_oif != ikeys[i] || | ||
1381 | rt_is_input_route(rt) || | ||
1382 | rt_is_expired(rt) || | ||
1383 | !net_eq(dev_net(rt->dst.dev), net) || | ||
1384 | rt->dst.error || | ||
1385 | rt->dst.dev != dev || | ||
1386 | rt->rt_gateway != old_gw) | ||
1387 | continue; | ||
1354 | 1388 | ||
1355 | if (!rt->peer) | 1389 | if (!rt->peer) |
1356 | rt_bind_peer(rt, rt->rt_dst, 1); | 1390 | rt_bind_peer(rt, rt->rt_dst, 1); |
1357 | 1391 | ||
1358 | peer = rt->peer; | 1392 | peer = rt->peer; |
1359 | if (peer) { | 1393 | if (peer) { |
1360 | peer->redirect_learned.a4 = new_gw; | 1394 | if (peer->redirect_learned.a4 != new_gw) { |
1361 | atomic_inc(&__rt_peer_genid); | 1395 | peer->redirect_learned.a4 = new_gw; |
1396 | atomic_inc(&__rt_peer_genid); | ||
1397 | } | ||
1398 | check_peer_redir(&rt->dst, peer); | ||
1399 | } | ||
1362 | } | 1400 | } |
1363 | |||
1364 | ip_rt_put(rt); | ||
1365 | return; | ||
1366 | } | 1401 | } |
1367 | } | 1402 | } |
1368 | return; | 1403 | return; |
@@ -1649,33 +1684,6 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) | |||
1649 | } | 1684 | } |
1650 | } | 1685 | } |
1651 | 1686 | ||
1652 | static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) | ||
1653 | { | ||
1654 | struct rtable *rt = (struct rtable *) dst; | ||
1655 | __be32 orig_gw = rt->rt_gateway; | ||
1656 | struct neighbour *n, *old_n; | ||
1657 | |||
1658 | dst_confirm(&rt->dst); | ||
1659 | |||
1660 | rt->rt_gateway = peer->redirect_learned.a4; | ||
1661 | |||
1662 | n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway); | ||
1663 | if (IS_ERR(n)) | ||
1664 | return PTR_ERR(n); | ||
1665 | old_n = xchg(&rt->dst._neighbour, n); | ||
1666 | if (old_n) | ||
1667 | neigh_release(old_n); | ||
1668 | if (!n || !(n->nud_state & NUD_VALID)) { | ||
1669 | if (n) | ||
1670 | neigh_event_send(n, NULL); | ||
1671 | rt->rt_gateway = orig_gw; | ||
1672 | return -EAGAIN; | ||
1673 | } else { | ||
1674 | rt->rt_flags |= RTCF_REDIRECTED; | ||
1675 | call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); | ||
1676 | } | ||
1677 | return 0; | ||
1678 | } | ||
1679 | 1687 | ||
1680 | static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) | 1688 | static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) |
1681 | { | 1689 | { |