ipv4: Handle PMTU in all ICMP error handlers.
authorDavid S. Miller <davem@davemloft.net>
Fri, 15 Jun 2012 05:21:46 +0000 (22:21 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Jun 2012 05:22:07 +0000 (22:22 -0700)
With ip_rt_frag_needed() removed, we have to explicitly update PMTU
information in every ICMP error handler.

Create two helper functions to facilitate this.

1) ipv4_sk_update_pmtu()

   This updates the PMTU when we have a socket context to
   work with.

2) ipv4_update_pmtu()

   Raw version, used when no socket context is available.  For this
   interface, we essentially just pass in explicit arguments for
   the flow identity information we would have extracted from the
   socket.

   And you'll notice that ipv4_sk_update_pmtu() is simply implemented
   in terms of ipv4_update_pmtu()

Note that __ip_route_output_key() is used, rather than something like
ip_route_output_flow() or ip_route_output_key().  This is because we
absolutely do not want to end up with a route that does IPSEC
encapsulation and the like.  Instead, we only want the route that
would get us to the node described by the outermost IP header.

Reported-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/route.h
net/ipv4/ah4.c
net/ipv4/esp4.c
net/ipv4/ip_gre.c
net/ipv4/ipcomp.c
net/ipv4/ipip.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/udp.c
net/ipv6/sit.c

index a36ae429ed5dcc3ec2b66c0e064b678bafb0fc2e..47eb25ac1f7f8bce86a0cb608382bd1d0c6ee4e3 100644 (file)
@@ -215,7 +215,10 @@ static inline int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 s
        return ip_route_input_common(skb, dst, src, tos, devin, true);
 }
 
-extern void            ip_rt_send_redirect(struct sk_buff *skb);
+extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
+                            int oif, u32 mark, u8 protocol, int flow_flags);
+extern void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu);
+extern void ip_rt_send_redirect(struct sk_buff *skb);
 
 extern unsigned int            inet_addr_type(struct net *net, __be32 addr);
 extern unsigned int            inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr);
index e8f2617ecd471dad3c0aafe6db9c31adfb39d730..916d5ecaf6c698fcce1c71006ac3c2edf187ff0f 100644 (file)
@@ -408,6 +408,7 @@ static void ah4_err(struct sk_buff *skb, u32 info)
                return;
        pr_debug("pmtu discovery on SA AH/%08x/%08x\n",
                 ntohl(ah->spi), ntohl(iph->daddr));
+       ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
        xfrm_state_put(x);
 }
 
index cb982a61536fade811908a18e6119f513914741e..7b95b49a36cea37d2a18720b7e13c10fadb4506d 100644 (file)
@@ -494,6 +494,7 @@ static void esp4_err(struct sk_buff *skb, u32 info)
                return;
        NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
                 ntohl(esph->spi), ntohl(iph->daddr));
+       ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
        xfrm_state_put(x);
 }
 
index f49047b796096d1ce8ccd577db04556b576bc120..594cec35ac4df198b4afb956104c1e5120c8c0ed 100644 (file)
@@ -516,9 +516,6 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
                        return;
-               case ICMP_FRAG_NEEDED:
-                       /* Soft state for pmtu is maintained by IP core. */
-                       return;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -538,7 +535,16 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
                                p[1]);
-       if (t == NULL || t->parms.iph.daddr == 0 ||
+       if (t == NULL)
+               goto out;
+
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
+                                t->parms.link, 0, IPPROTO_GRE, 0);
+               goto out;
+       }
+
+       if (t->parms.iph.daddr == 0 ||
            ipv4_is_multicast(t->parms.iph.daddr))
                goto out;
 
index 63b64c45a826391bf4eded1f70b006efdd5a8e36..b91375482d844c9f9cfa0011faef541462c7349f 100644 (file)
@@ -42,6 +42,7 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
                return;
        NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI4\n",
                 spi, &iph->daddr);
+       ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
        xfrm_state_put(x);
 }
 
index 2d0f99bf61b348e70d1f90308d3a0119a358f563..715338a1b205a07195e31878f50cdbef31864c86 100644 (file)
@@ -348,9 +348,6 @@ static int ipip_err(struct sk_buff *skb, u32 info)
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
                        return 0;
