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 | { |
