Skip to contentSkip to conversation

Do pmtu check in transport layer

PATCH Applied John Heffner 6 participants 26 messages Mar 24, 2007 – Apr 20, 2007 2 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/3 Do pmtu check in transport layer

Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is PMTUDISC_DO and packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.

---
 net/ipv4/ip_output.c  |    4 +++-
 net/ipv4/raw.c        |    8 +++++---
 net/ipv6/ip6_output.c |   11 ++++++-----
 net/ipv6/raw.c        |    7 +++++--
 4 files changed, 19 insertions(+), 11 deletions(-)
net/ipv4/ip_output.c +3 -1
index d096332..593acf7 100644
@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk,
 	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
 
-	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen ||
+	    (inet->pmtudisc >= IP_PMTUDISC_DO &&
+	     inet->cork.length + length > mtu)) {
 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
 		return -EMSGSIZE;
 	}
net/ipv4/raw.c +5 -3
index 87e9c16..f252f4e 100644
@@ -271,10 +271,12 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	struct iphdr *iph;
 	struct sk_buff *skb;
 	int err;
+	int mtu;
 
-	if (length > rt->u.dst.dev->mtu) {
-		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport,
-			       rt->u.dst.dev->mtu);
+	mtu = inet->pmtudisc == IP_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
+	                                         rt->u.dst.dev->mtu;
+	if (length > mtu) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
net/ipv6/ip6_output.c +6 -5
index 3055169..711dfc3 100644
@@ -1044,11 +1044,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 	fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
 
-	if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
-		if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
-			ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
-			return -EMSGSIZE;
-		}
+	if ((mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN &&
+	     inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) ||
+	    (np->pmtudisc >= IPV6_PMTUDISC_DO &&
+	     inet->cork.length + length > mtu)) {
+		ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
+		return -EMSGSIZE;
 	}
 
 	/*
net/ipv6/raw.c +5 -2
index 306d5d8..75db277 100644
@@ -556,9 +556,12 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	struct sk_buff *skb;
 	unsigned int hh_len;
 	int err;
+	int mtu;
 
-	if (length > rt->u.dst.dev->mtu) {
-		ipv6_local_error(sk, EMSGSIZE, fl, rt->u.dst.dev->mtu);
+	mtu = np->pmtudisc == IPV6_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
+	                                         rt->u.dst.dev->mtu;
+	if (length > mtu) {
+		ipv6_local_error(sk, EMSGSIZE, fl, mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
Signature · 1 line
1.5.0.2.gc260-dirty
#1 lore raw

PATCH 2/3 Move DF check to ip_forward

Do fragmentation check in ip_forward, similar to ipv6 forwarding. Also add a debug printk in the DF check in ip_fragment since we should now never reach it.

---
 net/ipv4/ip_forward.c |    8 ++++++++
 net/ipv4/ip_output.c  |    2 ++
 2 files changed, 10 insertions(+), 0 deletions(-)
net/ipv4/ip_forward.c +8 -0
index 369e721..0efb1f5 100644
@@ -85,6 +85,14 @@ int ip_forward(struct sk_buff *skb)
 	if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
 		goto sr_failed;
 
+	if (unlikely(skb->len > dst_mtu(&rt->u.dst) &&
+	             (skb->nh.iph->frag_off & htons(IP_DF))) && !skb->local_df) {
+		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+			  htonl(dst_mtu(&rt->u.dst)));
+		goto drop;
+	}
+
 	/* We are about to mangle packet. Copy it! */
 	if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
 		goto drop;
net/ipv4/ip_output.c +2 -0
index 593acf7..90bdd53 100644
@@ -433,6 +433,8 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
 	iph = skb->nh.iph;
 
 	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "ip_fragment: requested fragment of packet with DF set\n");
 		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(dst_mtu(&rt->u.dst)));
Signature · 1 line
1.5.0.2.gc260-dirty
#2 lore raw

PATCH 3/3 Add IP(V6)_PMTUDISC_RPOBE

Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.

---
 include/linux/in.h       |    1 +
 include/linux/in6.h      |    1 +
 include/linux/skbuff.h   |    3 ++-
 include/net/ip.h         |    2 +-
 net/core/skbuff.c        |    2 ++
 net/ipv4/ip_output.c     |   14 ++++++++++----
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv4/raw.c           |    3 +++
 net/ipv6/ip6_output.c    |   12 ++++++++----
 net/ipv6/ipv6_sockglue.c |    2 +-
 net/ipv6/raw.c           |    3 +++
 11 files changed, 33 insertions(+), 12 deletions(-)
