netfilter: xtables: move extension arguments into compound structure (6/6)
[safe/jmp/linux-2.6] / net / bridge / br_netfilter.c
index 5d8b939..a4abed5 100644 (file)
@@ -101,30 +101,30 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
         pppoe_proto(skb) == htons(PPP_IPV6) && \
         brnf_filter_pppoe_tagged)
 
-/* We need these fake structures to make netfilter happy --
- * lots of places assume that skb->dst != NULL, which isn't
- * all that unreasonable.
- *
+/*
+ * Initialize bogus route table used to keep netfilter happy.
  * Currently, we fill in the PMTU entry because netfilter
  * refragmentation needs it, and the rt_flags entry because
  * ipt_REJECT needs it.  Future netfilter modules might
- * require us to fill additional fields. */
-static struct net_device __fake_net_device = {
-       .hard_header_len        = ETH_HLEN
-};
+ * require us to fill additional fields.
+ */
+void br_netfilter_rtable_init(struct net_bridge *br)
+{
+       struct rtable *rt = &br->fake_rtable;
 
-static struct rtable __fake_rtable = {
-       .u = {
-               .dst = {
-                       .__refcnt               = ATOMIC_INIT(1),
-                       .dev                    = &__fake_net_device,
-                       .path                   = &__fake_rtable.u.dst,
-                       .metrics                = {[RTAX_MTU - 1] = 1500},
-                       .flags                  = DST_NOXFRM,
-               }
-       },
-       .rt_flags       = 0,
-};
+       atomic_set(&rt->u.dst.__refcnt, 1);
+       rt->u.dst.dev = br->dev;
+       rt->u.dst.path = &rt->u.dst;
+       rt->u.dst.metrics[RTAX_MTU - 1] = 1500;
+       rt->u.dst.flags = DST_NOXFRM;
+}
+
+static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
+{
+       struct net_bridge_port *port = rcu_dereference(dev->br_port);
+
+       return port ? &port->br->fake_rtable : NULL;
+}
 
 static inline struct net_device *bridge_parent(const struct net_device *dev)
 {
@@ -142,6 +142,23 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
        return skb->nf_bridge;
 }
 
+static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
+{
+       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+       if (atomic_read(&nf_bridge->use) > 1) {
+               struct nf_bridge_info *tmp = nf_bridge_alloc(skb);
+
+               if (tmp) {
+                       memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info));
+                       atomic_set(&tmp->use, 1);
+                       nf_bridge_put(nf_bridge);
+               }
+               nf_bridge = tmp;
+       }
+       return nf_bridge;
+}
+
 static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
 {
        unsigned int len = nf_bridge_encap_header_len(skb);
@@ -206,8 +223,12 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
        }
        nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
 
-       skb->dst = (struct dst_entry *)&__fake_rtable;
-       dst_hold(skb->dst);
+       skb->rtable = bridge_parent_rtable(nf_bridge->physindev);
+       if (!skb->rtable) {
+               kfree_skb(skb);
+               return 0;
+       }
+       dst_hold(&skb->rtable->u.dst);
 
        skb->dev = nf_bridge->physindev;
        nf_bridge_push_encap_header(skb);
@@ -336,7 +357,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
                        if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
                                goto free_skb;
 
-                       if (!ip_route_output_key(&rt, &fl)) {
+                       if (!ip_route_output_key(&init_net, &rt, &fl)) {
                                /* - Bridged-and-DNAT'ed traffic doesn't
                                 *   require ip_forwarding. */
                                if (((struct dst_entry *)rt)->dev == dev) {
@@ -371,8 +392,12 @@ bridged_dnat:
                        skb->pkt_type = PACKET_HOST;
                }
        } else {
-               skb->dst = (struct dst_entry *)&__fake_rtable;
-               dst_hold(skb->dst);
+               skb->rtable = bridge_parent_rtable(nf_bridge->physindev);
+               if (!skb->rtable) {
+                       kfree_skb(skb);
+                       return 0;
+               }
+               dst_hold(&skb->rtable->u.dst);
        }
 
        skb->dev = nf_bridge->physindev;
@@ -494,7 +519,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
        if (!setup_pre_routing(skb))
                return NF_DROP;
 
-       NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
                br_nf_pre_routing_finish_ipv6);
 
        return NF_STOLEN;
@@ -567,7 +592,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
                return NF_DROP;
        store_orig_dstaddr(skb);
 
-       NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
                br_nf_pre_routing_finish);
 
        return NF_STOLEN;
