On recent kernels there are two fields that we can use to keep track
of the overhead due to tunneling: hard_header_len and needed_headroom.
On older kernels everything got shoved into hard_header_len and the
actual header length got lost, which caused MTU calculations to be
incorrect. Since we know the device type we can figure out what the
header length should be.
+static unsigned int tunnel_hard_header_len(struct net_device *dev)
+{
+#ifdef HAVE_NETDEV_NEEDED_HEADROOM
+ return dev->hard_header_len;
+#else
+ return (dev->type == ARPHRD_ETHER) ? ETH_HLEN : 0;
+#endif
+}
static void ipgre_err(struct sk_buff *skb, u32 info)
{
static void ipgre_err(struct sk_buff *skb, u32 info)
{
df = tiph->frag_off;
if (df)
df = tiph->frag_off;
if (df)
-#ifdef HAVE_NETDEV_NEEDED_HEADROOM
- mtu = dst_mtu(&rt->u.dst) - dev->hard_header_len - tunnel->hlen;
-#else
- mtu = dst_mtu(&rt->u.dst) - tunnel->hlen;
-#endif
+ mtu = dst_mtu(&rt->u.dst) - tunnel_hard_header_len(dev)
+ - tunnel->hlen;
else
mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
else
mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
}
#ifdef HAVE_NETDEV_NEEDED_HEADROOM
dev->needed_headroom = hlen + addend;
}
#ifdef HAVE_NETDEV_NEEDED_HEADROOM
dev->needed_headroom = hlen + addend;
- mtu -= dev->hard_header_len + addend;
#else
dev->hard_header_len = hlen + addend;
#else
dev->hard_header_len = hlen + addend;
+ mtu -= tunnel_hard_header_len(dev) + addend;
tunnel->hlen = addend;
if (mtu < 68)
tunnel->hlen = addend;
if (mtu < 68)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
if (new_mtu < 68 ||
{
struct ip_tunnel *tunnel = netdev_priv(dev);
if (new_mtu < 68 ||
-#ifdef HAVE_NETDEV_NEEDED_HEADROOM
- new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
-#else
- new_mtu > 0xFFF8 - tunnel->hlen)
-#endif
+ new_mtu > 0xFFF8 - tunnel_hard_header_len(dev) - tunnel->hlen)
return -EINVAL;
dev->mtu = new_mtu;
return 0;
return -EINVAL;
dev->mtu = new_mtu;
return 0;