|
@@ -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);
|