]> xenbits.xensource.com Git - people/ssmith/nc2-2.6.27.bak/.git/commitdiff
patch promisc-bridging
authorSteven Smith <ssmith@weybridge.uk.xensource.com>
Thu, 28 May 2009 10:54:20 +0000 (11:54 +0100)
committerSteven Smith <ssmith@weybridge.uk.xensource.com>
Thu, 28 May 2009 10:54:20 +0000 (11:54 +0100)
net/bridge/br_forward.c
net/bridge/br_if.c
net/bridge/br_private.h
net/bridge/br_sysfs_if.c

index bdd9ccea17ceb992a956453f8c9611c5ed92c244..94dd648d9638e6e4f1a961e4c583d84d9358d2cd 100644 (file)
@@ -75,9 +75,33 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
                        br_forward_finish);
 }
 
+static void __br_promisc(const struct net_bridge_port *to, struct sk_buff *skb,
+       void (*__packet_hook)(const struct net_bridge_port *p,
+                             struct sk_buff *skb))
+{
+       struct net_bridge *br = to->br;
+       struct net_bridge_port *p;
+
+       list_for_each_entry_rcu(p, &br->promiscuous_list, promiscuous) {
+               struct sk_buff *skb2;
+
+               /* Do not redeliver to sending port. */
+               if (!should_deliver(p, skb))
+                       continue;
+
+               if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+                       br->dev->stats.tx_dropped++;
+               } else {
+                       __packet_hook(p, skb2);
+               }
+       }
+}
+
 /* called with rcu_read_lock */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
