ipv4: introduce hardened ip_no_pmtu_disc mode
[linux-2.6-block.git] / net / ipv4 / icmp.c
index fb3c5637199dbfacd7b7b5c40ae14b1b2880d178..0134663fdbce86f6da39d8f6c9d27ce6c404687c 100644 (file)
@@ -668,6 +668,16 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
        rcu_read_unlock();
 }
 
+static bool icmp_tag_validation(int proto)
+{
+       bool ok;
+
+       rcu_read_lock();
+       ok = rcu_dereference(inet_protos[proto])->icmp_strict_tag_validation;
+       rcu_read_unlock();
+       return ok;
+}
+
 /*
  *     Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, ICMP_QUENCH, and
  *     ICMP_PARAMETERPROB.
@@ -705,12 +715,22 @@ static void icmp_unreach(struct sk_buff *skb)
                case ICMP_PORT_UNREACH:
                        break;
                case ICMP_FRAG_NEEDED:
-                       if (net->ipv4.sysctl_ip_no_pmtu_disc == 2) {
-                               goto out;
-                       } else if (net->ipv4.sysctl_ip_no_pmtu_disc) {
+                       /* for documentation of the ip_no_pmtu_disc
+                        * values please see
+                        * Documentation/networking/ip-sysctl.txt
+                        */
+                       switch (net->ipv4.sysctl_ip_no_pmtu_disc) {
+                       default:
                                LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),
                                               &iph->daddr);
-                       } else {
+                               break;
+                       case 2:
+                               goto out;
+                       case 3:
+                               if (!icmp_tag_validation(iph->protocol))
+                                       goto out;
+                               /* fall through */
+                       case 0:
                                info = ntohs(icmph->un.frag.mtu);
                                if (!info)
                                        goto out;