aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorBandan Das <bandan.das@stratus.com>2010-09-19 05:34:33 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-19 15:42:34 -0400
commit462fb2af9788a82a534f8184abfde31574e1cfa0 (patch)
tree63a8abf3657bb4ce586fdf14c89e6d81ca280725 /net
parentaef3ea33e85035f7c827c1db9155f97f4b7ee725 (diff)
bridge : Sanitize skb before it enters the IP stack
Related dicussion here : http://lkml.org/lkml/2010/9/3/16 Introduce a function br_parse_ip_options that will audit the skb and possibly refill IP options before a packet enters the IP stack. If no options are present, the function will zero out the skb cb area so that it is not misinterpreted as options by some unsuspecting IP layer routine. If packet consistency fails, drop it. Signed-off-by: Bandan Das <bandan.das@stratus.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_netfilter.c107
-rw-r--r--net/ipv4/ip_options.c3
2 files changed, 80 insertions, 30 deletions
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 137f23259a93..77f7b5fda45a 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -209,6 +209,72 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb)
209 skb->protocol = htons(ETH_P_PPP_SES); 209 skb->protocol = htons(ETH_P_PPP_SES);
210} 210}
211 211
212/* When handing a packet over to the IP layer
213 * check whether we have a skb that is in the
214 * expected format
215 */
216
217int br_parse_ip_options(struct sk_buff *skb)
218{
219 struct ip_options *opt;
220 struct iphdr *iph;
221 struct net_device *dev = skb->dev;
222 u32 len;
223
224 iph = ip_hdr(skb);
225 opt = &(IPCB(skb)->opt);
226
227 /* Basic sanity checks */
228 if (iph->ihl < 5 || iph->version != 4)
229 goto inhdr_error;
230
231 if (!pskb_may_pull(skb, iph->ihl*4))
232 goto inhdr_error;
233
234 iph = ip_hdr(skb);
235 if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
236 goto inhdr_error;
237
238 len = ntohs(iph->tot_len);
239 if (skb->len < len) {
240 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
241 goto drop;
242 } else if (len < (iph->ihl*4))
243 goto inhdr_error;
244
245 if (pskb_trim_rcsum(skb, len)) {
246 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
247 goto drop;
248 }
249
250 /* Zero out the CB buffer if no options present */
251 if (iph->ihl == 5) {
252 memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
253 return 0;
254 }
255
256 opt->optlen = iph->ihl*4 - sizeof(struct iphdr);
257 if (ip_options_compile(dev_net(dev), opt, skb))
258 goto inhdr_error;
259
260 /* Check correct handling of SRR option */
261 if (unlikely(opt->srr)) {
262 struct in_device *in_dev = __in_dev_get_rcu(dev);
263 if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev))
264 goto drop;
265
266 if (ip_options_rcv_srr(skb))
267 goto drop;
268 }
269
270 return 0;
271
272inhdr_error:
273 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
274drop:
275 return -1;
276}
277
212/* Fill in the header for fragmented IP packets handled by 278/* Fill in the header for fragmented IP packets handled by
213 * the IPv4 connection tracking code. 279 * the IPv4 connection tracking code.
214 */ 280 */
@@ -549,7 +615,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
549{ 615{
550 struct net_bridge_port *p; 616 struct net_bridge_port *p;
551 struct net_bridge *br; 617 struct net_bridge *br;
552 struct iphdr *iph;
553 __u32 len = nf_bridge_encap_header_len(skb); 618 __u32 len = nf_bridge_encap_header_len(skb);
554 619
555 if (unlikely(!pskb_may_pull(skb, len))) 620 if (unlikely(!pskb_may_pull(skb, len)))
@@ -578,28 +643,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
578 643
579 nf_bridge_pull_encap_header_rcsum(skb); 644 nf_bridge_pull_encap_header_rcsum(skb);
580 645
581 if (!pskb_may_pull(skb, sizeof(struct iphdr))) 646 if (br_parse_ip_options(skb))
582 goto inhdr_error; 647 /* Drop invalid packet */
583 648 goto out;
584 iph = ip_hdr(skb);
585 if (iph->ihl < 5 || iph->version != 4)
586 goto inhdr_error;
587
588 if (!pskb_may_pull(skb, 4 * iph->ihl))
589 goto inhdr_error;
590
591 iph = ip_hdr(skb);
592 if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
593 goto inhdr_error;
594
595 len = ntohs(iph->tot_len);
596 if (skb->len < len || len < 4 * iph->ihl)
597 goto inhdr_error;
598
599 pskb_trim_rcsum(skb, len);
600
601 /* BUG: Should really parse the IP options here. */
602 memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
603 649
604 nf_bridge_put(skb->nf_bridge); 650 nf_bridge_put(skb->nf_bridge);
605 if (!nf_bridge_alloc(skb)) 651 if (!nf_bridge_alloc(skb))
@@ -614,8 +660,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
614 660
615 return NF_STOLEN; 661 return NF_STOLEN;
616 662
617inhdr_error:
618// IP_INC_STATS_BH(IpInHdrErrors);
619out: 663out:
620 return NF_DROP; 664 return NF_DROP;
621} 665}
@@ -759,14 +803,19 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
759#if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE) 803#if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE)
760static int br_nf_dev_queue_xmit(struct sk_buff *skb) 804static int br_nf_dev_queue_xmit(struct sk_buff *skb)
761{ 805{
806 int ret;
807
762 if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) && 808 if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
763 skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu && 809 skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&
764 !skb_is_gso(skb)) { 810 !skb_is_gso(skb)) {
765 /* BUG: Should really parse the IP options here. */ 811 if (br_parse_ip_options(skb))
766 memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 812 /* Drop invalid packet */
767 return ip_fragment(skb, br_dev_queue_push_xmit); 813 return NF_DROP;
814 ret = ip_fragment(skb, br_dev_queue_push_xmit);
768 } else 815 } else
769 return br_dev_queue_push_xmit(skb); 816 ret = br_dev_queue_push_xmit(skb);
817
818 return ret;
770} 819}
771#else 820#else
772static int br_nf_dev_queue_xmit(struct sk_buff *skb) 821static int br_nf_dev_queue_xmit(struct sk_buff *skb)
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index ba9836c488ed..1906fa35860c 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -466,7 +466,7 @@ error:
466 } 466 }
467 return -EINVAL; 467 return -EINVAL;
468} 468}
469 469EXPORT_SYMBOL(ip_options_compile);
470 470
471/* 471/*
472 * Undo all the changes done by ip_options_compile(). 472 * Undo all the changes done by ip_options_compile().
@@ -646,3 +646,4 @@ int ip_options_rcv_srr(struct sk_buff *skb)
646 } 646 }
647 return 0; 647 return 0;
648} 648}
649EXPORT_SYMBOL(ip_options_rcv_srr);