gre: Check that the IP header is actually there before using it.
authorJesse Gross <jesse@nicira.com>
Tue, 23 Feb 2010 23:05:02 +0000 (18:05 -0500)
committerJesse Gross <jesse@nicira.com>
Fri, 5 Mar 2010 21:31:27 +0000 (16:31 -0500)
GRE is nominally operating at layer 2 but it has some special
features for IP packets.  This checks that the IP header is present
before trying to read it.  If it is not there, we just disable the
special features but still process the packet.

datapath/linux-2.6/compat-2.6/ip_gre.c

index ec0f0c5e2aa4f6f9d7a9a244f4566aba9fc61905..23a557400756cb8db0d797028f6637f510d87932 100644 (file)
@@ -467,6 +467,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 +539,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 +694,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 +729,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 +737,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;
 
@@ -917,6 +943,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;