include/linux/in.h +1 -0
index 1912e7c..2dc1f8a 100644
@@ -83,6 +83,7 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3	/* Ignore dst pmtu	*/
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +1 -0
index 4e8350a..d559fac 100644
@@ -179,6 +179,7 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
include/linux/skbuff.h +2 -1
index 4ff3940..64038b4 100644
@@ -284,7 +284,8 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1;
+				ipvs_property:1,
+				ign_dst_mtu;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
index e79c3e3..f5874a3 100644
@@ -201,7 +201,7 @@ int ip_decrease_ttl(struct iphdr *iph)
 static inline
 int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
 {
-	return (inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
+	return (inet_sk(sk)->pmtudisc >= IP_PMTUDISC_DO ||
 		(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
 		 !(dst_metric(dst, RTAX_LOCK)&(1<<RTAX_MTU))));
 }
net/core/skbuff.c +2 -0
index 702fa8f..5c8515c 100644
@@ -474,6 +474,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	C(ipvs_property);
 #endif
+	C(ign_dst_mtu);
 	C(protocol);
 	n->destructor = NULL;
 	C(mark);
@@ -549,6 +550,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	new->ipvs_property = old->ipvs_property;
 #endif
+	new->ign_dst_mtu	= old->ign_dst_mtu;
 #ifdef CONFIG_BRIDGE_NETFILTER
 	new->nf_bridge	= old->nf_bridge;
 	nf_bridge_get(old->nf_bridge);
net/ipv4/ip_output.c +10 -4
index 90bdd53..a7e8944 100644
@@ -201,7 +201,8 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+	if (skb->len > dst_mtu(skb->dst) &&
+	    !skb->ign_dst_mtu && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -801,7 +802,9 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+		                            rt->u.dst.dev->mtu :
+		                            dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1220,13 +1223,16 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc != IP_PMTUDISC_DO)
+	if (inet->pmtudisc < IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
+	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index 23048d9..98fa088 100644
@@ -536,7 +536,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 			inet->hdrincl = val ? 1 : 0;
 			break;
 		case IP_MTU_DISCOVER:
-			if (val<0 || val>2)
+			if (val<0 || val>3)
 				goto e_inval;
 			inet->pmtudisc = val;
 			break;
net/ipv4/raw.c +3 -0
index f252f4e..f562262 100644
@@ -302,6 +302,9 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	if (err)
 		goto error_fault;
 
+	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	/* We don't modify invalid header */
 	if (length >= sizeof(*iph) && iph->ihl * 4U <= length) {
 		if (!iph->saddr)
net/ipv6/ip6_output.c +8 -4
index 711dfc3..8b8c04b 100644
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
-				dst_allfrag(skb->dst))
+	if ((skb->len > dst_mtu(skb->dst) && !skb->ign_dst_mtu &&
+	     !skb_is_gso(skb)) || dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
 		return ip6_output2(skb);
@@ -574,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = dst_mtu(&rt->u.dst);
+	mtu = skb->ign_dst_mtu ? skb->len : dst_mtu(&rt->u.dst);
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
@@ -1015,7 +1015,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = dst_mtu(rt->u.dst.path);
+		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
@@ -1303,6 +1304,9 @@ int ip6_push_pending_frames(struct sock *sk)
 		tmp_skb->sk = NULL;
 	}
 
+	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	ipv6_addr_copy(final_dst, &fl->fl6_dst);
 	__skb_pull(skb, skb->h.raw - skb->nh.raw);
 	if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
index f5f9582..6e88597 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
net/ipv6/raw.c +3 -0
index 75db277..9ef0946 100644
@@ -587,6 +587,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	if (err)
 		goto error_fault;
 
+	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
 		      dst_output);
Signature · 1 line
1.5.0.2.gc260-dirty
maintainer
#3 lore raw

Re: [PATCH 3/3] [NET] Add IP(V6)_PMTUDISC_RPOBE

From: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:46 -0400

Quoted 7 lines

Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.

Also applied to net-2.6.22, thanks John.

I made a slight change:

Quoted 5 lines
include/linux/skbuff.h +0 -0
index 4ff3940..64038b4 100644

include/linux/skbuff.h struct sk_buff { · line 291

Quoted 9 lines
include/linux/skbuff.h +2 -1
@@ -284,7 +284,8 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1;
+				ipvs_property:1,
+				ign_dst_mtu;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);

I marked "ign_dst_mtu" with a bit field size of one since this appears to be a boolean I take it this is what you mean to do here. Otherwise it adds another __u8 to struct sk_buff. :-)

#4 lore raw

Re: [PATCH 3/3] [NET] Add IP(V6)_PMTUDISC_RPOBE

In reply to John Heffner
Quoted 5 lines · John Heffner

Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.

