Skip to content

Commit 462fb2a

Browse files
Bandan Dasdavem330
authored andcommitted
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>
1 parent aef3ea3 commit 462fb2a

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

net/bridge/br_netfilter.c

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,72 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb)
209209
skb->protocol = htons(ETH_P_PPP_SES);
210210
}
211211

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+
217+
int 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+
272+
inhdr_error:
273+
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
274+
drop:
275+
return -1;
276+
}
277+
212278
/* Fill in the header for fragmented IP packets handled by
213279
* the IPv4 connection tracking code.
214280
*/
@@ -549,7 +615,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
549615
{
550616
struct net_bridge_port *p;
551617
struct net_bridge *br;
552-
struct iphdr *iph;
553618
__u32 len = nf_bridge_encap_header_len(skb);
554619

555620
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,
578643

579644
nf_bridge_pull_encap_header_rcsum(skb);
580645

581-
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
582-
goto inhdr_error;
583-
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));
646+
if (br_parse_ip_options(skb))
647+
/* Drop invalid packet */
648+
goto out;
603649

604650
nf_bridge_put(skb->nf_bridge);
605651
if (!nf_bridge_alloc(skb))
@@ -614,8 +660,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
614660

615661
return NF_STOLEN;
616662

617-
inhdr_error:
618-
// IP_INC_STATS_BH(IpInHdrErrors);
619663
out:
620664
return NF_DROP;
621665
}
@@ -759,14 +803,19 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
759803
#if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE)
760804
static int br_nf_dev_queue_xmit(struct sk_buff *skb)
761805
{
806+
int ret;
807+
762808
if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
763809
skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&
764810
!skb_is_gso(skb)) {
765-
/* BUG: Should really parse the IP options here. */
766-
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
767-
return ip_fragment(skb, br_dev_queue_push_xmit);
811+
if (br_parse_ip_options(skb))
812+
/* Drop invalid packet */
813+
return NF_DROP;
814+
ret = ip_fragment(skb, br_dev_queue_push_xmit);
768815
} else
769-
return br_dev_queue_push_xmit(skb);
816+
ret = br_dev_queue_push_xmit(skb);
817+
818+
return ret;
770819
}
771820
#else
772821
static int br_nf_dev_queue_xmit(struct sk_buff *skb)

net/ipv4/ip_options.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ int ip_options_compile(struct net *net,
466466
}
467467
return -EINVAL;
468468
}
469-
469+
EXPORT_SYMBOL(ip_options_compile);
470470

471471
/*
472472
* Undo all the changes done by ip_options_compile().
@@ -646,3 +646,4 @@ int ip_options_rcv_srr(struct sk_buff *skb)
646646
}
647647
return 0;
648648
}
649+
EXPORT_SYMBOL(ip_options_rcv_srr);

0 commit comments

Comments
 (0)