diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/icmp.c | 3 | ||||
-rw-r--r-- | net/ipv4/route.c | 38 |
2 files changed, 24 insertions, 17 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index c67d00e8c600..87397351ddac 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -691,7 +691,8 @@ static void icmp_unreach(struct sk_buff *skb) | |||
691 | NIPQUAD(iph->daddr)); | 691 | NIPQUAD(iph->daddr)); |
692 | } else { | 692 | } else { |
693 | info = ip_rt_frag_needed(net, iph, | 693 | info = ip_rt_frag_needed(net, iph, |
694 | ntohs(icmph->un.frag.mtu)); | 694 | ntohs(icmph->un.frag.mtu), |
695 | skb->dev); | ||
695 | if (!info) | 696 | if (!info) |
696 | goto out; | 697 | goto out; |
697 | } | 698 | } |
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index ce25a13f3430..5e3685c5c407 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1430,11 +1430,13 @@ static inline unsigned short guess_mtu(unsigned short old_mtu) | |||
1430 | } | 1430 | } |
1431 | 1431 | ||
1432 | unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, | 1432 | unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, |
1433 | unsigned short new_mtu) | 1433 | unsigned short new_mtu, |
1434 | struct net_device *dev) | ||
1434 | { | 1435 | { |
1435 | int i; | 1436 | int i, k; |
1436 | unsigned short old_mtu = ntohs(iph->tot_len); | 1437 | unsigned short old_mtu = ntohs(iph->tot_len); |
1437 | struct rtable *rth; | 1438 | struct rtable *rth; |
1439 | int ikeys[2] = { dev->ifindex, 0 }; | ||
1438 | __be32 skeys[2] = { iph->saddr, 0, }; | 1440 | __be32 skeys[2] = { iph->saddr, 0, }; |
1439 | __be32 daddr = iph->daddr; | 1441 | __be32 daddr = iph->daddr; |
1440 | unsigned short est_mtu = 0; | 1442 | unsigned short est_mtu = 0; |
@@ -1442,22 +1444,26 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, | |||
1442 | if (ipv4_config.no_pmtu_disc) | 1444 | if (ipv4_config.no_pmtu_disc) |
1443 | return 0; | 1445 | return 0; |
1444 | 1446 | ||
1445 | for (i = 0; i < 2; i++) { | 1447 | for (k = 0; k < 2; k++) { |
1446 | unsigned hash = rt_hash(daddr, skeys[i], 0); | 1448 | for (i = 0; i < 2; i++) { |
1449 | unsigned hash = rt_hash(daddr, skeys[i], ikeys[k]); | ||
1447 | 1450 | ||
1448 | rcu_read_lock(); | 1451 | rcu_read_lock(); |
1449 | for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; | 1452 | for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; |
1450 | rth = rcu_dereference(rth->u.dst.rt_next)) { | 1453 | rth = rcu_dereference(rth->u.dst.rt_next)) { |
1451 | if (rth->fl.fl4_dst == daddr && | ||
1452 | rth->fl.fl4_src == skeys[i] && | ||
1453 | rth->rt_dst == daddr && | ||
1454 | rth->rt_src == iph->saddr && | ||
1455 | rth->fl.iif == 0 && | ||
1456 | !(dst_metric_locked(&rth->u.dst, RTAX_MTU)) && | ||
1457 | net_eq(dev_net(rth->u.dst.dev), net) && | ||
1458 | rth->rt_genid == atomic_read(&rt_genid)) { | ||
1459 | unsigned short mtu = new_mtu; | 1454 | unsigned short mtu = new_mtu; |
1460 | 1455 | ||
1456 | if (rth->fl.fl4_dst != daddr || | ||
1457 | rth->fl.fl4_src != skeys[i] || | ||
1458 | rth->rt_dst != daddr || | ||
1459 | rth->rt_src != iph->saddr || | ||
1460 | rth->fl.oif != ikeys[k] || | ||
1461 | rth->fl.iif != 0 || | ||
1462 | dst_metric_locked(&rth->u.dst, RTAX_MTU) || | ||
1463 | !net_eq(dev_net(rth->u.dst.dev), net) || | ||
1464 | rth->rt_genid != atomic_read(&rt_genid)) | ||
1465 | continue; | ||
1466 | |||
1461 | if (new_mtu < 68 || new_mtu >= old_mtu) { | 1467 | if (new_mtu < 68 || new_mtu >= old_mtu) { |
1462 | 1468 | ||
1463 | /* BSD 4.2 compatibility hack :-( */ | 1469 | /* BSD 4.2 compatibility hack :-( */ |
@@ -1483,8 +1489,8 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, | |||
1483 | est_mtu = mtu; | 1489 | est_mtu = mtu; |
1484 | } | 1490 | } |
1485 | } | 1491 | } |
1492 | rcu_read_unlock(); | ||
1486 | } | 1493 | } |
1487 | rcu_read_unlock(); | ||
1488 | } | 1494 | } |
1489 | return est_mtu ? : new_mtu; | 1495 | return est_mtu ? : new_mtu; |
1490 | } | 1496 | } |