You should probably send a manpages update to the manpages maintainer too (cc'ed with fullquote)

-Andi

Quoted 236 lines
---
 include/linux/in.h       |    1 +
 include/linux/in6.h      |    1 +
 include/linux/skbuff.h   |    3 ++-
 include/net/ip.h         |    2 +-
 net/core/skbuff.c        |    2 ++
 net/ipv4/ip_output.c     |   14 ++++++++++----
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv4/raw.c           |    3 +++
 net/ipv6/ip6_output.c    |   12 ++++++++----
 net/ipv6/ipv6_sockglue.c |    2 +-
 net/ipv6/raw.c           |    3 +++
 11 files changed, 33 insertions(+), 12 deletions(-)
include/linux/in.h +1 -0
index 1912e7c..2dc1f8a 100644
@@ -83,6 +83,7 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3	/* Ignore dst pmtu	*/
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +1 -0
index 4e8350a..d559fac 100644
@@ -179,6 +179,7 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
include/linux/skbuff.h +2 -1
index 4ff3940..64038b4 100644
@@ -284,7 +284,8 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1;
+				ipvs_property:1,
+				ign_dst_mtu;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
index e79c3e3..f5874a3 100644
@@ -201,7 +201,7 @@ int ip_decrease_ttl(struct iphdr *iph)
 static inline
 int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
 {
-	return (inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
+	return (inet_sk(sk)->pmtudisc >= IP_PMTUDISC_DO ||
 		(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
 		 !(dst_metric(dst, RTAX_LOCK)&(1<<RTAX_MTU))));
 }
net/core/skbuff.c +2 -0
index 702fa8f..5c8515c 100644
@@ -474,6 +474,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	C(ipvs_property);
 #endif
+	C(ign_dst_mtu);
 	C(protocol);
 	n->destructor = NULL;
 	C(mark);
@@ -549,6 +550,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	new->ipvs_property = old->ipvs_property;
 #endif
+	new->ign_dst_mtu	= old->ign_dst_mtu;
 #ifdef CONFIG_BRIDGE_NETFILTER
 	new->nf_bridge	= old->nf_bridge;
 	nf_bridge_get(old->nf_bridge);
net/ipv4/ip_output.c +10 -4
index 90bdd53..a7e8944 100644
@@ -201,7 +201,8 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+	if (skb->len > dst_mtu(skb->dst) &&
+	    !skb->ign_dst_mtu && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -801,7 +802,9 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+		                            rt->u.dst.dev->mtu :
+		                            dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1220,13 +1223,16 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc != IP_PMTUDISC_DO)
+	if (inet->pmtudisc < IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
+	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index 23048d9..98fa088 100644
@@ -536,7 +536,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 			inet->hdrincl = val ? 1 : 0;
 			break;
 		case IP_MTU_DISCOVER:
-			if (val<0 || val>2)
+			if (val<0 || val>3)
 				goto e_inval;
 			inet->pmtudisc = val;
 			break;
net/ipv4/raw.c +3 -0
index f252f4e..f562262 100644
@@ -302,6 +302,9 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	if (err)
 		goto error_fault;
 
+	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	/* We don't modify invalid header */
 	if (length >= sizeof(*iph) && iph->ihl * 4U <= length) {
 		if (!iph->saddr)
net/ipv6/ip6_output.c +8 -4
index 711dfc3..8b8c04b 100644
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
-				dst_allfrag(skb->dst))
+	if ((skb->len > dst_mtu(skb->dst) && !skb->ign_dst_mtu &&
+	     !skb_is_gso(skb)) || dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
 		return ip6_output2(skb);
@@ -574,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = dst_mtu(&rt->u.dst);
+	mtu = skb->ign_dst_mtu ? skb->len : dst_mtu(&rt->u.dst);
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
@@ -1015,7 +1015,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = dst_mtu(rt->u.dst.path);
+		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
@@ -1303,6 +1304,9 @@ int ip6_push_pending_frames(struct sock *sk)
 		tmp_skb->sk = NULL;
 	}
 
+	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	ipv6_addr_copy(final_dst, &fl->fl6_dst);
 	__skb_pull(skb, skb->h.raw - skb->nh.raw);
 	if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
index f5f9582..6e88597 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
net/ipv6/raw.c +3 -0
index 75db277..9ef0946 100644
@@ -587,6 +587,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	if (err)
 		goto error_fault;
 
+	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
+		skb->ign_dst_mtu = 1;
+
 	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
 		      dst_output);
Signature · 6 lines
1.5.0.2.gc260-dirty

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

#7 lore raw

PATCH 1/1 ip(7) IP_PMTUDISC_PROBE

Document new IP_PMTUDISC_PROBE value for IP_MTU_DISCOVERY. (Going into 2.6.22).

Thanks,
-John

man7/ip.7 +10 -0
diff -rU3 man-pages-2.43-a/man7/ip.7 man-pages-2.43-b/man7/ip.7
@@ -515,6 +515,7 @@
 IP_PMTUDISC_WANT:Use per-route settings.
 IP_PMTUDISC_DONT:Never do Path MTU Discovery.
 IP_PMTUDISC_DO:Always do Path MTU Discovery. 
+IP_PMTUDISC_PROBE:Set DF but ignore Path MTU.
 .TE   
 
 When PMTU discovery is enabled the kernel automatically keeps track of
@@ -550,6 +551,15 @@
 with the
 .B IP_MTU
 option.     
+
+It is possible to implement RFC 4821 MTU probing with
+.B SOCK_DGRAM
+of
+.B SOCK_RAW
+sockets by setting a value of IP_PMTUDISC_PROBE.  This is also particularly
+useful for diagnostic tools such as
+.BR tracepath (8)
+that wish to deliberately send probe packets larger than the observed Path MTU.
 .TP
 .B IP_MTU
 Retrieve the current known path MTU of the current socket. 
#8 lore raw

Re: [PATCH] ip(7) IP_PMTUDISC_PROBE

Quoted 2 lines
Quoted 2 lines

Document new IP_PMTUDISC_PROBE value for IP_MTU_DISCOVERY. (Going into 2.6.22).

Hi John,

Thanks -- accepted -- fix will appear in man-pages-2.47.

Andi: thanks for pointing John in the right direction.

Cheers,

Michael

Quoted 5 lines
Quoted 5 lines

------------------------------------------------------------------------

diff -rU3 man-pages-2.43-a/man7/ip.7 man-pages-2.43-b/man7/ip.7

man7/ip.7 +0 -0
Signature · 5 lines
Michael Kerrisk
maintainer of Linux man pages Sections 2, 3, 4, 5, and 7
Want to help with man page maintenance?
Grab the latest tarball at http://www.kernel.org/pub/linux/docs/manpages/
read the HOWTOHELP file and grep the source files for 'FIXME'.
maintainer
#9 lore raw

Re: [PATCH 2/3] [NET] Move DF check to ip_forward

In reply to John Heffner

From: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:45 -0400

Quoted 5 lines

Do fragmentation check in ip_forward, similar to ipv6 forwarding. Also add a debug printk in the DF check in ip_fragment since we should now never reach it.

I don't think this debugging log message makes sense considering netfilter can potentially do interesting things to the packet.

Therefore I applied your patch without the log message, we can discuss it's merits seperately as I like the rest of this patch.

Thanks.

#10 lore raw

[NET]: Fix breakage, use ip_hdr() for DF check in ip_forward

In reply to John Heffner

The patch [NET] Move DF check to ip_forward broke net-2.6.22 as skb->nh has been renamed to skb->network_header. Use ip_hdr().

net/ipv4/ip_forward.c +1 -1
Index: net-2.6.22/net/ipv4/ip_forward.c
===================================================================
@@ -86,7 +86,7 @@ int ip_forward(struct sk_buff *skb)
 		goto sr_failed;
 
 	if (unlikely(skb->len > dst_mtu(&rt->u.dst) &&
-	             (skb->nh.iph->frag_off & htons(IP_DF))) && !skb->local_df) {
+	             (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
 		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(dst_mtu(&rt->u.dst)));
maintainer
#11 lore raw

Re: [NET]: Fix breakage, use ip_hdr() for DF check in ip_forward

From: Thomas Graf <[email protected]>
Date: Sun, 25 Mar 2007 15:37:31 +0200

Quoted 4 lines

The patch [NET] Move DF check to ip_forward broke net-2.6.22 as skb->nh has been renamed to skb->network_header. Use ip_hdr().

Thanks Thomas.

John I'm extremely irritated. You send me which is obviouly a feature patch, which I'm obviously NOT going to apply to the current upstream for 2.6.21, which is only accepting bug fixes, yet you do not test compile your work and build your patches against my net-2.6.22 tree which is where such feature patches are going to get applied?

That's just irresponsible, please don't do that again or you'll be on my shit list for a good long while.

maintainer Applied
#12 lore raw
In reply to John Heffner

From: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:44 -0400

Quoted 6 lines

Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is PMTUDISC_DO and packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.

Applied, thanks John.

#13 lore raw
In reply to John Heffner
Quoted 10 lines · John Heffner

Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is PMTUDISC_DO and packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.

net/ipv4/ip_output.c +0 -0
index d096332..593acf7 100644

net/ipv4/ip_output.c int ip_append_data(struct sock *sk, · line 830

Quoted 10 lines
net/ipv4/ip_output.c +3 -1
@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk,
 	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
 
-	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen ||
+	    (inet->pmtudisc >= IP_PMTUDISC_DO &&
+	     inet->cork.length + length > mtu)) {
 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
 		return -EMSGSIZE;
 	}

This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional header_len, not the trailer_len (which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes the header_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.

For all these reasons I think it should be reverted to the old behaviour.

#14 lore raw
Quoted 33 lines · Patrick McHardy
Quoted 10 lines · John Heffner

Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is PMTUDISC_DO and packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.

net/ipv4/ip_output.c +0 -0
index d096332..593acf7 100644

net/ipv4/ip_output.c int ip_append_data(struct sock *sk, · line 830

Quoted 10 lines
net/ipv4/ip_output.c +3 -1
@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk,
 	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
 
-	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen ||
+	    (inet->pmtudisc >= IP_PMTUDISC_DO &&
+	     inet->cork.length + length > mtu)) {
 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
 		return -EMSGSIZE;
 	}

This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional header_len, not the trailer_len (which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes the header_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.

Quoted 2 lines

For all these reasons I think it should be reverted to the old behaviour.

You're right, this is no good. I think the other problems are fixable, but NAT really screws this.

Unfortunately, there is still a real problem with ipv6, in that the output side does not generate a packet too big ICMP like ipv4. Also, it feels kind of undesirable be rely on local ICMP instead of direct error message delivery. I'll try to generate a new patch.

Thanks,
-John

#15 lore raw
Quoted 12 lines · John Heffner
Quoted 10 lines · Patrick McHardy

This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional header_len, not the trailer_len (which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes the header_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.

Quoted 6 lines
Quoted 2 lines

For all these reasons I think it should be reverted to the old behaviour.

You're right, this is no good. I think the other problems are fixable, but NAT really screws this.

Routing by fwmark is also unfixable and IPsec is quite hard.

Quoted 4 lines

Unfortunately, there is still a real problem with ipv6, in that the output side does not generate a packet too big ICMP like ipv4. Also, it feels kind of undesirable be rely on local ICMP instead of direct error message delivery. I'll try to generate a new patch.

I think its necessary since at the transport layer we simply don't have all the information about whats going to happen to a packet. IPv6 now also supports routing by fwmark, so it has the same problem if it doesn't generate packet too big messages.

#16 lore raw

cover Re-try changes for PMTUDISC_PROBE

In reply to Patrick McHardy

This backs out the the transport layer MTU checks that don't work. As a consequence, I had to back out the PMTUDISC_PROBE patch as well. These patches should fix the problem with ipv6 that the transport layer change tried to address, and re-implement PMTUDISC_PROBE. I think this approach is nicer than the last one, since it doesn't require a bit in struct sk_buff.

Thanks,
-John

maintainer
#17 lore raw

Re: [PATCH 0/0] Re-try changes for PMTUDISC_PROBE

From: John Heffner <[email protected]>
Date: Wed, 18 Apr 2007 21:07:10 -0400

Quoted 6 lines

This backs out the the transport layer MTU checks that don't work. As a consequence, I had to back out the PMTUDISC_PROBE patch as well. These patches should fix the problem with ipv6 that the transport layer change tried to address, and re-implement PMTUDISC_PROBE. I think this approach is nicer than the last one, since it doesn't require a bit in struct sk_buff.

Since I was rebasing net-2.6.22, here is what I did. I simply elided the two patches you backed out, then I'll add patch 3 and 4, the new one's, on top.

This will show up when I publish the rebased net-2.6.22 tree which I hope will happen later this evening.

Thanks!

#18 lore raw

PATCH 1/1 Revert "[NET] Add IP(V6)_PMTUDISC_RPOBE"

In reply to John Heffner

This reverts commit d21d2a90b879c0cf159df5944847e6d9833816eb.

Must be backed out because commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37 does not work.

---
 include/linux/in.h       |    1 -
 include/linux/in6.h      |    1 -
 include/linux/skbuff.h   |    3 +--
 include/net/ip.h         |    2 +-
 net/core/skbuff.c        |    2 --
 net/ipv4/ip_output.c     |   14 ++++----------
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv4/raw.c           |    3 ---
 net/ipv6/ip6_output.c    |   12 ++++--------
 net/ipv6/ipv6_sockglue.c |    2 +-
 net/ipv6/raw.c           |    3 ---
 11 files changed, 12 insertions(+), 33 deletions(-)
include/linux/in.h +0 -1
index 2dc1f8a..1912e7c 100644
@@ -83,7 +83,6 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
-#define IP_PMTUDISC_PROBE		3	/* Ignore dst pmtu	*/
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +0 -1
index d559fac..4e8350a 100644
@@ -179,7 +179,6 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
-#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
include/linux/skbuff.h +1 -2
index 8bf9b9f..7f17cfc 100644
@@ -277,8 +277,7 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1,
-				ign_dst_mtu:1;
+				ipvs_property:1;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
index 6a08b65..75f226d 100644
@@ -206,7 +206,7 @@ int ip_decrease_ttl(struct iphdr *iph)
 static inline
 int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
 {
-	return (inet_sk(sk)->pmtudisc >= IP_PMTUDISC_DO ||
+	return (inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
 		(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
 		 !(dst_metric(dst, RTAX_LOCK)&(1<<RTAX_MTU))));
 }
net/core/skbuff.c +0 -2
index 2391cdf..f0d986a 100644
@@ -479,7 +479,6 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	C(ipvs_property);
 #endif
-	C(ign_dst_mtu);
 	C(protocol);
 	n->destructor = NULL;
 	C(mark);
@@ -543,7 +542,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	new->ipvs_property = old->ipvs_property;
 #endif
-	new->ign_dst_mtu	= old->ign_dst_mtu;
 #ifdef CONFIG_NET_SCHED
 #ifdef CONFIG_NET_CLS_ACT
 	new->tc_verd = old->tc_verd;
net/ipv4/ip_output.c +4 -10
index 704bc44..79e71ee 100644
@@ -198,8 +198,7 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) &&
-	    !skb->ign_dst_mtu && !skb_is_gso(skb))
+	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -788,9 +787,7 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
-		                            rt->u.dst.dev->mtu :
-		                            dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1208,16 +1205,13 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc < IP_PMTUDISC_DO)
+	if (inet->pmtudisc != IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
-	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
+	if (inet->pmtudisc == IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index 4d54457..c199d23 100644
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		inet->hdrincl = val ? 1 : 0;
 		break;
 	case IP_MTU_DISCOVER:
-		if (val<0 || val>3)
+		if (val<0 || val>2)
 			goto e_inval;
 		inet->pmtudisc = val;
 		break;
net/ipv4/raw.c +0 -3
index addb786..c60aadf 100644
@@ -304,9 +304,6 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	if (err)
 		goto error_fault;
 
-	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	/* We don't modify invalid header */
 	if (length >= sizeof(*iph) && iph->ihl * 4U <= length) {
 		if (!iph->saddr)
net/ipv6/ip6_output.c +4 -8
index 78317a4..b8e307a 100644
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb->ign_dst_mtu &&
-	     !skb_is_gso(skb)) || dst_allfrag(skb->dst))
+	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+				dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
 		return ip6_output2(skb);
@@ -566,7 +566,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = skb->ign_dst_mtu ? skb->len : dst_mtu(&rt->u.dst);
+	mtu = dst_mtu(&rt->u.dst);
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
@@ -1050,8 +1050,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
-		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
+		mtu = dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
@@ -1339,9 +1338,6 @@ int ip6_push_pending_frames(struct sock *sk)
 		tmp_skb->sk = NULL;
 	}
 
-	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	ipv6_addr_copy(final_dst, &fl->fl6_dst);
 	__skb_pull(skb, skb_network_header_len(skb));
 	if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
index aa3d07c..da930fa 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>3)
+		if (val<0 || val>2)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
net/ipv6/raw.c +0 -3
index 86c9943..f4cd90b 100644
@@ -591,9 +591,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	if (err)
 		goto error_fault;
 
-	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
 		      dst_output);
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#19 lore raw

PATCH 1/1 Revert "[NET] Do pmtu check in transport layer"

This reverts commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37.

This idea does not work, as pointed at by Patrick McHardy.

---
 net/ipv4/ip_output.c  |    4 +---
 net/ipv4/raw.c        |    8 +++-----
 net/ipv6/ip6_output.c |   11 +++++------
 net/ipv6/raw.c        |    7 ++-----
 4 files changed, 11 insertions(+), 19 deletions(-)
net/ipv4/ip_output.c +1 -3
index 79e71ee..34606ef 100644
@@ -810,9 +810,7 @@ int ip_append_data(struct sock *sk,
 	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
 
-	if (inet->cork.length + length > 0xFFFF - fragheaderlen ||
-	    (inet->pmtudisc >= IP_PMTUDISC_DO &&
-	     inet->cork.length + length > mtu)) {
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
 		return -EMSGSIZE;
 	}
net/ipv4/raw.c +3 -5
index c60aadf..24d7c9f 100644
@@ -271,12 +271,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	struct iphdr *iph;
 	struct sk_buff *skb;
 	int err;
-	int mtu;
 
-	mtu = inet->pmtudisc == IP_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
-	                                         rt->u.dst.dev->mtu;
-	if (length > mtu) {
-		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu);
+	if (length > rt->u.dst.dev->mtu) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport,
+			       rt->u.dst.dev->mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
net/ipv6/ip6_output.c +5 -6
index b8e307a..4cfdad4 100644
@@ -1079,12 +1079,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 	fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
 
-	if ((mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN &&
-	     inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) ||
-	    (np->pmtudisc >= IPV6_PMTUDISC_DO &&
-	     inet->cork.length + length > mtu)) {
-		ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
-		return -EMSGSIZE;
+	if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
+		if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
+			ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
+			return -EMSGSIZE;
+		}
 	}
 
 	/*
net/ipv6/raw.c +2 -5
index f4cd90b..f65fcd7 100644
@@ -558,12 +558,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	struct sk_buff *skb;
 	unsigned int hh_len;
 	int err;
-	int mtu;
 
-	mtu = np->pmtudisc == IPV6_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
-	                                         rt->u.dst.dev->mtu;
-	if (length > mtu) {
-		ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+	if (length > rt->u.dst.dev->mtu) {
+		ipv6_local_error(sk, EMSGSIZE, fl, rt->u.dst.dev->mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#20 lore raw

PATCH 1/1 MTU discovery check in ip6_fragment()

Adds a check in ip6_fragment() mirroring ip_fragment() for packets that we can't fragment, and sends an ICMP Packet Too Big message in response.

---
 net/ipv6/ip6_output.c |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)
net/ipv6/ip6_output.c +13 -0
index 4cfdad4..5a5b7d4 100644
@@ -567,6 +567,19 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	nexthdr = *prevhdr;
 
 	mtu = dst_mtu(&rt->u.dst);
+
+	/* We must not fragment if the socket is set to force MTU discovery
+	 * or if the skb it not generated by a local socket.  (This last
+	 * check should be redundant, but it's free.)
+	 */
+	if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+		skb->dev = skb->dst->dev;
+		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#21 lore raw

PATCH 1/1 Add IP(V6)_PMTUDISC_RPOBE

Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.

---
 include/linux/in.h       |    1 +
 include/linux/in6.h      |    1 +
 net/ipv4/ip_output.c     |   20 +++++++++++++++-----
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv6/ip6_output.c    |   15 ++++++++++++---
 net/ipv6/ipv6_sockglue.c |    2 +-
 6 files changed, 31 insertions(+), 10 deletions(-)
include/linux/in.h +1 -0
index 1912e7c..3975cbf 100644
@@ -83,6 +83,7 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3       /* Ignore dst pmtu      */
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +1 -0
index 4e8350a..d559fac 100644
@@ -179,6 +179,7 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
net/ipv4/ip_output.c +15 -5
index 34606ef..66e2c3a 100644
@@ -189,6 +189,14 @@ static inline int ip_finish_output2(struct sk_buff *skb)
 	return -EINVAL;
 }
 
+static inline int ip_skb_dst_mtu(struct sk_buff *skb)
+{
+	struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
+
+	return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 static inline int ip_finish_output(struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
@@ -198,7 +206,7 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+	if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -422,7 +430,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
 	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
 		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-			  htonl(dst_mtu(&rt->u.dst)));
+			  htonl(ip_skb_dst_mtu(skb)));
 		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
@@ -787,7 +795,9 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+					    rt->u.dst.dev->mtu : 
+					    dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1203,13 +1213,13 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc != IP_PMTUDISC_DO)
+	if (inet->pmtudisc < IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index c199d23..4d54457 100644
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		inet->hdrincl = val ? 1 : 0;
 		break;
 	case IP_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		inet->pmtudisc = val;
 		break;
net/ipv6/ip6_output.c +12 -3
index 5a5b7d4..f508171 100644
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)
 	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
