/* sockopt flags */
        __u16                   recverr:1,
                                sndflow:1,
-                               pmtudisc:2,
+                               pmtudisc:3,
                                ipv6only:1,
                                srcprefs:3,     /* 001: prefer temporary address
                                                 * 010: prefer public address
 
 {
        struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
 
-       return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
+       return (np && np->pmtudisc >= IPV6_PMTUDISC_PROBE) ?
               skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
 }
 
+static inline bool ip6_sk_accept_pmtu(const struct sock *sk)
+{
+       return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE;
+}
+
 static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
 {
        return &rt->rt6i_gateway;
 
 #define IPV6_PMTUDISC_WANT             1
 #define IPV6_PMTUDISC_DO               2
 #define IPV6_PMTUDISC_PROBE            3
+/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4
+ * also see comments on IP_PMTUDISC_INTERFACE
+ */
+#define IPV6_PMTUDISC_INTERFACE                4
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR     32
 
        if (type == ICMPV6_PKT_TOOBIG) {
                struct dst_entry *dst = NULL;
 
+               if (!ip6_sk_accept_pmtu(sk))
+                       goto out;
+
                if (sock_owned_by_user(sk))
                        goto out;
                if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED))
 
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
                if (rt->dst.flags & DST_XFRM_TUNNEL)
-                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                       mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
                              rt->dst.dev->mtu : dst_mtu(&rt->dst);
                else
-                       mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+                       mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ?
                              rt->dst.dev->mtu : dst_mtu(rt->dst.path);
                if (np->frag_size < mtu) {
                        if (np->frag_size)
                        if (skb == NULL || skb_prev == NULL)
                                ip6_append_data_mtu(&mtu, &maxfraglen,
                                                    fragheaderlen, skb, rt,
-                                                   np->pmtudisc ==
+                                                   np->pmtudisc >=
                                                    IPV6_PMTUDISC_PROBE);
 
                        skb_prev = skb;
 
        case IPV6_MTU_DISCOVER:
                if (optlen < sizeof(int))
                        goto e_inval;
-               if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE)
+               if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_INTERFACE)
                        goto e_inval;
                np->pmtudisc = val;
                retv = 0;
 
                if (sk->sk_state == TCP_LISTEN)
                        goto out;
 
+               if (!ip6_sk_accept_pmtu(sk))
+                       goto out;
+
                tp->mtu_info = ntohl(info);
                if (!sock_owned_by_user(sk))
                        tcp_v6_mtu_reduced(sk);
 
        if (sk == NULL)
                return;
 
-       if (type == ICMPV6_PKT_TOOBIG)
+       if (type == ICMPV6_PKT_TOOBIG) {
+               if (!ip6_sk_accept_pmtu(sk))
+                       goto out;
                ip6_sk_update_pmtu(skb, sk, info);
+       }
        if (type == NDISC_REDIRECT) {
                ip6_sk_redirect(skb, sk);
                goto out;
 
        if (!t || (t->pathmtu <= pmtu))
                return;
 
+       if (!ip6_sk_accept_pmtu(sk))
+               return;
+
        if (sock_owned_by_user(sk)) {
                asoc->pmtu_pending = 1;
                t->pmtu_pending = 1;