+       __br_promisc(to, skb, __br_deliver);
+
        if (should_deliver(to, skb)) {
                __br_deliver(to, skb);
                return;
@@ -89,6 +113,8 @@ 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)
 {
+       __br_promisc(to, skb, __br_deliver);
+
        if (!skb_warn_if_lro(skb) && should_deliver(to, skb)) {
                __br_forward(to, skb);
                return;
index 154b1e14f6b05252fd274d19806add8a4822ef28..b5648f6e522564b3a2071b4c3eb8676283a57a45 100644 (file)
@@ -144,6 +144,7 @@ static void del_nbp(struct net_bridge_port *p)
 
        br_fdb_delete_by_port(br, p, 1);
 
+       list_del_rcu(&p->promiscuous);
        list_del_rcu(&p->list);
 
        rcu_assign_pointer(dev->br_port, NULL);
@@ -185,6 +186,7 @@ static struct net_device *new_bridge_dev(const char *name)
 
        spin_lock_init(&br->lock);
        INIT_LIST_HEAD(&br->port_list);
+       INIT_LIST_HEAD(&br->promiscuous_list);
        spin_lock_init(&br->hash_lock);
 
        br->bridge_id.prio[0] = 0x80;
@@ -258,6 +260,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
        p->port_no = index;
        br_init_port(p);
        p->state = BR_STATE_DISABLED;
+       INIT_LIST_HEAD(&p->promiscuous);
        br_stp_port_timer_init(p);
 
        return p;
@@ -547,6 +550,73 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
        return 0;
 }
 
+int br_port_is_promiscuous(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct net_bridge_port *p2;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(p2, &br->promiscuous_list, promiscuous) {
+               if (p2 == p)            {
+                       rcu_read_unlock();
+                       return 1;
+               }
+       }
+       rcu_read_unlock();
+
+       return 0;
+}
+
+int br_port_enable_promiscuous_mode(struct net_bridge *br, struct net_device *dev)
+{
+       struct net_bridge_port *p = dev->br_port;
+
+       if (!p || p->br != br) {
+               printk(KERN_INFO "interface %s is not on bridge %s\n", dev->name, br->dev->name);
+               return -EINVAL;
+       }
+
+       if (br_port_is_promiscuous(br, p)) {
+               printk(KERN_INFO "interface %s is already promiscuous on bridge %s\n",
+                      dev->name, br->dev->name);
+               return -EINVAL;
+       }
+
+       printk(KERN_INFO "enable promiscuous mode on interface %s (bridge %s)\n",
+              dev->name, br->dev->name);
+
+       list_add_rcu(&p->promiscuous, &br->promiscuous_list);
+
+       return 0;
+}
+
+int br_port_disable_promiscuous_mode(struct net_bridge *br, struct net_device *dev)
+{
+       struct net_bridge_port *p = dev->br_port;
+
+       if (!p || p->br != br) {
+               printk(KERN_INFO "interface %s is not on bridge %s\n", dev->name, br->dev->name);
+               return -EINVAL;
+       }
+
+       if (!p || p->br != br) {
+               printk(KERN_INFO "interface %s is not on bridge %s\n", dev->name, br->dev->name);
+               return -EINVAL;
+       }
+
+       if (!br_port_is_promiscuous(br, p)) {
+               printk(KERN_INFO "interface %s is not promiscuous on bridge %s\n",
+                      dev->name, br->dev->name);
+               return -EINVAL;
+       }
+
+       printk(KERN_INFO "disable promiscuous mode on interface %s (bridge %s)\n",
+              dev->name, br->dev->name);
+
+       list_del_rcu(&p->promiscuous);
+
+       return 0;
+}
+
 void __exit br_cleanup_bridges(void)
 {
        struct net_device *dev;
index c3dc18ddc0431b87fa02135b99ddb0f7bbf08d4c..310edbc8e85154744f000eaa8954898b50153bdb 100644 (file)
@@ -63,6 +63,7 @@ struct net_bridge_port
        struct net_bridge               *br;
        struct net_device               *dev;
        struct list_head                list;
+       struct list_head                promiscuous;
 
        /* STP */
        u8                              priority;
@@ -88,6 +89,7 @@ struct net_bridge
 {
        spinlock_t                      lock;
        struct list_head                port_list;
+       struct list_head                promiscuous_list;
        struct net_device               *dev;
        spinlock_t                      hash_lock;
        struct hlist_head               hash[BR_HASH_SIZE];
@@ -188,6 +190,12 @@ extern int br_del_if(struct net_bridge *br,
 extern int br_min_mtu(const struct net_bridge *br);
 extern void br_features_recompute(struct net_bridge *br);
 
+extern int br_port_enable_promiscuous_mode(struct net_bridge *br,
+                                          struct net_device *dev);
+extern int br_port_disable_promiscuous_mode(struct net_bridge *br,
+                                           struct net_device *dev);
+extern int br_port_is_promiscuous(struct net_bridge *br,
+                          struct net_bridge_port *p);
 /* br_input.c */
 extern int br_handle_frame_finish(struct sk_buff *skb);
 extern struct sk_buff *br_handle_frame(struct net_bridge_port *p,
index 02b2d50cce4da640f4b62a6652253d217e890af2..46241ea6794295340c4a64828337164bc764598a 100644 (file)
@@ -122,6 +122,21 @@ static ssize_t show_message_age_timer(struct net_bridge_port *p,
 }
 static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL);
 
+static ssize_t store_port_promisc(struct net_bridge_port *p, unsigned long v)
+{
+       if (v)
+               br_port_enable_promiscuous_mode(p->br, p->dev);
+       else
+               br_port_disable_promiscuous_mode(p->br, p->dev);
+
+       return 0;
+}
+static ssize_t show_port_promisc(struct net_bridge_port *p, char *buf)
+{
+       return sprintf(buf, "%d\n", br_port_is_promiscuous(p->br, p) ? 1 : 0);
+}
+static BRPORT_ATTR(promisc, S_IRUGO | S_IWUSR, show_port_promisc, store_port_promisc);
+
 static ssize_t show_forward_delay_timer(struct net_bridge_port *p,
                                            char *buf)
 {
@@ -153,6 +168,7 @@ static struct brport_attribute *brport_attrs[] = {
        &brport_attr_designated_port,
        &brport_attr_designated_cost,
        &brport_attr_state,
+       &brport_attr_promisc,
        &brport_attr_change_ack,
        &brport_attr_config_pending,
        &brport_attr_message_age_timer,