aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-11-18 15:24:32 -0500
committerDavid S. Miller <davem@davemloft.net>2011-11-18 15:24:32 -0500
commit9cc20b268a5a14f5e57b8ad405a83513ab0d78dc (patch)
treef0dc4bce125d845703ef6377d645a5ca9864e612 /net
parentfb120c0a2775b0d2d095a99ea8432bfb5d444ab2 (diff)
ipv4: fix redirect handling
commit f39925dbde77 (ipv4: Cache learned redirect information in inetpeer.) introduced a regression in ICMP redirect handling. It assumed ipv4_dst_check() would be called because all possible routes were attached to the inetpeer we modify in ip_rt_redirect(), but thats not true. commit 7cc9150ebe (route: fix ICMP redirect validation) tried to fix this but solution was not complete. (It fixed only one route) So we must lookup existing routes (including different TOS values) and call check_peer_redir() on them. Reported-by: Ivan Zahariev <famzah@icdsoft.com> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: Flavio Leitner <fbl@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/route.c110
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
1307static 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 */
1308void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, 1336void 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
1652static 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
1680static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) 1688static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
1681{ 1689{