+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) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+	if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
 				dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
@@ -566,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = dst_mtu(&rt->u.dst);
+	mtu = ip6_skb_dst_mtu(skb);
 
 	/* We must not fragment if the socket is set to force MTU discovery
 	 * or if the skb it not generated by a local socket.  (This last
@@ -1063,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = dst_mtu(rt->u.dst.path);
+		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
net/ipv6/ipv6_sockglue.c +1 -1
index da930fa..aa3d07c 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#22 lore raw

Re: [PATCH] [NET] Add IP(V6)_PMTUDISC_RPOBE

Sorry, forgot the -n flag on git-format-patch. Patches resent with correct sequence numbers.

Thanks,
-John

maintainer
#23 lore raw

Re: [PATCH] [NET] Add IP(V6)_PMTUDISC_RPOBE

From: John Heffner <[email protected]>
Date: Wed, 18 Apr 2007 21:11:26 -0400

Quoted 2 lines

Sorry, forgot the -n flag on git-format-patch. Patches resent with correct sequence numbers.

Thanks for fixing that.

#24 lore raw

PATCH 1/4 Revert "[NET] Add IP(V6)_PMTUDISC_RPOBE"

In reply to John Heffner

This reverts commit d21d2a90b879c0cf159df5944847e6d9833816eb.

Must be backed out because commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37 does not work.

---
 include/linux/in.h       |    1 -
 include/linux/in6.h      |    1 -
 include/linux/skbuff.h   |    3 +--
 include/net/ip.h         |    2 +-
 net/core/skbuff.c        |    2 --
 net/ipv4/ip_output.c     |   14 ++++----------
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv4/raw.c           |    3 ---
 net/ipv6/ip6_output.c    |   12 ++++--------
 net/ipv6/ipv6_sockglue.c |    2 +-
 net/ipv6/raw.c           |    3 ---
 11 files changed, 12 insertions(+), 33 deletions(-)
include/linux/in.h +0 -1
index 2dc1f8a..1912e7c 100644
@@ -83,7 +83,6 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
-#define IP_PMTUDISC_PROBE		3	/* Ignore dst pmtu	*/
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +0 -1
index d559fac..4e8350a 100644
@@ -179,7 +179,6 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
-#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
include/linux/skbuff.h +1 -2
index 8bf9b9f..7f17cfc 100644
@@ -277,8 +277,7 @@ struct sk_buff {
 				nfctinfo:3;
 	__u8			pkt_type:3,
 				fclone:2,
-				ipvs_property:1,
-				ign_dst_mtu:1;
+				ipvs_property:1;
 	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
index 6a08b65..75f226d 100644
@@ -206,7 +206,7 @@ int ip_decrease_ttl(struct iphdr *iph)
 static inline
 int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
 {
-	return (inet_sk(sk)->pmtudisc >= IP_PMTUDISC_DO ||
+	return (inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||
 		(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT &&
 		 !(dst_metric(dst, RTAX_LOCK)&(1<<RTAX_MTU))));
 }
net/core/skbuff.c +0 -2
index 2391cdf..f0d986a 100644
@@ -479,7 +479,6 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	C(ipvs_property);
 #endif
-	C(ign_dst_mtu);
 	C(protocol);
 	n->destructor = NULL;
 	C(mark);
@@ -543,7 +542,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
 	new->ipvs_property = old->ipvs_property;
 #endif
-	new->ign_dst_mtu	= old->ign_dst_mtu;
 #ifdef CONFIG_NET_SCHED
 #ifdef CONFIG_NET_CLS_ACT
 	new->tc_verd = old->tc_verd;
net/ipv4/ip_output.c +4 -10
index 704bc44..79e71ee 100644
@@ -198,8 +198,7 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) &&
-	    !skb->ign_dst_mtu && !skb_is_gso(skb))
+	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -788,9 +787,7 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
-		                            rt->u.dst.dev->mtu :
-		                            dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1208,16 +1205,13 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc < IP_PMTUDISC_DO)
+	if (inet->pmtudisc != IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
-	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
+	if (inet->pmtudisc == IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index 4d54457..c199d23 100644
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		inet->hdrincl = val ? 1 : 0;
 		break;
 	case IP_MTU_DISCOVER:
-		if (val<0 || val>3)
+		if (val<0 || val>2)
 			goto e_inval;
 		inet->pmtudisc = val;
 		break;
net/ipv4/raw.c +0 -3
index addb786..c60aadf 100644
@@ -304,9 +304,6 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	if (err)
 		goto error_fault;
 
-	if (inet->pmtudisc == IP_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	/* We don't modify invalid header */
 	if (length >= sizeof(*iph) && iph->ihl * 4U <= length) {
 		if (!iph->saddr)
net/ipv6/ip6_output.c +4 -8
index 78317a4..b8e307a 100644
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb->ign_dst_mtu &&
-	     !skb_is_gso(skb)) || dst_allfrag(skb->dst))
+	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+				dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
 		return ip6_output2(skb);
@@ -566,7 +566,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = skb->ign_dst_mtu ? skb->len : dst_mtu(&rt->u.dst);
+	mtu = dst_mtu(&rt->u.dst);
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
@@ -1050,8 +1050,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
-		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
+		mtu = dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
@@ -1339,9 +1338,6 @@ int ip6_push_pending_frames(struct sock *sk)
 		tmp_skb->sk = NULL;
 	}
 
-	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	ipv6_addr_copy(final_dst, &fl->fl6_dst);
 	__skb_pull(skb, skb_network_header_len(skb));
 	if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
index aa3d07c..da930fa 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>3)
+		if (val<0 || val>2)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
net/ipv6/raw.c +0 -3
index 86c9943..f4cd90b 100644
@@ -591,9 +591,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	if (err)
 		goto error_fault;
 
