netfilter: bridge-netfilter: update a comment in br_forward.c about ip_fragment()
[safe/jmp/linux-2.6] / net / bridge / br_forward.c
index 8be9f21..15abef7 100644 (file)
@@ -5,14 +5,13 @@
  *     Authors:
  *     Lennert Buytenhek               <buytenh@gnu.org>
  *
- *     $Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $
- *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  */
 
+#include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter_bridge.h>
 #include "br_private.h"
 
+static int deliver_clone(const struct net_bridge_port *prev,
+                        struct sk_buff *skb,
+                        void (*__packet_hook)(const struct net_bridge_port *p,
+                                              struct sk_buff *skb));
+
 /* Don't forward packets to originating port or forwarding diasabled */
-static inline int should_deliver(const struct net_bridge_port *p, 
+static inline int should_deliver(const struct net_bridge_port *p,
                                 const struct sk_buff *skb)
 {
-       return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING);
+       return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
+               p->state == BR_STATE_FORWARDING);
 }
 
 static inline unsigned packet_length(const struct sk_buff *skb)
@@ -35,16 +40,17 @@ static inline unsigned packet_length(const struct sk_buff *skb)
 int br_dev_queue_push_xmit(struct sk_buff *skb)
 {
        /* drop mtu oversized packets except gso */
-       if (packet_length(skb) > skb->dev->mtu && !skb_shinfo(skb)->gso_size)
+       if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
                kfree_skb(skb);
        else {
-#ifdef CONFIG_BRIDGE_NETFILTER
-               /* ip_refrag calls ip_fragment, doesn't copy the MAC header. */
-               nf_bridge_maybe_copy_header(skb);
-#endif
-               skb_push(skb, ETH_HLEN);
+               /* ip_fragment doesn't copy the MAC header */
+               if (nf_bridge_maybe_copy_header(skb))
+                       kfree_skb(skb);
+               else {
+                       skb_push(skb, ETH_HLEN);
 
-               dev_queue_xmit(skb);
+                       dev_queue_xmit(skb);
+               }
        }
 
        return 0;
@@ -52,7 +58,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
 
 int br_forward_finish(struct sk_buff *skb)
 {
-       return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
                       br_dev_queue_push_xmit);
 
 }
@@ -60,20 +66,25 @@ int br_forward_finish(struct sk_buff *skb)
 static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
        skb->dev = to->dev;
-       NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
-                       br_forward_finish);
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+               br_forward_finish);
 }
 
 static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
        struct net_device *indev;
 
+       if (skb_warn_if_lro(skb)) {
+               kfree_skb(skb);
+               return;
+       }
+
        indev = skb->dev;
        skb->dev = to->dev;
-       skb->ip_summed = CHECKSUM_NONE;
+       skb_forward_csum(skb);
 
-       NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
-                       br_forward_finish);
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+               br_forward_finish);
 }
 
 /* called with rcu_read_lock */
@@ -88,72 +99,167 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 }
 
 /* called with rcu_read_lock */
-void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
+void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0)
 {
        if (should_deliver(to, skb)) {
-               __br_forward(to, skb);
+               if (skb0)
+                       deliver_clone(to, skb, __br_forward);
+               else
+                       __br_forward(to, skb);
                return;
        }
 
-       kfree_skb(skb);
+       if (!skb0)
+               kfree_skb(skb);
 }
 
