From 87036dbdea0cbebdd0869aefb9181817463350f0 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 23 Feb 2010 18:05:02 -0500 Subject: [PATCH] gre: Check that the IP header is actually there before using it. 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 | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/datapath/linux-2.6/compat-2.6/ip_gre.c b/datapath/linux-2.6/compat-2.6/ip_gre.c index ec0f0c5e..23a55740 100644 --- a/datapath/linux-2.6/compat-2.6/ip_gre.c +++ b/datapath/linux-2.6/compat-2.6/ip_gre.c @@ -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; -- 2.30.2