-	if (np->pmtudisc == IPV6_PMTUDISC_PROBE)
-		skb->ign_dst_mtu = 1;
-
 	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
 	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
 		      dst_output);
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#25 lore raw

PATCH 2/4 Revert "[NET] Do pmtu check in transport layer"

This reverts commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37.

This idea does not work, as pointed at by Patrick McHardy.

---
 net/ipv4/ip_output.c  |    4 +---
 net/ipv4/raw.c        |    8 +++-----
 net/ipv6/ip6_output.c |   11 +++++------
 net/ipv6/raw.c        |    7 ++-----
 4 files changed, 11 insertions(+), 19 deletions(-)
net/ipv4/ip_output.c +1 -3
index 79e71ee..34606ef 100644
@@ -810,9 +810,7 @@ int ip_append_data(struct sock *sk,
 	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
 
-	if (inet->cork.length + length > 0xFFFF - fragheaderlen ||
-	    (inet->pmtudisc >= IP_PMTUDISC_DO &&
-	     inet->cork.length + length > mtu)) {
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
 		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
 		return -EMSGSIZE;
 	}
net/ipv4/raw.c +3 -5
index c60aadf..24d7c9f 100644
@@ -271,12 +271,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
 	struct iphdr *iph;
 	struct sk_buff *skb;
 	int err;
-	int mtu;
 
-	mtu = inet->pmtudisc == IP_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
-	                                         rt->u.dst.dev->mtu;
-	if (length > mtu) {
-		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu);
+	if (length > rt->u.dst.dev->mtu) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport,
+			       rt->u.dst.dev->mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
net/ipv6/ip6_output.c +5 -6
index b8e307a..4cfdad4 100644
@@ -1079,12 +1079,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 	fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
 
-	if ((mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN &&
-	     inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) ||
-	    (np->pmtudisc >= IPV6_PMTUDISC_DO &&
-	     inet->cork.length + length > mtu)) {
-		ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
-		return -EMSGSIZE;
+	if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {
+		if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {
+			ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);
+			return -EMSGSIZE;
+		}
 	}
 
 	/*
net/ipv6/raw.c +2 -5
index f4cd90b..f65fcd7 100644
@@ -558,12 +558,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
 	struct sk_buff *skb;
 	unsigned int hh_len;
 	int err;
-	int mtu;
 
-	mtu = np->pmtudisc == IPV6_PMTUDISC_DO ? dst_mtu(&rt->u.dst) :
-	                                         rt->u.dst.dev->mtu;
-	if (length > mtu) {
-		ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+	if (length > rt->u.dst.dev->mtu) {
+		ipv6_local_error(sk, EMSGSIZE, fl, rt->u.dst.dev->mtu);
 		return -EMSGSIZE;
 	}
 	if (flags&MSG_PROBE)
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#26 lore raw

PATCH 3/4 MTU discovery check in ip6_fragment()

Adds a check in ip6_fragment() mirroring ip_fragment() for packets that we can't fragment, and sends an ICMP Packet Too Big message in response.

---
 net/ipv6/ip6_output.c |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)
net/ipv6/ip6_output.c +13 -0
index 4cfdad4..5a5b7d4 100644
@@ -567,6 +567,19 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	nexthdr = *prevhdr;
 
 	mtu = dst_mtu(&rt->u.dst);
+
+	/* We must not fragment if the socket is set to force MTU discovery
+	 * or if the skb it not generated by a local socket.  (This last
+	 * check should be redundant, but it's free.)
+	 */
+	if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+		skb->dev = skb->dst->dev;
+		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+		IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
 	if (np && np->frag_size < mtu) {
 		if (np->frag_size)
 			mtu = np->frag_size;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
#27 lore raw

PATCH 4/4 Add IP(V6)_PMTUDISC_RPOBE

Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.

---
 include/linux/in.h       |    1 +
 include/linux/in6.h      |    1 +
 net/ipv4/ip_output.c     |   20 +++++++++++++++-----
 net/ipv4/ip_sockglue.c   |    2 +-
 net/ipv6/ip6_output.c    |   15 ++++++++++++---
 net/ipv6/ipv6_sockglue.c |    2 +-
 6 files changed, 31 insertions(+), 10 deletions(-)
include/linux/in.h +1 -0
index 1912e7c..3975cbf 100644
@@ -83,6 +83,7 @@ struct in_addr {
 #define IP_PMTUDISC_DONT		0	/* Never send DF frames */
 #define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
 #define IP_PMTUDISC_DO			2	/* Always DF		*/
+#define IP_PMTUDISC_PROBE		3       /* Ignore dst pmtu      */
 
 #define IP_MULTICAST_IF			32
 #define IP_MULTICAST_TTL 		33
include/linux/in6.h +1 -0
index 4e8350a..d559fac 100644
@@ -179,6 +179,7 @@ struct in6_flowlabel_req
 #define IPV6_PMTUDISC_DONT		0
 #define IPV6_PMTUDISC_WANT		1
 #define IPV6_PMTUDISC_DO		2
+#define IPV6_PMTUDISC_PROBE		3
 
 /* Flowlabel */
 #define IPV6_FLOWLABEL_MGR	32
net/ipv4/ip_output.c +15 -5
index 34606ef..66e2c3a 100644
@@ -189,6 +189,14 @@ static inline int ip_finish_output2(struct sk_buff *skb)
 	return -EINVAL;
 }
 
+static inline int ip_skb_dst_mtu(struct sk_buff *skb)
+{
+	struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
+
+	return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 static inline int ip_finish_output(struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
@@ -198,7 +206,7 @@ static inline int ip_finish_output(struct sk_buff *skb)
 		return dst_output(skb);
 	}
 #endif
-	if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+	if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
 		return ip_fragment(skb, ip_finish_output2);
 	else
 		return ip_finish_output2(skb);
@@ -422,7 +430,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
 	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
 		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-			  htonl(dst_mtu(&rt->u.dst)));
+			  htonl(ip_skb_dst_mtu(skb)));
 		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
@@ -787,7 +795,9 @@ int ip_append_data(struct sock *sk,
 			inet->cork.addr = ipc->addr;
 		}
 		dst_hold(&rt->u.dst);
-		inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
+		inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+					    rt->u.dst.dev->mtu : 
+					    dst_mtu(rt->u.dst.path);
 		inet->cork.rt = rt;
 		inet->cork.length = 0;
 		sk->sk_sndmsg_page = NULL;
@@ -1203,13 +1213,13 @@ int ip_push_pending_frames(struct sock *sk)
 	 * to fragment the frame generated here. No matter, what transforms
 	 * how transforms change size of the packet, it will come out.
 	 */
-	if (inet->pmtudisc != IP_PMTUDISC_DO)
+	if (inet->pmtudisc < IP_PMTUDISC_DO)
 		skb->local_df = 1;
 
 	/* DF bit is set when we want to see DF on outgoing frames.
 	 * If local_df is set too, we still allow to fragment this frame
 	 * locally. */
-	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	if (inet->pmtudisc >= IP_PMTUDISC_DO ||
 	    (skb->len <= dst_mtu(&rt->u.dst) &&
 	     ip_dont_fragment(sk, &rt->u.dst)))
 		df = htons(IP_DF);
net/ipv4/ip_sockglue.c +1 -1
index c199d23..4d54457 100644
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
 		inet->hdrincl = val ? 1 : 0;
 		break;
 	case IP_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		inet->pmtudisc = val;
 		break;
net/ipv6/ip6_output.c +12 -3
index 5a5b7d4..f508171 100644
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)
 	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
 }
 
