Only send a 'need to frag' ICMP message when the "Don't Fragment" bit is set. If it's not set then the packet can/will be fragmented.
This fixes sending an 'need to frag' message on a client that did not set the DF bit, i.e.: $ ping -s 1300 -M dont -c5 192.168.235.2 PING 192.168.235.3 (192.168.235.3) 1300(1328) bytes of data. From 192.168.236.254 icmp_seq=1 Frag needed and DF set (mtu = 1214) Signed-off-by: Bram Yvahk <bram-yv...@mail.wizbit.be> --- net/ipv4/ip_vti.c | 43 +++++++++++++++++++++++++++-------------- net/ipv6/ip6_vti.c | 56 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 68a21bf..5738e44 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -196,6 +196,34 @@ static bool vti_state_check(const struct xfrm_state *x, __be32 dst, __be32 src) return true; } +static bool vti_tunnel_check_size(struct sk_buff *skb) +{ + int mtu; + + if (skb->protocol == htons(ETH_P_IP)) { + if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df) + return true; + } + + mtu = dst_mtu(skb_dst(skb)); + if (skb->len > mtu) { + skb_dst_update_pmtu(skb, mtu); + if (skb->protocol == htons(ETH_P_IP)) { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + } else { + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + } + + return false; + } + + return true; +} + static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) { @@ -205,7 +233,6 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, struct net_device *tdev; /* Device to other host */ int pkt_len = skb->len; int err; - int mtu; if (!dst) { dev->stats.tx_carrier_errors++; @@ -233,19 +260,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error; } - mtu = dst_mtu(dst); - if (skb->len > mtu) { - skb_dst_update_pmtu(skb, mtu); - if (skb->protocol == htons(ETH_P_IP)) { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(mtu)); - } else { - if (mtu < IPV6_MIN_MTU) - mtu = IPV6_MIN_MTU; - - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - } - + if (!vti_tunnel_check_size(skb)) { dst_release(dst); goto tx_error; } diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 8b6eeff..47f178c 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -436,6 +436,46 @@ static bool vti6_state_check(const struct xfrm_state *x, } /** + * vti6_tunnel_check_size - check size of packet + * @skb: the outgoing socket buffer + * + * Description: + * Check if packet is too large (> pmtu) + * + * Return: + * true if size of packet is ok + * false if packet is too large + **/ +static bool vti6_tunnel_check_size(struct sk_buff *skb) +{ + int mtu; + + if (skb->protocol == htons(ETH_P_IP)) { + if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df) + return true; + } + + mtu = dst_mtu(skb_dst(skb)); + if (skb->len > mtu) { + skb_dst_update_pmtu(skb, mtu); + + if (skb->protocol == htons(ETH_P_IPV6)) { + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + } else { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + } + + return false; + } + + return true; +} + +/** * vti6_xmit - send a packet * @skb: the outgoing socket buffer * @dev: the outgoing tunnel device @@ -451,7 +491,6 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) struct xfrm_state *x; int pkt_len = skb->len; int err = -1; - int mtu; if (!dst) goto tx_err_link_failure; @@ -481,20 +520,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) goto tx_err_dst_release; } - mtu = dst_mtu(dst); - if (skb->len > mtu) { - skb_dst_update_pmtu(skb, mtu); - - if (skb->protocol == htons(ETH_P_IPV6)) { - if (mtu < IPV6_MIN_MTU) - mtu = IPV6_MIN_MTU; - - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - } else { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(mtu)); - } - + if (!vti6_tunnel_check_size(skb)) { err = -EMSGSIZE; goto tx_err_dst_release; } -- 2.7.0