ipv6: support IPV6_PMTU_INTERFACE on sockets
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Sun, 15 Dec 2013 02:41:14 +0000 (03:41 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Dec 2013 22:37:05 +0000 (17:37 -0500)
IPV6_PMTU_INTERFACE is the same as IPV6_PMTU_PROBE for ipv6. Add it
nontheless for symmetry with IPv4 sockets. Also drop incoming MTU
information if this mode is enabled.

The additional bit in ipv6_pinfo just eats in the padding behind the
bitfield. There are no changes to the layout of the struct at all.

Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ipv6.h
include/net/ip6_route.h
include/uapi/linux/in6.h
net/dccp/ipv6.c
net/ipv6/ip6_output.c
net/ipv6/ipv6_sockglue.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/sctp/input.c

index 3fde066..7e1ded0 100644 (file)
@@ -191,7 +191,7 @@ struct ipv6_pinfo {
        /* sockopt flags */
        __u16                   recverr:1,
                                sndflow:1,
-                               pmtudisc:2,
+                               pmtudisc:3,
                                ipv6only:1,
                                srcprefs:3,     /* 001: prefer temporary address
                                                 * 010: prefer public address
index 733747c..c2626ce 100644 (file)
@@ -178,10 +178,15 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
 {
        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;
index 440d5c4..f94f1d0 100644 (file)
@@ -188,6 +188,10 @@ enum {
 #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
index 2b90a78..629019e 100644 (file)
@@ -141,6 +141,9 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        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))
index 9a311cc..bc4e1bc 100644 (file)
@@ -1165,10 +1165,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                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)
@@ -1270,7 +1270,7 @@ alloc_new_skb:
                        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;
index 759fbf9..af0ecb9 100644 (file)
@@ -722,7 +722,7 @@ done:
        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;
index da046a5..d955487 100644 (file)
@@ -397,6 +397,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                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);
index 089c741..65ed5cd 100644 (file)
@@ -538,8 +538,11 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        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;
index 2a192a7..042ec6c 100644 (file)
@@ -389,6 +389,9 @@ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
        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;