gre: Correctly account for hardware header length.
[sliver-openvswitch.git] / datapath / linux-2.6 / compat-2.6 / ip_gre.c
index 7b23d1a..8c70ed2 100644 (file)
    fatal route to network, even if it were you who configured
    fatal static route: you are innocent. :-)
 
-
+   XXX: Forcing the DF flag on was done only when setting up tunnels via the
+       ioctl interface and not Netlink.  Since it prevents some operations
+       and isn't very transparent I removed it.  It seems nobody really
+       cared about it anyways.
+        Moral: don't create loops.
 
    3. Really, ipv4/ipip.c, ipv4/ip_gre.c and ipv6/sit.c contain
    practically identical code. It would be good to glue them
@@ -147,7 +151,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev);
 
 #define HASH_SIZE  16
 
-static int ipgre_net_id;
+static int ipgre_net_id __read_mostly;
 struct ipgre_net {
        struct ip_tunnel *tunnels[4][HASH_SIZE];
 
@@ -442,6 +446,14 @@ static void ipgre_tunnel_uninit(struct net_device *dev)
        dev_put(dev);
 }
 
+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)
 {
@@ -467,6 +479,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        struct ip_tunnel *t;
        __be16 flags;
 
+       if (skb_headlen(skb) < grehlen)
+               return;
+
        flags = p[0];
        if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
                if (flags&(GRE_VERSION|GRE_ROUTING))
@@ -536,8 +551,16 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
        if (INET_ECN_is_ce(iph->tos)) {
                if (skb->protocol == htons(ETH_P_IP)) {
+                       if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
+                           + sizeof(struct iphdr) - skb->data)))
+                               return;
+
                        IP_ECN_set_ce(ip_hdr(skb));
                } else if (skb->protocol == htons(ETH_P_IPV6)) {
+                       if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
+                           + sizeof(struct ipv6hdr) - skb->data)))
+                               return;
+
                        IP6_ECN_set_ce(ipv6_hdr(skb));
                }
        }
@@ -683,6 +706,8 @@ static int ipgre_rcv(struct sk_buff *skb)
                nf_reset(skb);
 
                skb_reset_network_header(skb);
+
+               /* Invalidates pointers. */
                ipgre_ecn_decapsulate(iph, skb);
 
                netif_rx(skb);
@@ -716,6 +741,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
        int    gre_hlen;
        __be32 dst;
        int    mtu;
+       u8   original_protocol;
 
 #ifdef HAVE_NETDEV_STATS
        stats = &dev->stats;
@@ -723,6 +749,18 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
        stats = &tunnel->stat;
 #endif
 
+       /* Validate the protocol headers before we try to use them. */
+       original_protocol = skb->protocol;
+       if (skb->protocol == htons(ETH_P_IP)) {
+               if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
+                   + sizeof(struct iphdr) - skb->data)))
+                       skb->protocol = 0;
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+               if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
+                   + sizeof(struct ipv6hdr) - skb->data)))
+                       skb->protocol = 0;
+       }
+
        if (dev->type == ARPHRD_ETHER)
                IPCB(skb)->flags = 0;
 
@@ -783,6 +821,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                tos = 0;
                if (skb->protocol == htons(ETH_P_IP))
                        tos = old_iph->tos;
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       tos = ipv6_get_dsfield(ipv6_hdr(skb));
        }
 
        {
@@ -807,11 +847,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
 
        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;
 
@@ -917,6 +954,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
        }
 
+       skb->protocol = original_protocol;
+
        ((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags;
        ((__be16 *)(iph + 1))[1] = (dev->type == ARPHRD_ETHER) ?
                                   htons(ETH_P_TEB) : skb->protocol;
@@ -1008,11 +1047,10 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev)
        }
 #ifdef HAVE_NETDEV_NEEDED_HEADROOM
        dev->needed_headroom = hlen + addend;
-       mtu -= dev->hard_header_len + addend;
 #else
        dev->hard_header_len = hlen + addend;
-       mtu -= addend;
 #endif
+       mtu -= tunnel_hard_header_len(dev) + addend;
        tunnel->hlen = addend;
 
        if (mtu < 68)
@@ -1073,9 +1111,6 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                add_tunnel = (cmd == SIOCADDTUNNEL || cmd == SIOCADDGRETAP);
                gretap = (cmd == SIOCADDGRETAP || cmd == SIOCCHGGRETAP);
 
-               if (p.iph.ttl)
-                       p.iph.frag_off |= htons(IP_DF);
-
                if (!(p.i_flags&GRE_KEY))
                        p.i_key = 0;
                if (!(p.o_flags&GRE_KEY))
@@ -1173,11 +1208,7 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
        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;
@@ -1465,17 +1496,8 @@ static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
 
 static int ipgre_init_net(struct net *net)
 {
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int err;
-       struct ipgre_net *ign;
-
-       err = -ENOMEM;
-       ign = kzalloc(sizeof(struct ipgre_net), GFP_KERNEL);
-       if (ign == NULL)
-               goto err_alloc;
-
-       err = net_assign_generic(net, ipgre_net_id, ign);
-       if (err < 0)
-               goto err_assign;
 
        ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), GRE_IOCTL_DEVICE,
                                           ipgre_tunnel_setup);
@@ -1502,10 +1524,6 @@ static int ipgre_init_net(struct net *net)
 err_reg_dev:
        free_netdev(ign->fb_tunnel_dev);
 err_alloc_dev:
-       /* nothing */
-err_assign:
-       kfree(ign);
-err_alloc:
        return err;
 }
 
@@ -1519,12 +1537,13 @@ static void ipgre_exit_net(struct net *net)
        ipgre_destroy_tunnels(ign, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
-       kfree(ign);
 }
 
 static struct pernet_operations ipgre_net_ops = {
        .init = ipgre_init_net,
        .exit = ipgre_exit_net,
+       .id   = &ipgre_net_id,
+       .size = sizeof(struct ipgre_net),
 };
 
 static int ipgre_tap_init(struct net_device *dev)
@@ -1573,6 +1592,7 @@ static void ipgre_tap_setup(struct net_device *dev)
 
        dev->iflink             = 0;
        dev->features           |= NETIF_F_NETNS_LOCAL;
+       dev->tx_queue_len       = 0;
 
        SET_ETHTOOL_OPS(dev, &ethtool_ops);
 }
@@ -1661,8 +1681,13 @@ static void ipgre_netlink_parms(struct nlattr *data[],
                parms->iph.frag_off = htons(IP_DF);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)
+static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
+                        struct nlattr *data[])
+#else
 static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[],
                         struct nlattr *data[])
+#endif
 {
        struct ip_tunnel *nt;
        struct net *net = dev_net(dev);
@@ -1861,7 +1886,7 @@ static int __init ipgre_init(void)
                return -EAGAIN;
        }
 
-       err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops);
+       err = register_pernet_device(&ipgre_net_ops);
        if (err < 0)
                goto gen_device_failed;
 
@@ -1882,7 +1907,7 @@ out:
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
-       unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops);
+       unregister_pernet_device(&ipgre_net_ops);
 #endif
 gen_device_failed:
        inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
@@ -1896,7 +1921,7 @@ static void __exit ipgre_fini(void)
        rtnl_link_unregister(&ipgre_tap_ops);
        rtnl_link_unregister(&ipgre_link_ops);
 #endif
-       unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops);
+       unregister_pernet_device(&ipgre_net_ops);
        if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
                printk(KERN_INFO "ipgre close: can't remove protocol\n");
 }