@@ -591,9 +616,9 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb,
                                   const struct net_device *out,
                                   int (*okfn)(struct sk_buff *))
 {
-       if (skb->dst == (struct dst_entry *)&__fake_rtable) {
-               dst_release(skb->dst);
-               skb->dst = NULL;
+       if (skb->rtable && skb->rtable == bridge_parent_rtable(in)) {
+               dst_release(&skb->rtable->u.dst);
+               skb->rtable = NULL;
        }
 
        return NF_ACCEPT;
@@ -632,11 +657,16 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
 {
        struct nf_bridge_info *nf_bridge;
        struct net_device *parent;
-       int pf;
+       u_int8_t pf;
 
        if (!skb->nf_bridge)
                return NF_ACCEPT;
 
+       /* Need exclusive nf_bridge_info since we might have multiple
+        * different physoutdevs. */
+       if (!nf_bridge_unshare(skb))
+               return NF_DROP;
+
        parent = bridge_parent(out);
        if (!parent)
                return NF_DROP;
@@ -659,7 +689,7 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
        nf_bridge->mask |= BRNF_BRIDGED;
        nf_bridge->physoutdev = skb->dev;
 
-       NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent,
+       NF_HOOK(pf, NF_INET_FORWARD, skb, bridge_parent(in), parent,
                br_nf_forward_finish);
 
        return NF_STOLEN;
@@ -718,6 +748,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff *skb,
        if (!skb->nf_bridge)
                return NF_ACCEPT;
 
+       /* Need exclusive nf_bridge_info since we might have multiple
+        * different physoutdevs. */
+       if (!nf_bridge_unshare(skb))
+               return NF_DROP;
+
        nf_bridge = skb->nf_bridge;
        if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT))
                return NF_ACCEPT;
@@ -756,7 +791,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
 {
        struct nf_bridge_info *nf_bridge = skb->nf_bridge;
        struct net_device *realoutdev = bridge_parent(skb->dev);
-       int pf;
+       u_int8_t pf;
 
 #ifdef CONFIG_NETFILTER_DEBUG
        /* Be very paranoid. This probably won't happen anymore, but let's
@@ -801,11 +836,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
        nf_bridge_pull_encap_header(skb);
        nf_bridge_save_header(skb);
 
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-       if (nf_bridge->netoutdev)
-               realoutdev = nf_bridge->netoutdev;
-#endif
-       NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev,
+       NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev,
                br_nf_dev_queue_xmit);
 
        return NF_STOLEN;
@@ -844,7 +875,7 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb,
  * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
  * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
  * ip_refrag() can return NF_STOLEN. */
-static struct nf_hook_ops br_nf_ops[] = {
+static struct nf_hook_ops br_nf_ops[] __read_mostly = {
        { .hook = br_nf_pre_routing,
          .owner = THIS_MODULE,
          .pf = PF_BRIDGE,
@@ -878,12 +909,12 @@ static struct nf_hook_ops br_nf_ops[] = {
        { .hook = ip_sabotage_in,
          .owner = THIS_MODULE,
          .pf = PF_INET,
-         .hooknum = NF_IP_PRE_ROUTING,
+         .hooknum = NF_INET_PRE_ROUTING,
          .priority = NF_IP_PRI_FIRST, },
        { .hook = ip_sabotage_in,
          .owner = THIS_MODULE,
          .pf = PF_INET6,
-         .hooknum = NF_IP6_PRE_ROUTING,
+         .hooknum = NF_INET_PRE_ROUTING,
          .priority = NF_IP6_PRI_FIRST, },
 };
 
@@ -940,24 +971,10 @@ static ctl_table brnf_table[] = {
        { .ctl_name = 0 }
 };
 
-static ctl_table brnf_bridge_table[] = {
-       {
-               .ctl_name       = NET_BRIDGE,
-               .procname       = "bridge",
-               .mode           = 0555,
-               .child          = brnf_table,
-       },
-       { .ctl_name = 0 }
-};
-
-static ctl_table brnf_net_table[] = {
-       {
-               .ctl_name       = CTL_NET,
-               .procname       = "net",
-               .mode           = 0555,
-               .child          = brnf_bridge_table,
-       },
-       { .ctl_name = 0 }
+static struct ctl_path brnf_path[] = {
+       { .procname = "net", .ctl_name = CTL_NET, },
+       { .procname = "bridge", .ctl_name = NET_BRIDGE, },
+       { }
 };
 #endif
 
@@ -969,7 +986,7 @@ int __init br_netfilter_init(void)
        if (ret < 0)
                return ret;
 #ifdef CONFIG_SYSCTL
-       brnf_sysctl_header = register_sysctl_table(brnf_net_table);
+       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");