-/* called under bridge lock */
-static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
-       void (*__packet_hook)(const struct net_bridge_port *p, 
+static int deliver_clone(const struct net_bridge_port *prev,
+                        struct sk_buff *skb,
+                        void (*__packet_hook)(const struct net_bridge_port *p,
+                                              struct sk_buff *skb))
+{
+       skb = skb_clone(skb, GFP_ATOMIC);
+       if (!skb) {
+               struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
+
+               dev->stats.tx_dropped++;
+               return -ENOMEM;
+       }
+
+       __packet_hook(prev, skb);
+       return 0;
+}
+
+static struct net_bridge_port *maybe_deliver(
+       struct net_bridge_port *prev, struct net_bridge_port *p,
+       struct sk_buff *skb,
+       void (*__packet_hook)(const struct net_bridge_port *p,
                              struct sk_buff *skb))
 {
+       int err;
+
+       if (!should_deliver(p, skb))
+               return prev;
+
+       if (!prev)
+               goto out;
+
+       err = deliver_clone(prev, skb, __packet_hook);
+       if (err)
+               return ERR_PTR(err);
+
+out:
+       return p;
+}
+
+/* called under bridge lock */
+static void br_flood(struct net_bridge *br, struct sk_buff *skb,
+                    struct sk_buff *skb0,
+                    void (*__packet_hook)(const struct net_bridge_port *p,
+                                          struct sk_buff *skb))
+{
        struct net_bridge_port *p;
        struct net_bridge_port *prev;
 
-       if (clone) {
-               struct sk_buff *skb2;
-
-               if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-                       br->statistics.tx_dropped++;
-                       return;
-               }
+       prev = NULL;
 
-               skb = skb2;
+       list_for_each_entry_rcu(p, &br->port_list, list) {
+               prev = maybe_deliver(prev, p, skb, __packet_hook);
+               if (IS_ERR(prev))
+                       goto out;
        }
 
+       if (!prev)
+               goto out;
+
+       if (skb0)
+               deliver_clone(prev, skb, __packet_hook);
+       else
+               __packet_hook(prev, skb);
+       return;
+
+out:
+       if (!skb0)
+               kfree_skb(skb);
+}
+
+
+/* called with rcu_read_lock */
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
+{
+       br_flood(br, skb, NULL, __br_deliver);
+}
+
+/* called under bridge lock */
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
+                     struct sk_buff *skb2)
+{
+       br_flood(br, skb, skb2, __br_forward);
+}
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+/* called with rcu_read_lock */
+static void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
+                              struct sk_buff *skb, struct sk_buff *skb0,
+                              void (*__packet_hook)(
+                                       const struct net_bridge_port *p,
+                                       struct sk_buff *skb))
+{
+       struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
+       struct net_bridge *br = netdev_priv(dev);
+       struct net_bridge_port *port;
+       struct net_bridge_port *lport, *rport;
+       struct net_bridge_port *prev;
+       struct net_bridge_port_group *p;
+       struct hlist_node *rp;
+
        prev = NULL;
 
-       list_for_each_entry_rcu(p, &br->port_list, list) {
-               if (should_deliver(p, skb)) {
-                       if (prev != NULL) {
-                               struct sk_buff *skb2;
+       rp = br->router_list.first;
+       p = mdst ? mdst->ports : NULL;
+       while (p || rp) {
+               lport = p ? p->port : NULL;
+               rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) :
+                            NULL;
 
-                               if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-                                       br->statistics.tx_dropped++;
-                                       kfree_skb(skb);
-                                       return;
-                               }
+               port = (unsigned long)lport > (unsigned long)rport ?
+                      lport : rport;
 
-                               __packet_hook(prev, skb2);
-                       }
+               prev = maybe_deliver(prev, port, skb, __packet_hook);
+               if (IS_ERR(prev))
+                       goto out;
 
-                       prev = p;
-               }
+               if ((unsigned long)lport >= (unsigned long)port)
+                       p = p->next;
+               if ((unsigned long)rport >= (unsigned long)port)
+                       rp = rp->next;
        }
 
-       if (prev != NULL) {
+       if (!prev)
+               goto out;
+
+       if (skb0)
+               deliver_clone(prev, skb, __packet_hook);
+       else
                __packet_hook(prev, skb);
-               return;
-       }
+       return;
 
-       kfree_skb(skb);
+out:
+       if (!skb0)
+               kfree_skb(skb);
 }
 
-
 /* called with rcu_read_lock */
-void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+void br_multicast_deliver(struct net_bridge_mdb_entry *mdst,
+                         struct sk_buff *skb)
 {
-       br_flood(br, skb, clone, __br_deliver);
+       br_multicast_flood(mdst, skb, NULL, __br_deliver);
 }
 
-/* called under bridge lock */
-void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+/* called with rcu_read_lock */
+void br_multicast_forward(struct net_bridge_mdb_entry *mdst,
+                         struct sk_buff *skb, struct sk_buff *skb2)
 {
-       br_flood(br, skb, clone, __br_forward);
+       br_multicast_flood(mdst, skb, skb2, __br_forward);
 }
+#endif