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;
/* 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;
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);
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;
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;
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;
struct net_bridge *br;
struct net_device *dev;
struct list_head list;
+ struct list_head promiscuous;
/* STP */
u8 priority;
{
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];
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,
}
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)
{
&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,