]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/bridge/br_netfilter.c
net: use the macros defined for the members of flowi
[net-next-2.6.git] / net / bridge / br_netfilter.c
index 137f23259a93947ce581a9d14c8f6048f3ca2c19..6e13920939113b07e7ae1bbed452323e261865d2 100644 (file)
@@ -64,22 +64,24 @@ static int brnf_filter_pppoe_tagged __read_mostly = 0;
 
 static inline __be16 vlan_proto(const struct sk_buff *skb)
 {
-       return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+       if (vlan_tx_tag_present(skb))
+               return skb->protocol;
+       else if (skb->protocol == htons(ETH_P_8021Q))
+               return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+       else
+               return 0;
 }
 
 #define IS_VLAN_IP(skb) \
-       (skb->protocol == htons(ETH_P_8021Q) && \
-        vlan_proto(skb) == htons(ETH_P_IP) &&  \
+       (vlan_proto(skb) == htons(ETH_P_IP) && \
         brnf_filter_vlan_tagged)
 
 #define IS_VLAN_IPV6(skb) \
-       (skb->protocol == htons(ETH_P_8021Q) && \
-        vlan_proto(skb) == htons(ETH_P_IPV6) &&\
+       (vlan_proto(skb) == htons(ETH_P_IPV6) && \
         brnf_filter_vlan_tagged)
 
 #define IS_VLAN_ARP(skb) \
-       (skb->protocol == htons(ETH_P_8021Q) && \
-        vlan_proto(skb) == htons(ETH_P_ARP) && \
+       (vlan_proto(skb) == htons(ETH_P_ARP) && \
         brnf_filter_vlan_tagged)
 
 static inline __be16 pppoe_proto(const struct sk_buff *skb)
@@ -106,7 +108,6 @@ static struct dst_ops fake_dst_ops = {
        .family =               AF_INET,
        .protocol =             cpu_to_be16(ETH_P_IP),
        .update_pmtu =          fake_update_pmtu,
-       .entries =              ATOMIC_INIT(0),
 };
 
 /*
@@ -130,17 +131,18 @@ void br_netfilter_rtable_init(struct net_bridge *br)
 
 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
 {
-       if (!br_port_exists(dev))
-               return NULL;
-       return &br_port_get_rcu(dev)->br->fake_rtable;
+       struct net_bridge_port *port;
+
+       port = br_port_get_rcu(dev);
+       return port ? &port->br->fake_rtable : NULL;
 }
 
 static inline struct net_device *bridge_parent(const struct net_device *dev)
 {
-       if (!br_port_exists(dev))
-               return NULL;
+       struct net_bridge_port *port;
 
-       return br_port_get_rcu(dev)->br->dev;
+       port = br_port_get_rcu(dev);
+       return port ? port->br->dev : NULL;
 }
 
 static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
@@ -209,6 +211,72 @@ static inline void nf_bridge_update_protocol(struct sk_buff *skb)
                skb->protocol = htons(ETH_P_PPP_SES);
 }
 
+/* When handing a packet over to the IP layer
+ * check whether we have a skb that is in the
+ * expected format
+ */
+
+static int br_parse_ip_options(struct sk_buff *skb)
+{
+       struct ip_options *opt;
+       struct iphdr *iph;
+       struct net_device *dev = skb->dev;
+       u32 len;
+
+       iph = ip_hdr(skb);
+       opt = &(IPCB(skb)->opt);
+
+       /* Basic sanity checks */
+       if (iph->ihl < 5 || iph->version != 4)
+               goto inhdr_error;
+
+       if (!pskb_may_pull(skb, iph->ihl*4))
+               goto inhdr_error;
+
+       iph = ip_hdr(skb);
+       if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
+               goto inhdr_error;
+
+       len = ntohs(iph->tot_len);
+       if (skb->len < len) {
+               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
+               goto drop;
+       } else if (len < (iph->ihl*4))
+               goto inhdr_error;
+
+       if (pskb_trim_rcsum(skb, len)) {
+               IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
+               goto drop;
+       }
+
+       /* Zero out the CB buffer if no options present */
+       if (iph->ihl == 5) {
+               memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
+               return 0;
+       }
+
+       opt->optlen = iph->ihl*4 - sizeof(struct iphdr);
+       if (ip_options_compile(dev_net(dev), opt, skb))
+               goto inhdr_error;
+
+       /* Check correct handling of SRR option */
+       if (unlikely(opt->srr)) {
+               struct in_device *in_dev = __in_dev_get_rcu(dev);
+               if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev))
+                       goto drop;
+
+               if (ip_options_rcv_srr(skb))
+                       goto drop;
+       }
+
+       return 0;
+
+inhdr_error:
+       IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
+drop:
+       return -1;
+}
+
 /* Fill in the header for fragmented IP packets handled by
  * the IPv4 connection tracking code.
  */
@@ -345,13 +413,8 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
        if (dnat_took_place(skb)) {
                if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) {
                        struct flowi fl = {
-                               .nl_u = {
-                                       .ip4_u = {
-                                                .daddr = iph->daddr,
-                                                .saddr = 0,
-                                                .tos = RT_TOS(iph->tos) },
-                               },
-                               .proto = 0,
+                               .fl4_dst = iph->daddr,
+                               .fl4_tos = RT_TOS(iph->tos),
                        };
                        struct in_device *in_dev = __in_dev_get_rcu(dev);
 
@@ -549,7 +612,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
 {
        struct net_bridge_port *p;
        struct net_bridge *br;
-       struct iphdr *iph;
        __u32 len = nf_bridge_encap_header_len(skb);
 
        if (unlikely(!pskb_may_pull(skb, len)))
@@ -578,28 +640,9 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
 
        nf_bridge_pull_encap_header_rcsum(skb);
 
-       if (!pskb_may_pull(skb, sizeof(struct iphdr)))
-               goto inhdr_error;
-
-       iph = ip_hdr(skb);
-       if (iph->ihl < 5 || iph->version != 4)
-               goto inhdr_error;
-
-       if (!pskb_may_pull(skb, 4 * iph->ihl))
-               goto inhdr_error;
-
-       iph = ip_hdr(skb);
-       if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
-               goto inhdr_error;
-
-       len = ntohs(iph->tot_len);
-       if (skb->len < len || len < 4 * iph->ihl)
-               goto inhdr_error;
-
-       pskb_trim_rcsum(skb, len);
-
-       /* BUG: Should really parse the IP options here. */
-       memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
+       if (br_parse_ip_options(skb))
+               /* Drop invalid packet */
+               goto out;
 
        nf_bridge_put(skb->nf_bridge);
        if (!nf_bridge_alloc(skb))
@@ -614,8 +657,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
 
        return NF_STOLEN;
 
-inhdr_error:
-//      IP_INC_STATS_BH(IpInHdrErrors);
 out:
        return NF_DROP;
 }
@@ -759,14 +800,19 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
 #if defined(CONFIG_NF_CONNTRACK_IPV4) || defined(CONFIG_NF_CONNTRACK_IPV4_MODULE)
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
 {
+       int ret;
+
        if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
            skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&
            !skb_is_gso(skb)) {
-               /* BUG: Should really parse the IP options here. */
-               memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
-               return ip_fragment(skb, br_dev_queue_push_xmit);
+               if (br_parse_ip_options(skb))
+                       /* Drop invalid packet */
+                       return NF_DROP;
+               ret = ip_fragment(skb, br_dev_queue_push_xmit);
        } else
-               return br_dev_queue_push_xmit(skb);
+               ret = br_dev_queue_push_xmit(skb);
+
+       return ret;
 }
 #else
 static int br_nf_dev_queue_xmit(struct sk_buff *skb)
@@ -954,15 +1000,22 @@ int __init br_netfilter_init(void)
 {
        int ret;
 
-       ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+       ret = dst_entries_init(&fake_dst_ops);
        if (ret < 0)
                return ret;
+
+       ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+       if (ret < 0) {
+               dst_entries_destroy(&fake_dst_ops);
+               return ret;
+       }
 #ifdef CONFIG_SYSCTL
        brnf_sysctl_header = register_sysctl_paths(brnf_path, brnf_table);
        if (brnf_sysctl_header == NULL) {
                printk(KERN_WARNING
                       "br_netfilter: can't register to sysctl.\n");
                nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+               dst_entries_destroy(&fake_dst_ops);
                return -ENOMEM;
        }
 #endif
@@ -976,4 +1029,5 @@ void br_netfilter_fini(void)
 #ifdef CONFIG_SYSCTL
        unregister_sysctl_table(brnf_sysctl_header);
 #endif
+       dst_entries_destroy(&fake_dst_ops);
 }