+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) ?
+	       skb->dst->dev->mtu : dst_mtu(skb->dst);
+}
+
 int ip6_output(struct sk_buff *skb)
 {
-	if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) ||
+	if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
 				dst_allfrag(skb->dst))
 		return ip6_fragment(skb, ip6_output2);
 	else
@@ -566,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
-	mtu = dst_mtu(&rt->u.dst);
+	mtu = ip6_skb_dst_mtu(skb);
 
 	/* We must not fragment if the socket is set to force MTU discovery
 	 * or if the skb it not generated by a local socket.  (This last
@@ -1063,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 		inet->cork.fl = *fl;
 		np->cork.hop_limit = hlimit;
 		np->cork.tclass = tclass;
-		mtu = dst_mtu(rt->u.dst.path);
+		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?
+		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);
 		if (np->frag_size < mtu) {
 			if (np->frag_size)
 				mtu = np->frag_size;
net/ipv6/ipv6_sockglue.c +1 -1
index da930fa..aa3d07c 100644
@@ -694,7 +694,7 @@ done:
 		retv = ip6_ra_control(sk, val, NULL);
 		break;
 	case IPV6_MTU_DISCOVER:
-		if (val<0 || val>2)
+		if (val<0 || val>3)
 			goto e_inval;
 		np->pmtudisc = val;
 		retv = 0;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty