summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2013-12-15 03:41:14 +0100
committerDavid S. Miller <davem@davemloft.net>2013-12-18 23:37:05 +0100
commit93b36cf3425b9bd9c56df7680fb237686b9c82ae (patch)
tree1562e2899394e011e22ce21523dd6ce1defc3ee8
parentipv4: new ip_no_pmtu_disc mode to always discard incoming frag needed msgs (diff)
downloadlinux-93b36cf3425b9bd9c56df7680fb237686b9c82ae.tar.xz
linux-93b36cf3425b9bd9c56df7680fb237686b9c82ae.zip
ipv6: support IPV6_PMTU_INTERFACE on sockets
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>
-rw-r--r--include/linux/ipv6.h2
-rw-r--r--include/net/ip6_route.h7
-rw-r--r--include/uapi/linux/in6.h4
-rw-r--r--net/dccp/ipv6.c3
-rw-r--r--net/ipv6/ip6_output.c6
-rw-r--r--net/ipv6/ipv6_sockglue.c2
-rw-r--r--net/ipv6/tcp_ipv6.c3
-rw-r--r--net/ipv6/udp.c5
-rw-r--r--net/sctp/input.c3
9 files changed, 28 insertions, 7 deletions
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 3fde06645553..7e1ded0d8e45 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -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
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 733747ce163c..c2626ce1f2ad 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -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;
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 440d5c479145..f94f1d013bf2 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -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
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 2b90a786e475..629019e6f8e9 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -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))
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 9a311cc79672..bc4e1bcdf4c0 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -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;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 759fbf96515b..af0ecb94b3b4 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -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;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index da046a5d7ffb..d955487f2c54 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -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);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 089c741a3992..65ed5cd79264 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -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;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 2a192a7c5d81..042ec6c9ae24 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -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;