-               case ICMP_FRAG_NEEDED:
-                       /* Soft state for pmtu is maintained by IP core. */
-                       return 0;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -369,7 +366,17 @@ static int ipip_err(struct sk_buff *skb, u32 info)
 
        rcu_read_lock();
        t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr);
-       if (t == NULL || t->parms.iph.daddr == 0)
+       if (t == NULL)
+               goto out;
+
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
+                                t->dev->ifindex, 0, IPPROTO_IPIP, 0);
+               err = 0;
+               goto out;
+       }
+
+       if (t->parms.iph.daddr == 0)
                goto out;
 
        err = 0;
index 2c00e8bf684d1a3272401652c343b66fa69a3e54..340fcf29a966399c596dac530f27fb1fb3651974 100644 (file)
@@ -371,6 +371,7 @@ void ping_err(struct sk_buff *skb, u32 info)
                break;
        case ICMP_DEST_UNREACH:
                if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
+                       ipv4_sk_update_pmtu(skb, sk, info);
                        if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
                                err = EMSGSIZE;
                                harderr = 1;
index 4032b818f3e41c13c5a0477a309eeea91ea3bf81..659ddfb109477112fd1216e82167ac2a22793fd3 100644 (file)
@@ -216,6 +216,9 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
        int err = 0;
        int harderr = 0;
 
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+               ipv4_sk_update_pmtu(skb, sk, info);
+
        /* Report error on raw socket, if:
           1. User requested ip_recverr.
           2. Socket is connected (otherwise the error indication
index 655506af47cab540b910e1028837e970c7c93dc0..41df5297a412e9f2ad722f1f28846d609bbe4ea6 100644 (file)
@@ -1711,6 +1711,34 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
        }
 }
 
+void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
+                     int oif, u32 mark, u8 protocol, int flow_flags)
+{
+       const struct iphdr *iph = (const struct iphdr *)skb->data;
+       struct flowi4 fl4;
+       struct rtable *rt;
+
+       flowi4_init_output(&fl4, oif, mark, RT_TOS(iph->tos), RT_SCOPE_UNIVERSE,
+                          protocol, flow_flags | FLOWI_FLAG_PRECOW_METRICS,
+                          iph->daddr, iph->saddr, 0, 0);
+       rt = __ip_route_output_key(net, &fl4);
+       if (!IS_ERR(rt)) {
+               ip_rt_update_pmtu(&rt->dst, mtu);
+               ip_rt_put(rt);
+       }
+}
+EXPORT_SYMBOL_GPL(ipv4_update_pmtu);
+
+void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+{
+       const struct inet_sock *inet = inet_sk(sk);
+
+       return ipv4_update_pmtu(skb, sock_net(sk), mtu,
+                               sk->sk_bound_dev_if, sk->sk_mark,
+                               inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
+                               inet_sk_flowi_flags(sk));
+}
+EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu);
 
 static void ipv4_validate_peer(struct rtable *rt)
 {
index eaca73644e7965e5405b6e61f362a3be3e0d2e2f..db017efb76ea0b2fdd8a2ee975c955bda403a4d1 100644 (file)
@@ -615,6 +615,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
                break;
        case ICMP_DEST_UNREACH:
                if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
+                       ipv4_sk_update_pmtu(skb, sk, info);
                        if (inet->pmtudisc != IP_PMTUDISC_DONT) {
                                err = EMSGSIZE;
                                harderr = 1;
index 60415711563f36ffc21a4ebed82349751c62bae4..49aea94c9be3fcc9a898a734e28a54ccd6d49258 100644 (file)
@@ -527,9 +527,6 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
                        return 0;
-               case ICMP_FRAG_NEEDED:
-                       /* Soft state for pmtu is maintained by IP core. */
-                       return 0;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -551,7 +548,17 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
                                skb->dev,
                                iph->daddr,
                                iph->saddr);
-       if (t == NULL || t->parms.iph.daddr == 0)
+       if (t == NULL)
+               goto out;
+
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
+                                t->dev->ifindex, 0, IPPROTO_IPV6, 0);
+               err = 0;
+               goto out;
+       }
+
+       if (t->parms.iph.daddr == 0)
                goto out;
 
        err = 0;
This page took 0.061057 seconds and 5 git commands to generate.